Porting MVC Music Store to RavenPorting the HomeController, the map/reduce way
The current HomeController looks like this:
I really don’t like the fact that the controller issues queries like that, but we will let it go for now.
This query (thanks to EF Prof) looks like this:
And here we run into a very interesting problem, we can’t really replicate this query. The reason is that this query runs over multiple tables which our model says would be in different documents.
There are several ways in which we can fix this. One way of doing this would be to define a map / reduce index on top of orders.
Note: Yes, I am familiar with this comic.
The way that I am about to show you isn’t the way I would recommend going for real, but I want to show it anyway. I’ll discuss the idiomatic Raven way of handling this feature in my next post.
Map/reduce in Raven is just a couple of Linq queries, so it is nothing to be worried about. As a reminder, we have the following order documents in our database:
We define the index “SoldAlbums” using the following queries.
// map from order in docs.Orders from line in order.Lines select new{ line.Album, line.Quantity } // reduce from result in results group result by result.Album into g select new{ Album = g.Key, Quantity = g.Sum(x=>x.Quantity) }
As you can see, those are two very simple Linq queries.
The result of which would be:
Once we have that, it is trivial to derive the answer to GetTopSellingAlbums. Indeed, the following function implements the exact same logic and has the same output as the previous implementation:
The way it work is pretty simple, we get the most sold albums (by sorting on descending quantity), then load them from the database. Because we might have less than count top selling albums, we need to top it off from regular albums.
This mean that this code execute 2 – 3 queries. I don’t really like it, but on my machine, it takes about less than 10 ms to do all three requests, which is livable.
The reason that I am posting this solution is that I want to show this as an approach to a problem, not as the recommended approach for how to solve it, I’ll do that in my next post.
More posts in "Porting MVC Music Store to Raven" series:
- (31 May 2010) StoreManagerController, part 2
- (29 May 2010) StoreManagerController
- (28 May 2010) Porting the checkout process
- (25 May 2010) StoreController
- (24 May 2010) Advanced Migrations
- (23 May 2010) Migrations
- (22 May 2010) Porting the HomeController, the Right Way
- (21 May 2010) Porting the HomeController, the map/reduce way
- (20 May 2010) Data migration
- (19 May 2010) Setting up the application
- (18 May 2010) The data model
Comments
Interesting. Looking forward to seeing your next post on the 'recommended approach' to see if we come up with similar solutions.
Is there any reason why you're choosing to go with 'OrderBy("-Quantity")' rather than the more natural 'OrderByDescending("Quantity")' ?
Demis,
This is using the low level API, the high level API would just use Linq.
Very Nice, supporting multiple levels of API! And I thought I was the only dev crazy enough to spend the time to do this :)
Demis,
You pretty much have to, because there is usually more power (but more work) at lower levels.
And yes higher levels offer better productivity
I hope the idiomatic way is more immediately comprehensible and succinct. It sure does seem like a bunch more code for the same functionality at first glance.
It is fantastic how you have managed to create a buzz around document DBs and especially RavenDB.
I am currently wondering how many good projects may be out there that go unattended and without the focus they may deserve because there isn't a 'famous' name attached to it and simply go under in the webs due to the sheer mass of signals.
The situation has grown even worse with less people blogging and more people tweeting, where the signal-to-noise ratio is, at least from my perspective, lower.
Frank,
Take a look at how Rhino Mocks became famous.
Ayende,
that was somewhat ahead of my time...how can I take a look? Your first posts are in 2005 and judging by the comments left you didn't have that many readers then than now...
I am genuinely interested to hear that story :)
Frank,
Putting it out, making it work, listening to people, writing blog posts (I had about 80 subscribers then), writing articles in places like CodeProject, participating in mailing lists about TDD.
That sort of thing.
IOW, a lot of really hard work
That's the thing isn't it? It isn't sufficient to be a Genius that found the end to all...it takes a lot of sweat for others to notice. It's probably a good thing, as it's honest stuff.
I really don’t like the fact that the controller issues queries like that, but we will let it go for now.
What don't you like about it?
...we can’t really replicate this query. The reason is that this query runs over multiple tables which our model says would be in different documents.
Replicate as in rewrite for RavenDB? Sorry, I am lost.
Ron the MVC Rookie
Ron,
The controller should orchestrate stuff, not make actual queries.
And replicate == re-write - in this instance
Ron,
This is not my app. I just ported it, I didn't re-architect it.
Moreover, stuff that work for very small app isn't appropriate for bigger apps
Thank you Ayende. Learning a lot of best practices form you!
Ron
Örjan,
With the MVC Music Store, I only ported the code, I did NOT change the design in any significant way.
I would generally create Query objects for anything that was more complicated than a Load by id, yes.
Comment preview