Class DistanceFacetsExample
Shows simple usage of dynamic range faceting, using the expressions module to calculate distance.
Inheritance
System.Object
DistanceFacetsExample
Implements
System.IDisposable
Inherited Members
System.Object.Equals(System.Object)
System.Object.Equals(System.Object, System.Object)
System.Object.GetHashCode()
System.Object.GetType()
System.Object.MemberwiseClone()
System.Object.ReferenceEquals(System.Object, System.Object)
System.Object.ToString()
Namespace: Lucene.Net.Demo.Facet
Assembly: Lucene.Net.Demo.dll
Syntax
public class DistanceFacetsExample : IDisposable
Examples
using J2N;
using Lucene.Net.Analysis.Core;
using Lucene.Net.Documents;
using Lucene.Net.Expressions;
using Lucene.Net.Expressions.JS;
using Lucene.Net.Facet;
using Lucene.Net.Facet.Range;
using Lucene.Net.Facet.Taxonomy;
using Lucene.Net.Index;
using Lucene.Net.Queries;
using Lucene.Net.Queries.Function;
using Lucene.Net.Search;
using Lucene.Net.Store;
using Lucene.Net.Util;
using System;
using System.Diagnostics;
using System.Globalization;
namespace Lucene.Net.Demo.Facet
{
/// <summary>
/// Shows simple usage of dynamic range faceting, using the
/// expressions module to calculate distance.
/// </summary>
public class DistanceFacetsExample : IDisposable
{
/// <summary>
/// Using a constant for all functionality related to a specific index
/// is the best strategy. This allows you to upgrade Lucene.Net first
/// and plan the upgrade of the index binary format for a later time.
/// Once the index is upgraded, you simply need to update the constant
/// version and redeploy your application.
/// </summary>
private const LuceneVersion EXAMPLE_VERSION = LuceneVersion.LUCENE_48;
internal static readonly DoubleRange ONE_KM = new DoubleRange("< 1 km", 0.0, true, 1.0, false);
internal static readonly DoubleRange TWO_KM = new DoubleRange("< 2 km", 0.0, true, 2.0, false);
internal static readonly DoubleRange FIVE_KM = new DoubleRange("< 5 km", 0.0, true, 5.0, false);
internal static readonly DoubleRange TEN_KM = new DoubleRange("< 10 km", 0.0, true, 10.0, false);
private readonly Directory indexDir = new RAMDirectory();
private IndexSearcher searcher;
private readonly FacetsConfig config = new FacetsConfig();
/// <summary>The "home" latitude.</summary>
public readonly static double ORIGIN_LATITUDE = 40.7143528;
/// <summary>The "home" longitude.</summary>
public readonly static double ORIGIN_LONGITUDE = -74.0059731;
/// <summary>
/// Radius of the Earth in KM
/// <para/>
/// NOTE: this is approximate, because the earth is a bit
/// wider at the equator than the poles. See
/// http://en.wikipedia.org/wiki/Earth_radius
/// </summary>
public readonly static double EARTH_RADIUS_KM = 6371.01;
/// <summary>Build the example index.</summary>
public void Index()
{
using (IndexWriter writer = new IndexWriter(indexDir, new IndexWriterConfig(EXAMPLE_VERSION,
new WhitespaceAnalyzer(EXAMPLE_VERSION))))
{
// TODO: we could index in radians instead ... saves all the conversions in GetBoundingBoxFilter
// Add documents with latitude/longitude location:
Document doc = new Document();
doc.Add(new DoubleField("latitude", 40.759011, Field.Store.NO));
doc.Add(new DoubleField("longitude", -73.9844722, Field.Store.NO));
writer.AddDocument(doc);
doc = new Document();
doc.Add(new DoubleField("latitude", 40.718266, Field.Store.NO));
doc.Add(new DoubleField("longitude", -74.007819, Field.Store.NO));
writer.AddDocument(doc);
doc = new Document();
doc.Add(new DoubleField("latitude", 40.7051157, Field.Store.NO));
doc.Add(new DoubleField("longitude", -74.0088305, Field.Store.NO));
writer.AddDocument(doc);
// Open near-real-time searcher
searcher = new IndexSearcher(DirectoryReader.Open(writer, true));
} // Disposes writer
}
private ValueSource GetDistanceValueSource()
{
Expression distance = JavascriptCompiler.Compile(
string.Format(CultureInfo.InvariantCulture, "haversin({0:R},{1:R},latitude,longitude)", ORIGIN_LATITUDE, ORIGIN_LONGITUDE));
SimpleBindings bindings = new SimpleBindings();
bindings.Add(new SortField("latitude", SortFieldType.DOUBLE));
bindings.Add(new SortField("longitude", SortFieldType.DOUBLE));
return distance.GetValueSource(bindings);
}
/// <summary>
/// Given a latitude and longitude (in degrees) and the
/// maximum great circle (surface of the earth) distance,
/// returns a simple Filter bounding box to "fast match"
/// candidates.
/// </summary>
public static Filter GetBoundingBoxFilter(double originLat, double originLng, double maxDistanceKM)
{
// Basic bounding box geo math from
// http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates,
// licensed under creative commons 3.0:
// http://creativecommons.org/licenses/by/3.0
// TODO: maybe switch to recursive prefix tree instead
// (in lucene/spatial)? It should be more efficient
// since it's a 2D trie...
// Degrees -> Radians:
double originLatRadians = originLat.ToRadians();
double originLngRadians = originLng.ToRadians();
double angle = maxDistanceKM / (SloppyMath.EarthDiameter(originLat) / 2.0);
double minLat = originLatRadians - angle;
double maxLat = originLatRadians + angle;
double minLng;
double maxLng;
if (minLat > -90.ToRadians() && maxLat < 90.ToRadians())
{
double delta = Math.Asin(Math.Sin(angle) / Math.Cos(originLatRadians));
minLng = originLngRadians - delta;
if (minLng < -180.ToRadians())
{
minLng += 2 * Math.PI;
}
maxLng = originLngRadians + delta;
if (maxLng > 180.ToRadians())
{
maxLng -= 2 * Math.PI;
}
}
else
{
// The query includes a pole!
minLat = Math.Max(minLat, -90.ToRadians());
maxLat = Math.Min(maxLat, 90.ToRadians());
minLng = -180.ToRadians();
maxLng = 180.ToRadians();
}
BooleanFilter f = new BooleanFilter();
// Add latitude range filter:
f.Add(NumericRangeFilter.NewDoubleRange("latitude", minLat.ToDegrees(), maxLat.ToDegrees(), true, true),
Occur.MUST);
// Add longitude range filter:
if (minLng > maxLng)
{
// The bounding box crosses the international date
// line:
BooleanFilter lonF = new BooleanFilter();
lonF.Add(NumericRangeFilter.NewDoubleRange("longitude", minLng.ToDegrees(), null, true, true),
Occur.SHOULD);
lonF.Add(NumericRangeFilter.NewDoubleRange("longitude", null, maxLng.ToDegrees(), true, true),
Occur.SHOULD);
f.Add(lonF, Occur.MUST);
}
else
{
f.Add(NumericRangeFilter.NewDoubleRange("longitude", minLng.ToDegrees(), maxLng.ToDegrees(), true, true),
Occur.MUST);
}
return f;
}
/// <summary>User runs a query and counts facets.</summary>
public FacetResult Search()
{
FacetsCollector fc = new FacetsCollector();
searcher.Search(new MatchAllDocsQuery(), fc);
Facets facets = new DoubleRangeFacetCounts("field", GetDistanceValueSource(), fc,
GetBoundingBoxFilter(ORIGIN_LATITUDE, ORIGIN_LONGITUDE, 10.0),
ONE_KM,
TWO_KM,
FIVE_KM,
TEN_KM);
return facets.GetTopChildren(10, "field");
}
/// <summary>User drills down on the specified range.</summary>
public TopDocs DrillDown(DoubleRange range)
{
// Passing no baseQuery means we drill down on all
// documents ("browse only"):
DrillDownQuery q = new DrillDownQuery(null);
ValueSource vs = GetDistanceValueSource();
q.Add("field", range.GetFilter(GetBoundingBoxFilter(ORIGIN_LATITUDE, ORIGIN_LONGITUDE, range.Max), vs));
DrillSideways ds = new SearchDrillSideways(searcher, config, vs);
return ds.Search(q, 10).Hits;
}
private class SearchDrillSideways : DrillSideways
{
private readonly ValueSource vs;
public SearchDrillSideways(IndexSearcher indexSearcher, FacetsConfig facetsConfig, ValueSource valueSource)
: base(indexSearcher, facetsConfig, (TaxonomyReader)null)
{
this.vs = valueSource;
}
protected override Facets BuildFacetsResult(FacetsCollector drillDowns, FacetsCollector[] drillSideways, string[] drillSidewaysDims)
{
Debug.Assert(drillSideways.Length == 1);
return new DoubleRangeFacetCounts("field", vs, drillSideways[0], ONE_KM, TWO_KM, FIVE_KM, TEN_KM);
}
}
public void Dispose()
{
searcher?.IndexReader?.Dispose();
indexDir?.Dispose();
}
/// <summary>Runs the search and drill-down examples and prints the results.</summary>
public static void Main(string[] args)
{
using (DistanceFacetsExample example = new DistanceFacetsExample())
{
example.Index();
Console.WriteLine("Distance facet counting example:");
Console.WriteLine("-----------------------");
Console.WriteLine(example.Search());
Console.WriteLine("\n");
Console.WriteLine("Distance facet drill-down example (field/< 2 km):");
Console.WriteLine("---------------------------------------------");
TopDocs hits = example.DrillDown(TWO_KM);
Console.WriteLine(hits.TotalHits + " totalHits");
} // Disposes example
}
}
}
Fields
| Improve this Doc View SourceEARTH_RADIUS_KM
Radius of the Earth in KM
NOTE: this is approximate, because the earth is a bit wider at the equator than the poles. See http://en.wikipedia.org/wiki/Earth_radius
Declaration
public static readonly double EARTH_RADIUS_KM
Field Value
Type | Description |
---|---|
System.Double |
ORIGIN_LATITUDE
The "home" latitude.
Declaration
public static readonly double ORIGIN_LATITUDE
Field Value
Type | Description |
---|---|
System.Double |
ORIGIN_LONGITUDE
The "home" longitude.
Declaration
public static readonly double ORIGIN_LONGITUDE
Field Value
Type | Description |
---|---|
System.Double |
Methods
| Improve this Doc View SourceDispose()
Declaration
public void Dispose()
DrillDown(DoubleRange)
User drills down on the specified range.
Declaration
public TopDocs DrillDown(DoubleRange range)
Parameters
Type | Name | Description |
---|---|---|
DoubleRange | range |
Returns
Type | Description |
---|---|
TopDocs |
GetBoundingBoxFilter(Double, Double, Double)
Given a latitude and longitude (in degrees) and the maximum great circle (surface of the earth) distance, returns a simple Filter bounding box to "fast match" candidates.
Declaration
public static Filter GetBoundingBoxFilter(double originLat, double originLng, double maxDistanceKM)
Parameters
Type | Name | Description |
---|---|---|
System.Double | originLat | |
System.Double | originLng | |
System.Double | maxDistanceKM |
Returns
Type | Description |
---|---|
Filter |
Index()
Build the example index.
Declaration
public void Index()
Main(String[])
Runs the search and drill-down examples and prints the results.
Declaration
public static void Main(string[] args)
Parameters
Type | Name | Description |
---|---|---|
System.String[] | args |
Search()
User runs a query and counts facets.
Declaration
public FacetResult Search()
Returns
Type | Description |
---|---|
FacetResult |
Implements
System.IDisposable