Awesome RavenDB Feature of the dayEval Patching, Part II–Denormalized References
I mentioned yesterday that I am keeping the best for today. What I am going to show you is how you can use Eval Patching for keeping track of denormalized references.
In this case, we have Users & Posts. Each Post contains a UserName property as well as the user id. When the user changes his name, we need to update all of the relevant posts.
Here is how you can do this:
store.DatabaseCommands.UpdateByIndex("Posts/ByUser", new IndexQuery{Query = "UserId:" + userId}, new AdvancedPatchRequest { Script = @" var user = LoadDocument(this.UserId); this.UserName = user.Name; " });
And this is a really simple scenario, the options that this opens, the ability to load a separate document and modify the current document based on its value is really powerful.
More posts in "Awesome RavenDB Feature of the day" series:
- (26 Jul 2012) Eval Patching, Part II–Denormalized References
- (24 Jul 2012) Evil Patching
Comments
Wow, so this means that if I have to make this kind of changes - reflect a change from one entity to another - I'm going to make a database command with this kind of script and send it to the server, and the server is going to make the update to all the entities without me needing to load them on client and send them back?
If that's right it's awesome - it's going to make RavenDb much more efficient.
Another question - is there a way to specifying that we want to 'wait for non-stale results' before executing the Script/DatabaseCommand?
Apostol, Yes, that is the idea. And no, there is no way to currently do that.
It would be a nice idea to have a trigger-like system for such cases. "No matter who changes the name, I want all denormalized data to be updates as well using the following command".
That could be configured at the client, too, if I'm not mistaken. The database does not need to be the driver for triggers.
What does the document look like when you do an AdvancedPatchRequest, and are AdvancedPatchRequests replicated and part of backup?
Tobi, If you want it done on the client, that is easy, we have listener support for that.
Khalid, APR are commands to the database that modify the state. Their results are written to the db and then treated normally.
What happens if the index is stale at the time of the update?
E.g., users/wilma just saved a new post, then changed her name, and the Posts/ByUser index hasn't been updated yet.
Why not use Mono's C# compiler services so that you could write these actions in C# instead of JavaScript?
@Paul, pass in AllowStale = true as the 4th argument to UpdateByIndex(..) and it'll handle this. Basically the patching won't go ahead if the index is stale at the time.
@Paul,
Sorry that should've been AllowStale = false and it's actually the default, so it's already handled for you!!
Paul, An exception is thrown, no update takes place.
Eric, Put simply, because it is much easier to limit what you are doing in IronJS than in C#. We already do dynamic compilation, and it is much easier to go this route than that.
I can understand that. It just sucks that the IronJS project seems to have died out.
This function looks powerful but ugly and hackishly at the some time - like something unfinished, part of much bigger functionality (a full-blown server side data processing language).
Perhaps it would be nice to have a small util included which allows you to execute a .cs script from the command line against a raven instance, something like DynamicScriptExecutor.exe Jul27Patch.cs --instance:http://localhost:8090/. which feeds back a success response or any exception info.
We can't have any transactional guarantees, right? So is there a way to detect a failure during the updates?
Daniel, This is transactional, like all other things in RavenDB.
@Oren: a suggestion, maybe a stupid one: it would be possible to specify a field as a "computed one"? It would be cool to define a field as computed, specifying the computing algorithm (in JS, of course) and when RavenDB parses that, it will understand which other fields are involved, and keep track of the things automatically.
Think about it: it would be conceptually just like indexes. You don't update indexes values by hand, but instead you specify a map/reduce "logic" and the index values will be updated for you by RavenDB.
The same concept should be used for computed values (typically denormalized values): the dev defines only one time the logic, and RavenDB would do the rest, in the background, when it feels like it should do it. And just like indexes can be stale, computed values could be, too.
There could be 2 ways of doing it, i think, and they would have different consequences:
1) per-field-compute-fiunction: the dev could define the body of a JS function, the return value of which would set the specified field value, like for example "return this.Comments.length" to define the logic for the "CommensCount" field. This function should be run only when one of the fields in the specified formula is updated;
2) per-entity: (like a server-side js entity-update trigger)it could do more stuff, like "this.CommentsCount = this.Comments.length; this.Foo = "Bar" + this.Baz;" or something like that;
What do you think?
Njy, We already have a feature like that, it is called Indexed Properties http://issues.hibernatingrhinos.com/issue/RavenDB-245
We simplify things a little for this article: The stack keeps track of what is executing in our application. As we enter and exit methods items are added to or removed from the top.
Comment preview