I have to calculate a "total" for each user based on individual actions -- that is, a User hasMany Actions and each Action has a point. So I need to basically get a sum of all the points on all the actions.
Actions are added and subtracted regularly. Since this can be a rather heavy operation, it's not feasible that I execute the "total" each time I need it. Therefore, I am thinking of running the operation once a day for each user and storing the total points on the User as an int.
Because I have thousands of users, I am trying to figure out the best way to do this. Should I basically iterate through all the users and call User.save() each time, or is there some kind of batch update mechanism I can use in Grails / GORM?
Is the model in your question your actual model, or a simplified version of it? If all you're doing is User hasMany Actions and each Action has a point value (an integer?) that you'd like to sum up, that's really the kind of thing that relational databases excel at. If you have the proper indexes on your database, I'd think this would be a very quick call, as long as you're doing a groupBy query (or using grails projection criteria).
Here's an example method that will take a list of users and will return an array that pairs user.id to the number of points that user currently has:
def calculateCurrentPoints(users) {
Action.executeQuery('select a.user.id, sum(points) from Action a where a.user.id in (:userIds) group by a.user.id', [userIds: users.id])
}
If desired, you could easily turn this into a map of user id to points for easier look-up:
def calculateCurrentPoints(users) {
def result = Action.executeQuery('select a.user.id, sum(points) from Action a where a.user.id in (:userIds) group by a.user.id', [userIds: users.id])
result.inject([:]) { map, userWithPoints ->
map[userWithPoints[0]] = userWithPoints[1]
return map
}
}
If this really is slower than I'm thinking it is, you could use the executeUpdate with HQL similiar to what I have above to update a "totalPoints" field on each user (or keep it updated as part of a service whenever you add a new Action to that user).
Calling User.save() will not actually write the change to the database, it is only when the Hibernate session is flushed that the change is written (docs)
You can flush the session manually be accessing the SessionFactory and calling flush on the resulting session, as shown in this FAQ.
I imagine you would want to load the users using a batching technique to ensure you don't have all of the thousands of users in memory at the same time.
As an aside if you wanted to cache the value but have it automatically updated whenever you add an action you could hook into active record's events to update the calculated value, here is an example from Ruby
Related
In an MVC application, for a given action that all users are authorized to perform, I would like to filter results based on the user's group membership. For instance ...
Users in GroupA should only see records pertaining to BuildingX.
Users in GroupB should only see records pertaining to BuildingY.
Users in GroupC should see all records.
I have no problem using authorization filters to restrict access to Actions, but I'm having a much harder time finding how to restrict access to data short of explicitly modifying statements every place where data is fetched.
Assuming your records are in a database, the roles membership model doesn't extend to the database out of the box. You can build a roles-based access control for your database, but you will likely save time using a simpler approach. For example, using code like this in your controller:
if (Roles.IsUserInRole("GroupA")) {
// Get data for GroupA.
}
// Display data...
A year later, working on a different but related issue, I found the EntityFramework.DynamicFilters package which does exactly what I need!
I have the table Users and the table Posts.
---Users---
id
name
---Posts---
id
text
User
How do I get the list of users, including the posts? I know how to get the list of posts with the users, because the pointer of the user is in that table. But the users does not have any pointers to posts.
What you want will be a lot of requests. You can easily query for (and paginate) the users. Then, for each user, you need to make another (paginated) request to find Posts with a pointer to that user. For that use whereKey:equalTo: with the bame of the user column on the Post and the current user instance.
Do you have a list of users already, and need to get all of their posts? If so, use the whereKey:containedIn: method. You can also create a query on users that pulls all the users you want to pull the posts for, and then rather than running the query, you create a second query on your posts objects and use whereKey:matchesQuery: which means that the object referenced in the Key is an object that would be returned by the query you pass in. So it'll return all the posts relevant to the users that would be returned from the initial query.
When you run this query, you may want to look into the query.each() method as an alternative to query.find(). query.find() is limited to 1000 results, query.each goes through every single result. This could be a problem when you scale, though, as cloud methods have a 15 second timer, and before/after save triggers have a 3 second timer.
I have a model in my Rails app for a SalesOpportunity, and running a SWOT analysis (Strength, Weakness, Opportunity, Threat) to decide how good the SalesOpportunity is. Swots belong_to SalesOpportunities and therefore in the SalesOpportunity Model I have a method called update_swot_score which iterates through the Swot objects and calculates a score based on parameters I'm feeding in. All of this works fine.
What I'm wondering is whether I need to add a field to my SalesOpportunity model (let's call it swot_score for simplicity) and to update the instance variable at the end of the update_swot_score method using #swot_score = "results of my calculation", or whether I can directly access a result of the update_swot_score method (ideally in my view - I'll display different partials depending on the result).
What is the Rails way of doing this? Is there a performance efficiency to be gained by using either method?
i will suggest to add in the db as a dedicated column to store score...there are few good reasons as why you should do it:-
dedicated column to store only score
easily maintainable even you decide to add/edit new score type in future
Can be called/updated/modified anywhere throughout your application
can use callbacks in future as well(on_create,on_update) as its in a Model
Performance wise,its awesome as you can cache it..can also counter_cache and expire too.
Hope it helps
I'm wondering what the most efficient way of updating a single value in a domain class from a row in the database. Lets say the domain class has 20+ fields
def itemId = 1
def item = Item.get(itemId)
itemId.itemUsage += 1
Item.executeUpdate("update Item set itemUsage=(:itemUsage) where id =(:itemId)", [usageCount: itemId.itemUsage, itemId: itemId.id])
vs
def item = Item.get(itemId)
itemId.itemUsage += 1
itemId.save(flush:true)
executeUpdate is more efficient if the size and number of the un-updated fields is large (and this is subjective). It's how I often delete instances too, running 'delete from Foo where id=123' since it seems wasteful to me to load the instance fully just to call delete() on it.
If you have large strings in your domain class and use the get() and save() approach then you serialize all of that data from the database to the web server twice unnecessarily when all you need to change is one field.
The effect on the 2nd-level cache needs to be considered if you're using it (and if you edit instances a lot you probably shouldn't). With executeUpdate it will flush all instances previously loaded with get() but if you update with get + save if flushes just that one instance. This gets worse if you're clustered since after executeUpdate you'd clear all of the various cluster node caches vs flushing the one instance on all nodes.
Your best bet is to benchmark both approaches. If you're not overloading the database then you may be prematurely optimizing and using the standard approach might be best to keep things simple while you solve other problems.
If you use get/save, you'll get the maximum advantage of the hibernate cache. executeUpdate might force more selects and updates.
The way executeUpdate interacts with the hibernate cache makes a difference here. The hibernate cache gets invalidated on executeUpdate. The next access of that Item after the executeUpdate would have to go to the database (and possibly more, I think hibernate might invalidate all Items in the cache).
Your best bet is to turn on debug logging for 'org.hibernate' in your Config.groovy and examine the SQL calls.
I think they are equal. The both issue 2 sql calls.
More efficient would be just a single update
Item.executeUpdate("update Item set itemUsage=itemUsage+1 where id =(:itemId)", [ itemId: itemId.id])
You can use the dynamicUpdate mapping attribute in your Item class:
http://grails.org/doc/latest/ref/Database%20Mapping/dynamicUpdate.html
With this option enabled, your second way to update a single field using Gorm will be as efficient as the first one.
I have a Rails model with various attributes and has_many relationships to other models. In the view I want the user to be able to enter/change values which will change the values in the loaded object and return results calculated from them whenever they make a change. However, I don't want the values to change in the database until they select 'Save'. The problem I'm running into is that the variables I associate with the collections pull from the database after each new change. So if I use something like the following:
#model.attributes = params[:model]
it lasts the length of that particular call to the model, but the next time the user makes a change the collection is reloaded, losing all the previous changes aside from the most recent one. I've tried using an instance variable, e.g.:
#costs ||= self.costs
But whenever I run an operation on it, it reloads from the database, overwriting previous changes. E.g.
#costs[i].monthly = 10
Is there any way to ensure that changes persist across the session without saving to the database? Right now it's looking like my best option may be to create a separate model to store the values in and then save them back to the original when the user is finished.
You could either resave it to the params hash whenever you make a change, or try using the session hash (but you'll have to remember to clear it).
http://guides.rubyonrails.org/security.html#what-are-sessions
In short, data does not persist between requests. At the time I was under the impression that attributes held values for the length of the session. I've since learned that the only things that persist are the values stored in the database and those passed back and forth in the params and session hashes.