In RavenDB I understand when you require to count properties matching a certain criteria, it can be achieved
- by creating facets against those property names you wish to group,
- and then creating an index over the above properties and the properties required in the where clause.
- and finally issuing a query by using the above index. and materializing the query using the ToFacets extension.
But what happens when your where clause happens to contain a predicate against a property that is a collection of values on the document? Because if I add the nested property from the collection to the index against the parent document, my facet counts on properties on the parent document will not be accurate?
for e.g.
public class Camera {
string Make { get;set; }
string Model { get;set; }
double Price { get;set; }
IEnumerable<string> Showrooms { get;set; }
}
My query would look like
(from camera in session.Query<Camera, Camera_Facets>()
where camera.Price < 100 && camera.ShowRooms.Any(s => s.In("VIC", "ACT", "QLD"))
select new {
camera.Make,
camera.Model,
camera.Price}
).ToFacets("facets/CameraFacets");
Update:
Here is the failing test
[TestFixture]
public class FacetedSearchWhenQueryingNestedCollections
{
public class Car
{
public Car()
{
Locations = new List<string>();
}
public string Id { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public double Price { get; set; }
public IEnumerable<string> Locations { get; set; }
}
public class Car_Facets : AbstractIndexCreationTask<Car>
{
public Car_Facets()
{
Map = cars => from car in cars
select new {car.Make, car.Model, car.Price};
}
}
private static IDocumentSession OpenSession
{
get { return new EmbeddableDocumentStore {RunInMemory = true}.Initialize().OpenSession(); }
}
[Test]
public void CanGetFacetsWhenQueryingNesetedCollectionValues()
{
var cars = Builder<Car>.CreateListOfSize(50)
.Section(0, 10)
.With(x => x.Model = "Camry")
.With(x => x.Make = "Toyota")
.With(x => x.Price = 2000)
.With(x => x.Locations = new[] {"VIC", "ACT"})
.Section(11, 20)
.With(x => x.Model = "Corolla")
.With(x => x.Make = "Toyota")
.With(x => x.Price = 1000)
.With(x => x.Locations = new[] { "NSW", "ACT" })
.Section(21, 30)
.With(x => x.Model = "Rx8")
.With(x => x.Make = "Mazda")
.With(x => x.Price = 5000)
.With(x => x.Locations = new[] { "ACT", "SA", "TAS" })
.Section(31, 49)
.With(x => x.Model = "Civic")
.With(x => x.Make = "Honda")
.With(x => x.Price = 1500)
.With(x => x.Locations = new[] { "QLD", "SA", "TAS" })
.Build();
IDictionary<string, IEnumerable<FacetValue>> facets;
using(var s = OpenSession)
{
s.Store(new FacetSetup { Id = "facets/CarFacets", Facets = new List<Facet> { new Facet { Name = "Model" }, new Facet { Name = "Make" }, new Facet { Name = "Price" } } });
s.SaveChanges();
IndexCreation.CreateIndexes(typeof(Car_Facets).Assembly, s.Advanced.DocumentStore);
foreach (var car in cars)
s.Store(car);
s.SaveChanges();
s.Query<Car, Car_Facets>().Customize(x => x.WaitForNonStaleResults()).ToList();
facets = s.Query<Car, Car_Facets>()
.Where(x => x.Price < 3000)
.Where(x => x.Locations.Any(l => l.In("QLD", "VIC")))
.ToFacets("facets/CarFacets");
}
Assert.IsNotNull(facets);
Assert.IsTrue(facets.All(f => f.Value.Count() > 0));
Assert.IsTrue(facets.All(f => f.Value.All(x => x.Count > 0)));
}
[TearDown]
public void ClearData()
{
using(var s = OpenSession)
{
foreach (var car in s.Query<Car>().ToList())
s.Delete(car);
s.SaveChanges();
}
}
}
But if I change my query to be. (not querying the nested collection any more)
facets = s.Query<Car, Car_Facets>()
.Where(x => x.Price < 3000)
.ToFacets("facets/CarFacets");
Now I get 3 Enumerations in the dictionary, all with values.
if I change my index to be
Create a class for the projection
And then query as
it works.