RavenDB MultiMap Index

157 views
Skip to first unread message

MyP3uK

unread,
Apr 19, 2012, 11:51:03 AM4/19/12
to ravendb
I am new to RavenDB. I am trying to use a multi map index feature,
though I am not sure if it is the best approach to my problem. So I
have three documents: Unit, Car, People.

Car document looks like this:

{
Id: "cars/123",
PersonId: "people/1235",
UnitId: "units/4321",
Make: "Toyota",
Model: "Prius"
}

People document looks like this:

{
Id: "people/1235",
FirstName: "test",
LastName: "test"
}

And unit doc:

{
Id: "units/4321",
Address: "blah blah"
}

This is an abbreviated example, in my real app there are way more
fields, so data de-normalization would be my last resort.

I need to create and index that will have all of this three docuemnts
joined in one document. Something like this:

{
CarId: "cars/123",
PersonId: "people/1235",
UnitId: "units/4321",
Make: "Toyota",
Model: "Prius"
FirstName: "test",
LastName: "test"
Address: "blah blah"
}

// same unit different person owns a different car

{
CarId: "cars/122",
PersonId: "people/1236",
UnitId: "units/4321",
Make: "Toyota",
Model: "4runner"
FirstName: "test",
LastName: "test"
Address: "blah blah"
}

In a relational database I would just use two joins to People and
Unit tables by ids
and my car table would be an aggregate entity.

Here is the index definition that I have:

public class MyMultiIndex :
AbstractMultiMapIndexCreationTask<JoinedDocument>
{
public MyMultiIndex()
{
// creating maps
AddMap<Car>(cars => cars.Select(e => new { e.CarId, e.Make,
e.Model, PersonId = e.PersonId, UnitId = e.UnitId, FirstName =
(null)string, LastName = (null)string, Address = (nul)string }));
AddMap<People>(people => people.Select(e => new { CarId =
(string)null, Make = (string)null, Model = (string)null, PersonId =
e.Id, UnitId = (null)string, FirstName = e.FirstName, LastName =
e.LastName, Address = (nul)string }));
AddMap<Unit>(people => people.Select(e => new { CarId =
(string)null, Make = (string)null, Model = (string)null, PersonId =
(null)string, UnitId = e.null, FirstName = (nul)string , LastName =
(nul)string , Address = e.Address }));

Reduce = results => from result in results
group result by result.CarId
into g
select new JoinedDocument
{
CarId = g.Key,
PersonId = g.First(e => e.CarId == g.Key).PersonId,
UnitId = g.First(e => e.CarId == g.Key).UnitId,
Model = g.First(e => e.CarId == g.Key).Model,
Make = g.First(e => e.CarId == g.Key).Make,

**// this never works. It is like result set does not contain
anything with this personId. It looks like AddMap for people document
did not work.**

FirstName = results.First(e => e.PersonId == g.First(ie =>
ie.CarId == g.Key).PersonId).FirstName,

**// this never works. It is like result set does not contain
anything with this personId. It looks like AddMap for people document
did not work.**

LastName = results.First(e => e.PersonId == g.First(ie =>
ie.CarId == g.Key).PersonId).LastName,

**// this never works. It is like result set does not contain
anything with this personId. It looks like AddMap for unit document
did not work.**

UnitAddress = results.First(e => e.UnitId == g.First(ie =>
ie.CarId == g.Key).UnitId).LastName,
};
Index(map => map.Model, FieldIndexing.Analyzed);
Index(map => map.Make, FieldIndexing.Analyzed);
Index(map => map.LastName, FieldIndexing.Analyzed);
Index(map => map.FirstName, FieldIndexing.Analyzed);
Index(map => map.Make, FieldIndexing.Analyzed);
Index(map => map.UnitAddress, FieldIndexing.Analyzed);
}
}

When RavenDb runs this index I see errors when it is trying to run the
Reduce function I have provided. It throws error when I am trying to
match a record where person's first name and last name exist, same
happens with the unit.



Oren Eini (Ayende Rahien)

unread,
Apr 19, 2012, 12:09:51 PM4/19/12
to rav...@googlegroups.com
Didn't you ask this exact same question in stack overflow?

Galim Kaudinov

unread,
Apr 19, 2012, 12:12:51 PM4/19/12
to rav...@googlegroups.com
Yes I did, and I think I did not get a good answer.
--
Best Regards.
Galim Kaudinov.

gal...@gmail.com
galim.k...@gmail.com
kaud...@bk.ru

Oren Eini (Ayende Rahien)

unread,
Apr 19, 2012, 12:17:37 PM4/19/12
to rav...@googlegroups.com
Let go back, before you decided on a solution.
What is it that you need?

MyP3uK

unread,
Apr 19, 2012, 12:44:43 PM4/19/12
to ravendb
So I have three separate documents Car, Unit, Person. Car document has
Id references to Unit doc and Person doc. I want to query for a car
base on properties in Unit or Person doc.
So what I really need is an equivalent to a join in a relational
database. I thought that I can achieve this using multi map index, but
it does not seem to work for me.
I would like to avoid data de-nromalization if it is possible and not
complicated.

On Apr 19, 10:17 am, "Oren Eini (Ayende Rahien)" <aye...@ayende.com>
> > galim.kaudi...@gmail.com
> > kaudi...@bk.ru

Oren Eini (Ayende Rahien)

unread,
Apr 19, 2012, 12:45:46 PM4/19/12
to rav...@googlegroups.com

MyP3uK

unread,
Apr 19, 2012, 1:05:06 PM4/19/12
to ravendb
I read the article. I am not sure how to apply it in my case. I need
to search by three documents Car, Person, Vehicle, not sure how I
would achieve this using AbstractGenericIndexCreationTask. So for
example if a user enters "John Doe" in the search field I need to
return all the vehicles that John Does has, again vehicle document
does not have the person name all it has is the link (Id) to a person
document. Also in our app user has the ability to specify exactly
which field she wants to search in. So if user chooses to search by
person name and enters "John Blue" the query should return all of the
vehicles that John Blue has but no blue vehicles.

On Apr 19, 10:45 am, "Oren Eini (Ayende Rahien)" <aye...@ayende.com>

Oren Eini (Ayende Rahien)

unread,
Apr 19, 2012, 1:11:30 PM4/19/12
to rav...@googlegroups.com
Do you actually need to search on all three ? isn't it more common to search for one of them, then find matching cars?

MyP3uK

unread,
Apr 19, 2012, 1:27:37 PM4/19/12
to ravendb
Well the result of my search should always be a list of Cars..

So as I wrote in my post, car document has the following props

{
CarId: "cars/123",
UnitId: "units/456",
PersonId "people/321"
// Another car properties skipped...
}

What I really need is to query Cars. And In the query I need to be
able to specify First Name or Last Name of a person (There might be
more properties as well, this is just an example). In a relational
database I would just join to a Person table based on the Person Id in
the Car object. But I can't really figure out how to do it in RavenDB
without de-normolizing the data. I thought that multi map index would
be a key here.

On Apr 19, 11:11 am, "Oren Eini (Ayende Rahien)" <aye...@ayende.com>

Oren Eini (Ayende Rahien)

unread,
Apr 19, 2012, 1:35:00 PM4/19/12
to rav...@googlegroups.com

MyP3uK

unread,
Apr 19, 2012, 1:38:16 PM4/19/12
to ravendb
Thanks! I am looking at it right now.

On Apr 19, 11:35 am, "Oren Eini (Ayende Rahien)" <aye...@ayende.com>
wrote:
> Okay, I have written up a post on how to handle your scenario:http://ayende.com/blog/156225/relational-searching-sucks-donrsquo-t-t...

Scott D

unread,
Apr 19, 2012, 1:40:36 PM4/19/12
to ravendb
The end result that this search needs is to do a "full text search"
across the person's First or Last name, or the Make, Model, and
License Plate of their vehicle. The name comes from a Person document
and the vehicle information is in the Vehicle document. The goal is to
do a map of both the person and vehicle documents and then perform a
Reduce so that the index can be searched by First/Last name and the
vehicle properties. We have a common ID in both the Person and
Vehicle documents that we include in the maps as well. In the Reduce,
we group by this identifier with the goal of producing the "union" of
the Person and Vehicle properties in the index.

It seems that the issue with the reduce is that a when we query that
index by the common ID, we get back 2 results. One of them contains
only the properties of the Person document and the other contains only
the properties of the Vehicle document. The expectation is that this
would produce a single result, with the properties of the Person and
Vehicle represented in that single returned result. Is that the
correct expectation?

MyP3uK

unread,
Apr 19, 2012, 1:43:10 PM4/19/12
to ravendb
So from what I understand from the article. The easiest and most
efficient solution would be to de-normalize data and put properties on
the Car object?

Thanks

On Apr 19, 11:35 am, "Oren Eini (Ayende Rahien)" <aye...@ayende.com>
wrote:
> Okay, I have written up a post on how to handle your scenario:http://ayende.com/blog/156225/relational-searching-sucks-donrsquo-t-t...

Oren Eini (Ayende Rahien)

unread,
Apr 19, 2012, 2:21:35 PM4/19/12
to rav...@googlegroups.com
Huh?
Where did you get that? Didn't you read the post?

MyP3uK

unread,
Apr 19, 2012, 2:37:34 PM4/19/12
to ravendb
Probably I am missing something. But looking at the post I dont
undertand how do you search for a car by person's lastname only?
On Apr 19, 12:21 pm, "Oren Eini (Ayende Rahien)" <aye...@ayende.com>
> ...
>
> read more »

MyP3uK

unread,
Apr 19, 2012, 3:39:53 PM4/19/12
to ravendb
So from I understand in the scenario where I need to get vehicle by
person's First Name. I need to query first person doc index to get the
Id for a person and than query for a vehicle by person id. Right?

On Apr 19, 12:21 pm, "Oren Eini (Ayende Rahien)" <aye...@ayende.com>
> ...
>
> read more »

Itamar Syn-Hershko

unread,
Apr 19, 2012, 4:51:18 PM4/19/12
to rav...@googlegroups.com
No, you define another multimap/reduce index, first map all cars with CustomerName=null, and then map all cutomers with their corresponding CustomerName field set, and then join them altogether in the Reduce function.

Note this requirement is different from the simple search requirement you had before

MyP3uK

unread,
Apr 19, 2012, 5:00:22 PM4/19/12
to ravendb
So that is exactly what I am trying to accomplish. Can you take a look
at the Reduce function I wrote? It does not seem to work right? any
ideas from the top of your head?

On Apr 19, 2:51 pm, Itamar Syn-Hershko <ita...@hibernatingrhinos.com>
wrote:
> ...
>
> read more »

Itamar Syn-Hershko

unread,
Apr 19, 2012, 5:35:13 PM4/19/12
to rav...@googlegroups.com
The problem is you can't do that with this model. What is the business logic behind this modeling?

MyP3uK

unread,
Apr 19, 2012, 5:43:14 PM4/19/12
to ravendb
I want to allow user to be able to

1) search a car by any field in a Unit, Person, Vehicle,
2) search by all of the fields together.

2 is easy to accomplish, 1 I don't know how.

So lets say you have a grid like this

|--Model--|--Make--|--Color--|--FirstName--|--LastName--|--
UnitAddress--|
4runner Toyota Red John Doe 6404
E Redmond st.

A user is allowed to search by all of those fields together and by
also by specifying in which field to search.

An my car document is stored like this

{
PersonId: "people/123" // this points to John Doe in the Person
doc collection
UnitId: "units/1123" // this points to unit with address 6404 E
Redmond st.
Model: "4runner",
Make: "Toyota",
Color: "Red"
}

Thanks.


On Apr 19, 3:35 pm, Itamar Syn-Hershko <ita...@hibernatingrhinos.com>
> ...
>
> read more »

Itamar Syn-Hershko

unread,
Apr 19, 2012, 5:56:29 PM4/19/12
to rav...@googlegroups.com
Ok, so first let's agree we are discussing two different indexes here. Number 2 is settled, now let's tackle #1.

Can you explain this modeling?

MyP3uK

unread,
Apr 19, 2012, 6:06:02 PM4/19/12
to ravendb

So #2. I have this car document in raven.

{
PersonId: "people/123" // this points to John Doe in the Person
doc collection
UnitId: "units/1123" // this points to unit with address 6404 E
Redmond st.
Model: "4runner",
Make: "Toyota",
Color: "Red"
}

I want to be able to query this Car from raven by properties in the
person. Lets say this car belongs to John Doe.
A user is able to say - I want to search by a person name where first
name equal to John.

I do understand that I can issue two queries first to the person doc
collection and second to the car doc collection by Person id. But I
think this is not very efficient and if this is the only option I'd
rather de-normolize the data. So do you think it is possible to create
a multi index that will basically give this kind of materialized view:

|--Model--|--Make--|--Color--|--FirstName--|--LastName--|
4runner Toyota Red John Doe

Thanks.





On Apr 19, 3:56 pm, Itamar Syn-Hershko <ita...@hibernatingrhinos.com>
> ...
>
> read more »

Itamar Syn-Hershko

unread,
Apr 19, 2012, 6:38:10 PM4/19/12
to rav...@googlegroups.com
Based on Person and Car details, yes, but not on details from 3 different entities Person Car and Unit.

MyP3uK

unread,
Apr 19, 2012, 7:08:08 PM4/19/12
to ravendb
Got it. So would advice de-normalizing data into the Vehicle doc? I
don't want to issue multiple queries

On Apr 19, 4:38 pm, Itamar Syn-Hershko <ita...@hibernatingrhinos.com>
> ...
>
> read more »

Colin Place

unread,
Apr 27, 2012, 4:19:48 PM4/27/12
to rav...@googlegroups.com
Before denormalizing data into the Car document, could you explain why you need a separate Unit document?  From the details you've given it would seem more natural to have a collection of Units within the Person document.  If that is feasible, does that simplify the index?
Reply all
Reply to author
Forward
0 new messages