Complex nested structures in RavenDB
This started out as a question in the mailing list. Consider the following (highly simplified) model:
public class Building { public string Name { get; set; } public List<Floor> Floors { get; set; } } public class Floor { public int Number { get; set; } public List<Apartment> Apartments { get; set; } } public class Apartment { public string ApartmentNumber { get; set; } public int SquareFeet { get; set; } }
And here you can see an actual document:
{ "Name": "Corex's Building - Herzliya", "Floors": [ { "Number": 1, "Apartments": [ { "ApartmentNumber": 102, "SquareFeet": 260 }, { "ApartmentNumber": 104, "SquareFeet": 260 }, { "ApartmentNumber": 107, "SquareFeet": 460 } ] }, { "Number": 2, "Apartments": [ { "ApartmentNumber": 201, "SquareFeet": 260 }, { "ApartmentNumber": 203, "SquareFeet": 660 } ] } ] }
Usually the user is working with the Building document. But every now an then, they need to show just a specific apartment.
Normally, I would tell them that they can just load the relevant document and extract the inner information on the client, that is very cheap to do. And that is still the recommendation. But I thought that I would use this opportunity to show off some features that don’t get their due exposure.
We then define the following index:
Note that we can use the Query() method to fetch the query specific parameter from the user. Then we just search the data for the relevant item.
From the client code, this will look like:
var q = session.Query<Building>() .Where(b =>/* some query for building */) .TransformWith<SingleApartment, Apartment>() .AddTransformerParameter("apartmentNumber", 201) .ToList(); var apartment = session.Load<SingleApartment, Apartment>("building/123", configuration => configuration.AddTransformerParameter("apartmentNumber", 102));
And that is all there is to it.
Comments
very nice feature, do you have to specify the query parameter whenever you call the transformer, or can you make it optional?
Paul, You can specify Parameter (in which case it is required) or ParameterOrDefault (in which case you can provide a default value).
Ayende,
I realize that this is not SQL, but is it really that difficult to allow me to use:
var q = session.Query<Apartment>() .Where(a => a.apartmentNumber == 201).ToList();
It seems like this should be doable and certainly makes for much cleaner code.
That should have said .Query<Apartment>()
keeps removing my angle braces so trying [] instead... .QueryApartment
Sergey, You can absolutely do this with Linq:
session.Query<Building>() .Where(b =&;gt; b.Floors.Any( f => f.Apartments.Any( a=> a.ApartmentNumber == "201") ) ) .ToList();
However, what this will return is actually the full document back. Because when you are using it like this, you sometimes want to get just the nested object back, not the full document back. That is why we need to use a transformer for this.
It is not a document. It is called a data structure. Please call things by their name and stop confusing the world.
ThomasX, You do realize that you are talking about documents in doc db, right?
Comment preview