Browse files

Fix sort by distance functionality

  • Loading branch information...
1 parent ce35962 commit baa55d493a85bfb3d9f1093712817d7744ee8b5d @synhershko synhershko committed May 21, 2012
View
2 Raven.Abstractions/Data/Constants.cs
@@ -7,6 +7,8 @@ public static class Constants
public const string LastModified = "Last-Modified";
public const string DefaultDatabase = "<default>";
public const string TemporaryScoreValue = "Temp-Index-Score";
+ public const string SpatialFieldName = "__spatial";
+ public const string SpatialShapeFieldName = "__spatialShape";
public const string DistanceFieldName = "__distance";
public const string RandomFieldName = "__random";
public const string NullValueNotAnalyzed = "[[NULL_VALUE]]";
View
19 Raven.Database/Extensions/IndexingExtensions.cs
@@ -4,18 +4,14 @@
// </copyright>
//-----------------------------------------------------------------------
using System;
-using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.Search;
-using Lucene.Net.Util;
using Raven.Abstractions.Data;
using Raven.Abstractions.Indexing;
-using Raven.Database.Data;
-using Raven.Database.Indexing;
using Raven.Database.Indexing.Sorting;
using Constants = Raven.Abstractions.Data.Constants;
@@ -92,7 +88,9 @@ public static Sort GetSort(this IndexQuery self, IndexDefinition indexDefinition
{
if (self.SortedFields == null || self.SortedFields.Length <= 0)
return null;
- var isSpatialIndexQuery = self is SpatialIndexQuery;
+
+ var spatialQuery = self as SpatialIndexQuery;
+
return new Sort(self.SortedFields
.Select(sortedField =>
{
@@ -103,12 +101,11 @@ public static Sort GetSort(this IndexQuery self, IndexDefinition indexDefinition
return new RandomSortField(Guid.NewGuid().ToString());
return new RandomSortField(parts[1]);
}
- // TODO
- //if (isSpatialIndexQuery && sortedField.Field == Constants.DistanceFieldName)
- //{
- // var dsort = new Lucene.Net.Spatial.Tier.DistanceFieldComparatorSource((Lucene.Net.Spatial.Tier.DistanceFilter)filter);
- // return new SortField(Constants.DistanceFieldName, dsort, sortedField.Descending);
- //}
+ if (spatialQuery != null && sortedField.Field == Constants.DistanceFieldName)
+ {
+ var dsort = new SpatialDistanceFieldComparatorSource(spatialQuery.Latitude, spatialQuery.Longitude);
+ return new SortField(Constants.DistanceFieldName, dsort, sortedField.Descending);
+ }
var sortOptions = GetSortOption(indexDefinition, sortedField.Field);
if (sortOptions == null || sortOptions == SortOptions.None)
return new SortField(sortedField.Field, CultureInfo.InvariantCulture, sortedField.Descending);
View
5 Raven.Database/Indexing/Index.cs
@@ -902,7 +902,7 @@ private static void DisposeAnalyzerAndFriends(List<Action> toDispose, PerFieldAn
private TopDocs ExecuteQuery(IndexSearcher indexSearcher, Query luceneQuery, int start, int pageSize,
IndexQuery indexQuery)
{
- Sort sort = indexQuery.GetSort(parent.indexDefinition);
+ var sort = indexQuery.GetSort(parent.indexDefinition);
if (pageSize == Int32.MaxValue) // we want all docs
{
@@ -915,7 +915,8 @@ private static void DisposeAnalyzerAndFriends(List<Action> toDispose, PerFieldAn
// NOTE: We get Start + Pagesize results back so we have something to page on
if (sort != null)
{
- return indexSearcher.Search(luceneQuery, null, minPageSize, sort);
+ var ret = indexSearcher.Search(luceneQuery, null, minPageSize, sort);
+ return ret;
}
return indexSearcher.Search(luceneQuery, null, minPageSize);
}
View
158 Raven.Database/Indexing/Sorting/SpatialDistanceSortField.cs
@@ -0,0 +1,158 @@
+using System;
+using System.Diagnostics;
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+using Raven.Abstractions.Data;
+using Spatial4n.Core.Exceptions;
+using Spatial4n.Core.Shapes;
+
+namespace Raven.Database.Indexing.Sorting
+{
+ public class SpatialDistanceSortField : SortField
+ {
+ private readonly double lng, lat;
+
+ public SpatialDistanceSortField(string field, bool reverse, SpatialIndexQuery qry) : base(field, CUSTOM, reverse)
+ {
+ lat = qry.Latitude;
+ lng = qry.Longitude;
+ }
+
+ public override FieldComparator GetComparator(int numHits, int sortPos)
+ {
+ return new SpatialDistanceFieldComparatorSource.SpatialDistanceFieldComparator(lat, lng, numHits);
+ }
+
+ public override FieldComparatorSource GetComparatorSource()
+ {
+ return new SpatialDistanceFieldComparatorSource(lat, lng);
+ }
+ }
+
+ public class SpatialDistanceFieldComparatorSource : FieldComparatorSource
+ {
+ protected readonly double lng, lat;
+
+ public SpatialDistanceFieldComparatorSource(double lat, double lng)
+ {
+ this.lat = lat;
+ this.lng = lng;
+ }
+
+ public override FieldComparator NewComparator(string fieldname, int numHits, int sortPos, bool reversed)
+ {
+ return new SpatialDistanceFieldComparator(lat, lng, numHits);
+ }
+
+ public class SpatialDistanceFieldComparator : FieldComparator
+ {
+ private readonly double[] values;
+ private double[] currentReaderValues;
+ private double bottom;
+ private readonly Point originPt;
+
+ public SpatialDistanceFieldComparator(double lat, double lng, int numHits)
+ {
+ values = new double[numHits];
+ originPt = SpatialIndex.RavenSpatialContext.MakePoint(lng, lat);
+ }
+
+ public override int Compare(int slot1, int slot2)
+ {
+ double a = values[slot1];
+ double b = values[slot2];
+ if (a > b)
+ return 1;
+ if (a < b)
+ return -1;
+
+ return 0;
+ }
+
+ public override void SetBottom(int slot)
+ {
+ bottom = values[slot];
+ }
+
+ public override int CompareBottom(int doc)
+ {
+ var v2 = currentReaderValues[doc];
+ if (bottom > v2)
+ {
+ return 1;
+ }
+
+ if (bottom < v2)
+ {
+ return -1;
+ }
+
+ return 0;
+ }
+
+ public override void Copy(int slot, int doc)
+ {
+ values[slot] = currentReaderValues[doc];
+ }
+
+ public override void SetNextReader(IndexReader reader, int docBase)
+ {
+ currentReaderValues = ComputeDistances(reader);
+ }
+
+ public override IComparable Value(int slot)
+ {
+ return values[slot];
+ }
+
+ protected internal double[] ComputeDistances(IndexReader reader)
+ {
+ double[] retArray = null;
+ var termDocs = reader.TermDocs();
+ var termEnum = reader.Terms(new Term(Constants.SpatialShapeFieldName));
+ try
+ {
+ do
+ {
+ Term term = termEnum.Term();
+ if (term == null)
+ break;
+
+ Debug.Assert(Constants.SpatialShapeFieldName.Equals(term.Field()));
+
+ Shape termval;
+ try
+ {
+ termval = SpatialIndex.RavenSpatialContext.ReadShape(term.Text()); // read shape
+ }
+ catch (InvalidShapeException)
+ {
+ continue;
+ }
+
+ var pt = termval as Point;
+ if (pt == null)
+ continue;
+
+ var distance = SpatialIndex.RavenSpatialContext.GetDistCalc().Distance(pt, originPt);
+
+ if (retArray == null)
+ // late init
+ retArray = new double[reader.MaxDoc()];
+ termDocs.Seek(termEnum);
+ while (termDocs.Next())
+ {
+ retArray[termDocs.Doc()] = distance;
+ }
+ } while (termEnum.Next());
+ }
+ finally
+ {
+ termDocs.Close();
+ termEnum.Close();
+ }
+ return retArray ?? new double[reader.MaxDoc()];
+ }
+ }
+ }
+}
View
22 Raven.Database/Indexing/SpatialIndex.cs
@@ -10,6 +10,7 @@
using Lucene.Net.Spatial;
using Lucene.Net.Spatial.Prefix;
using Lucene.Net.Spatial.Prefix.Tree;
+using Raven.Abstractions.Data;
using Spatial4n.Core.Context;
using Spatial4n.Core.Distance;
using Spatial4n.Core.Query;
@@ -28,14 +29,15 @@ public static class SpatialIndex
static SpatialIndex()
{
maxLength = GeohashPrefixTree.GetMaxLevelsPossible();
- fieldInfo = new SimpleSpatialFieldInfo("RavenDBSpatial");
+ fieldInfo = new SimpleSpatialFieldInfo(Constants.SpatialFieldName);
strategy = new RecursivePrefixTreeStrategy(new GeohashPrefixTree(RavenSpatialContext, maxLength));
}
public static IEnumerable<Fieldable> Generate(double? lat, double? lng)
{
Shape shape = RavenSpatialContext.MakePoint(lng ?? 0, lat ?? 0);
- return strategy.CreateFields(fieldInfo, shape, true, false).Where(f => f != null);
+ return strategy.CreateFields(fieldInfo, shape, true, false).Where(f => f != null)
+ .Concat(new[] { new Field(Constants.SpatialShapeFieldName, RavenSpatialContext.ToString(shape), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS), });
}
/// <summary>
@@ -49,5 +51,21 @@ public static Query MakeQuery(double lat, double lng, double radius)
{
return strategy.MakeQuery(new SpatialArgs(SpatialOperation.IsWithin, RavenSpatialContext.MakeCircle(lng, lat, radius)), fieldInfo);
}
+
+ public static Filter MakeFilter(IndexQuery indexQuery)
+ {
+ var spatialQry = indexQuery as SpatialIndexQuery;
+ if (spatialQry == null) return null;
+
+ var args = new SpatialArgs(SpatialOperation.IsWithin, RavenSpatialContext.MakeCircle(spatialQry.Longitude, spatialQry.Latitude, spatialQry.Radius));
+ return strategy.MakeFilter(args, fieldInfo);
+ }
+
+ public static double GetDistance(double fromLat, double fromLng, double toLat, double toLng)
+ {
+ var ptFrom = RavenSpatialContext.MakePoint(fromLng, fromLat);
+ var ptTo = RavenSpatialContext.MakePoint(toLng, toLat);
+ return RavenSpatialContext.GetDistCalc().Distance(ptFrom, ptTo);
+ }
}
}
View
3 Raven.Database/Raven.Database.csproj
@@ -144,6 +144,9 @@
<Compile Include="Indexing\Sorting\RandomFieldComparatorSource.cs" />
<Compile Include="Indexing\Sorting\RandomFieldComparator.cs" />
<Compile Include="Indexing\Sorting\RandomSortField.cs" />
+ <Compile Include="Indexing\Sorting\SpatialDistanceSortField.cs">
+ <SubType>Code</SubType>
+ </Compile>
<Compile Include="Linq\Ast\TransformDynamicLambdaExpressions.cs" />
<Compile Include="Plugins\Builtins\InvalidDocumentNames.cs" />
<Compile Include="Plugins\Builtins\Tenants\ModifiedTenantDatabase.cs" />
View
10 SharedLibs/Lucene.Net.Contrib.Spatial.XML
@@ -104,10 +104,20 @@
Corresponds with Solr's FieldType.createFields().
</member>
<member name="M:Lucene.Net.Spatial.SpatialStrategy`1.MakeQuery(Spatial4n.Core.Query.SpatialArgs,`0)">
+ <summary>
Make a query
+ </summary>
+ <param name="args"></param>
+ <param name="fieldInfo"></param>
+ <returns></returns>
</member>
<member name="M:Lucene.Net.Spatial.SpatialStrategy`1.MakeFilter(Spatial4n.Core.Query.SpatialArgs,`0)">
+ <summary>
Make a Filter
+ </summary>
+ <param name="args"></param>
+ <param name="fieldInfo"></param>
+ <returns></returns>
</member>
<member name="M:Lucene.Net.Spatial.Prefix.PrefixTreeStrategy.SetDefaultFieldValuesArrayLen(System.Int32)">
Used in the in-memory ValueSource as a default ArrayList length for this field's array of values, per doc.
View
BIN SharedLibs/Lucene.Net.Contrib.Spatial.dll
Binary file not shown.
View
BIN SharedLibs/Lucene.Net.Contrib.Spatial.pdb
Binary file not shown.
View
BIN SharedLibs/Spatial4n.Core.dll
Binary file not shown.

0 comments on commit baa55d4

Please sign in to comment.