Given a model like this:
public class Location
{
public int Id { get; set; }
public string Name { get; set; }
public Coordinates Geo { get; set; }
public IList<Review> Reviews { get; set; }
}
public class Review
{
public string Comment { get; set; }
public int Rating { get; set; }
}
How can I write a query that will return matching locations sorted in descending order by their Rating?
I have an index that allows me to query for locations within a certain radius around a geopoint:
public class Locations_ByCoordinates : AbstractIndexCreationTask<Location>
{
public Locations_ByCoordinates()
{
Map = locations => from l in locations
select new {_ = SpatialIndex.Generate(l.Geo.Lat, l.Geo.Lon)};
}
}
This seems to work and I am able to sort the results like so:
RavenQueryStatistics stats;
var query = session.Advanced.LuceneQuery<Location>(Names.Indexes.LocationsByCoordinates)
.Statistics(out stats)
.WithinRadiusOf(radius, coordinates.Lat, coordinates.Lon);
switch (sort)
{
case Sort.Distance:
query = query.SortByDistance();
break;
case Sort.Rating:
query = query.AddOrder(l => l.Reviews.Average(r => r.Rating), true); // TODO: This throws an exception, perhaps a modified index with an aggregate is required? Maybe Map/Reduce?
break;
case Sort.Alphabetical:
query = query.OrderBy(l => l.Name);
break;
case Sort.Number_of_ratings:
query = query.AddOrder(l => l.Reviews.Count, true);
break;
case Sort.Relevance: // This is the default ordering in RavenDB.
break;
default:
throw new ArgumentOutOfRangeException("sort");
}
Every option works OK except sorting by average rating which throws an exception about unknown field. I feel this has to be a Map/Reduce index rather than a Map index. But I have no idea how to write it.
I am migrating this code from MongoDB, but I used to sort the documents by rating on the client side there, I thought I’d do this on the server side now.
Since all reviews are in the location, and you want the average rating of the reviews for each location, you don’t need to reduce. Just create a new field in your map.