Ayende @ Rahien

Refunds available at head office

Awesome RavenDB Feature of the day: Eval 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.

Tags:

Posted By: Ayende Rahien

Published at

Originally posted at

Comments

Apostol
07/26/2012 09:53 AM by
Apostol

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?

Ayende Rahien
07/26/2012 10:43 AM by
Ayende Rahien

Apostol, Yes, that is the idea. And no, there is no way to currently do that.

tobi
07/26/2012 10:59 AM by
tobi

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.

Khalid Abuhakmeh
07/26/2012 12:47 PM by
Khalid Abuhakmeh

What does the document look like when you do an AdvancedPatchRequest, and are AdvancedPatchRequests replicated and part of backup?

Ayende Rahien
07/26/2012 12:53 PM by
Ayende Rahien

Tobi, If you want it done on the client, that is easy, we have listener support for that.

Ayende Rahien
07/26/2012 12:54 PM by
Ayende Rahien

Khalid, APR are commands to the database that modify the state. Their results are written to the db and then treated normally.

Paul Stovell
07/26/2012 01:17 PM by
Paul Stovell

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.

Eric J. Smith
07/26/2012 02:59 PM by
Eric J. Smith

Why not use Mono's C# compiler services so that you could write these actions in C# instead of JavaScript?

Matt Warren
07/26/2012 04:08 PM by
Matt Warren

@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.

Matt Warren
07/26/2012 04:10 PM by
Matt Warren

@Paul,

Sorry that should've been AllowStale = false and it's actually the default, so it's already handled for you!!

Ayende Rahien
07/26/2012 04:27 PM by
Ayende Rahien

Paul, An exception is thrown, no update takes place.

Ayende Rahien
07/26/2012 04:28 PM by
Ayende Rahien

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.

Eric J. Smith
07/26/2012 04:44 PM by
Eric J. Smith

I can understand that. It just sucks that the IronJS project seems to have died out.

Rafal
07/26/2012 09:00 PM by
Rafal

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).

aaron
07/27/2012 01:03 AM by
aaron

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.

Daniel Lang
07/29/2012 01:28 PM by
Daniel Lang

We can't have any transactional guarantees, right? So is there a way to detect a failure during the updates?

Ayende Rahien
07/29/2012 01:31 PM by
Ayende Rahien

Daniel, This is transactional, like all other things in RavenDB.

njy
07/30/2012 09:26 AM by
njy

@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?

Ayende Rahien
07/30/2012 05:01 PM by
Ayende Rahien

Njy, We already have a feature like that, it is called Indexed Properties http://issues.hibernatingrhinos.com/issue/RavenDB-245

borcam
08/24/2012 01:51 PM by
borcam

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.

Comments have been closed on this topic.