Show / Hide Table of Contents

    Class DistanceFacetsExample

    Shows simple usage of dynamic range faceting, using the expressions module to calculate distance.

    Inheritance
    System.Object
    DistanceFacetsExample
    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 Source

    EARTH_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
    | Improve this Doc View Source

    ORIGIN_LATITUDE

    The "home" latitude.

    Declaration
    public static readonly double ORIGIN_LATITUDE
    Field Value
    Type Description
    System.Double
    | Improve this Doc View Source

    ORIGIN_LONGITUDE

    The "home" longitude.

    Declaration
    public static readonly double ORIGIN_LONGITUDE
    Field Value
    Type Description
    System.Double

    Methods

    | Improve this Doc View Source

    Dispose()

    Declaration
    public void Dispose()
    | Improve this Doc View Source

    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
    | Improve this Doc View Source

    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
    | Improve this Doc View Source

    Index()

    Build the example index.

    Declaration
    public void Index()
    | Improve this Doc View Source

    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
    | Improve this Doc View Source

    Search()

    User runs a query and counts facets.

    Declaration
    public FacetResult Search()
    Returns
    Type Description
    FacetResult
    • Improve this Doc
    • View Source
    Back to top Copyright © 2020 Licensed to the Apache Software Foundation (ASF)