NCache 上に Lucene API を実装することで、Lucene を分散およびスケーラブルにします。 NCacheの分散アーキテクチャ。 から始まる NCache, NCache は別の Lucene 機能である GeoSpatial Indexes をサポートしています。 分散 Lucene で GeoSpatial インデックスを使用する方法を見てみましょう。
Distributed Lucene を使用した全文検索は、インデックス作成と検索の XNUMX つのフェーズに分かれています。 インデックス作成フェーズでは、アナライザーがテキストからインデックスを作成します。 次に、検索フェーズでそれらのインデックスのみが使用されます。
GeoSpatial インデックスを使用しているため、ドキュメント内の経度と緯度の座標にインデックスを付け、それらの保存された場所に基づいてデータを検索したいと考えています。
NCache 詳細 NCache ドキュメンテーション エディションの比較
分散 Lucene で GeoSpatial インデックスを使用する方法
地理空間座標を使用してドキュメントのインデックスを作成し、場所に基づく検索を実行するために、Distributed Lucene は次を使用します。 空間4n、「.NET 用の地理空間ライブラリ」。
サンプル アプリケーションを開始する前に、 NCache インストールされており、分散 Lucene キャッシュが既に作成されています。 分散 Lucene キャッシュを構成する方法については、以下を確認してください。 永続キャッシュを使用して分散 Lucene を作成する.
1.いくつかのランドマークにインデックスを付ける
GeoSpatial Indexes を使用して、過去の旅行でお気に入りのランドマークを保存しましょう。
まず、コンソール アプリケーションを作成し、NuGet パッケージをインストールしましょう。 Lucene.Net.Spatial.NCache
.
の中に、 Program.cs
ファイル、パリ周辺のいくつかのランドマークにインデックスを付けましょう. 各ランドマークには、名前、経度、緯度があります。 このようなもの、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
using DistributedLucene.Net.Spatial; using Landmarks; using Lucene.Net.Analysis.Standard; using Lucene.Net.Index; using Lucene.Net.Spatial.Prefix; using Lucene.Net.Spatial.Prefix.Tree; using Lucene.Net.Store; using Lucene.Net.Util; using Spatial4n.Core.Context; const LuceneVersion LuceneVersion = LuceneVersion.LUCENE_48; const string CacheName = "DemoLuceneCache"; const string IndexName = "landmarks"; const string NameFieldName = "name"; const string LocationFieldName = "landmarkLocation"; // 1. Let's index some locations around Paris var favoriteLandmarks = new Landmark[] { new Landmark("Eiffel Tower", new Location(48.858093, 2.294694)), new Landmark("Sacre Coeur", new Location(48.886452, 2.343121)), new Landmark("Louvre Museum", new Location(48.860294, 2.338629)), new Landmark("Palace of Versailles", new Location(48.804722, 2.121782)), new Landmark("Disneyland Paris", new Location(48.867374, 2.784018)), new Landmark("Arc de Triomphe", new Location(48.873756, 2.294946)) }; IndexLandmarks(favoriteLandmarks); // Later, we will search all landmarks close to a reference point here... static void IndexLandmarks(IEnumerable landmarks) { // Create an SpatialStrategy var context = SpatialContext.GEO; var strategy = new RecursivePrefixTreeStrategy( new GeohashPrefixTree(context, maxLevels: 11), fieldName: LocationFieldName); // Open a directory using var indexDirectory = NCacheDirectory.Open(CacheName, IndexName); var config = new IndexWriterConfig(LuceneVersion, new StandardAnalyzer(LuceneVersion)) { OpenMode = OpenMode.CREATE }; // Create a writer using var writer = new IndexWriter(indexDirectory, config); foreach (var landmark in landmarks) { // Create a SpatialDocument from our Landmark var document = landmark.ToSpatialDocument(strategy); // Add a document writer.AddDocument(document, strategy); } // Write all documents writer.Commit(); } public record Location(double Latitude, double Longitude); public record Landmark(string Name, Location Position); |
に精通している場合 分散 Lucene を使用したフルテキスト インデックス作成の場合、GeoSpatial インデックスの作成も非常に似ています。 開く必要があります NCache ディレクトリ、ライターを作成し、ドキュメントをライターに追加します。
ただし、アナライザーを使用してテキストにインデックスを付ける代わりに、 SpatialStrategy
. 戦略は、ポイントとシェイプをインデックス可能なフィールドに変えます。
ランドマークにインデックスを付けるために、 RecursivePrefixTreeStrategy
. この戦略は、非点形状の検索をサポートします。 これを作成するために、ジオハッシュ ベースのツリーとフィールド名を使用しました。 後で同じフィールド名を使用してドキュメントを作成します。
分散型 Lucene では、新しいタイプのドキュメントが導入されています。 SpatialDocument
. いくつかの図形が添付された Lucene ドキュメントです。 点、長方形、または円のように。
これは ToSpatialDocument()
ランドマークをに変換するために使用した拡張メソッド SpatialDocument
,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
using Lucene.Net.Documents; using Lucene.Net.Spatial; using Spatial4n.Core.Context; using static System.FormattableString; using Document = Lucene.Net.Documents.Document; namespace Landmarks; public static class LocationExtensions { public static SpatialDocument ToSpatialDocument(this Landmark landmark, SpatialStrategy strategy) { var document = new Document { new StringField("name", landmark.Name, Field.Store.YES) }; var point = SpatialContext.GEO.MakePoint(landmark.Position.Longitude, landmark.Position.Latitude); document.Add(new StoredField(strategy.FieldName, Invariant($"{point.X} {point.Y}"))); return new SpatialDocument { Document = document, Shapes = new[] { point } }; } } |
ランドマーク名を文字列フィールドに保存し、ランドマークの場所の文字列表現を別のフィールドに保存したことに注意してください。 SpatialStrategy
フィールド名。 次に、 SpatialDocument
通常の Lucene ドキュメントとランドマークの場所のポイントを使用します。
ポイントを作成するために、 SpatialContext.GEO
コンストラクターを直接使用する代わりに、ファクトリを使用します。
2.最寄りのランドマークを検索
お気に入りのランドマークのインデックスを作成したので、パリ空港の周囲 30 キロメートルにある XNUMX つのランドマークを見つけてみましょう。
更新しましょう Program.cs
を含めるファイル SearchAround()
方法、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
using DistributedLucene.Net.Spatial; using Landmarks; using Lucene.Net.Analysis.Standard; using Lucene.Net.Index; using Lucene.Net.Search; using Lucene.Net.Spatial; using Lucene.Net.Spatial.Prefix; using Lucene.Net.Spatial.Prefix.Tree; using Lucene.Net.Spatial.Queries; using Lucene.Net.Store; using Lucene.Net.Util; using Spatial4n.Core.Context; using Spatial4n.Core.Distance; const LuceneVersion LuceneVersion = LuceneVersion.LUCENE_48; const string CacheName = "DemoLuceneCache"; const string IndexName = "landmarks"; const string NameFieldName = "name"; const string LocationFieldName = "landmarkLocation"; // 2. Let's find five landmarks, 30Km around the airport // and print them var airport = new Landmark("Charles de Gaulle Airport", new Location(49.009724, 2.547778)); SearchAround(5, airport, 30); static void SearchAround(int landmarkCount, Landmark referenceLandmark, int distanceInKm) { // Create a reader using var indexDirectory = NCacheDirectory.Open(CacheName, IndexName); using var reader = DirectoryReader.Open(indexDirectory); var searcher = new IndexSearcher(reader); var startingPoint = referenceLandmark.ToPoint(); var context = SpatialContext.GEO; var sortByName = new Sort(new SortField(NameFieldName, SortFieldType.STRING)); // Create a circle of 30km around a reference point var spatialArgs = new SpatialArgs( SpatialOperation.Intersects, context.MakeCircle(startingPoint, DistanceUtils.Dist2Degrees(distanceInKm, DistanceUtils.EARTH_MEAN_RADIUS_KM))); var strategy = new RecursivePrefixTreeStrategy( new GeohashPrefixTree(context, maxLevels: 11), fieldName: LocationFieldName); // Create a filter using the same strategy var filter = strategy.MakeFilter(spatialArgs); // Search documents var documents = searcher.Search(new MatchAllDocsQuery(), filter, landmarkCount, sortByName); foreach (var scoreDoc in documents.ScoreDocs) { var document = searcher.Doc(scoreDoc.Doc); // Create a result tuple var (name, awayInKm) = document.ToResponse(startingPoint); Console.WriteLine($"Name: {name}"); Console.WriteLine($"Distance: {awayInKm}"); } } |
いくつかのキーワードを含むドキュメントを検索する代わりに、GeoSpatial インデックスを使用して、ドキュメントの位置に基づいて検索を実行します。
空港に近いすべてのランドマークを見つけるために、 Search()
メソッド、フィルター、カウント、およびソート順。 同じものからフィルターを作成しました SpatialStrategy
以前使用していたもの。 そして、私たちは書いた SpatialArg
指定された半径 (キロメートル単位) の開始点を中心とする円内のすべてのドキュメントをクエリします。 このような、
1 2 3 |
var spatialArgs = new SpatialArgs( SpatialOperation.Intersects, context.MakeCircle(startingPoint, DistanceUtils.Dist2Degrees(distanceInKm, DistanceUtils.EARTH_MEAN_RADIUS_KM))); |
次に、見つかった各ドキュメントから応答オブジェクトを作成し、開始点からの距離を計算しました。 このような、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
using Spatial4n.Core.Context; using Spatial4n.Core.Distance; using Spatial4n.Core.Shapes; using System.Globalization; using Document = Lucene.Net.Documents.Document; namespace Landmarks; public static class LocationExtensions { public static (string Name, double DistanceInKm) ToResponse(this Document document, IPoint startingPoint) { var name = document.GetField("name").GetStringValue(); var location = document.GetField("landmarkLocation").GetStringValue(); var positions = location.Split(' '); var x = double.Parse(positions[0], CultureInfo.InvariantCulture); var y = double.Parse(positions[1], CultureInfo.InvariantCulture); var distanceInDeg = SpatialContext.GEO.CalcDistance(startingPoint, x, y); var distanceInKm = distanceInDeg * DistanceUtils.DEG_TO_KM; return (name, distanceInKm); } } |
ランドマークの場所を解析し、 CalcDistance()
開始点で、ドキュメントの位置を見つけました。
これらは、空港に最も近いインデックス付きの XNUMX つのランドマークです。
NCache 詳細 ダウンロード NCache によるフルテキスト インデックス作成 NCache ルセン
まとめ
分散 Lucene の GeoSpatial インデックスを実装するには、 SpatialDocument
平野の代わりに Document
.
Since NCache Lucene.NET API を実装しているため、Lucene コードをスケーリングできます。 NCache 数行のコードを変更し、いくつかの命名規則に従ってください。
他の最近のことを知るために NCache 機能、チェック 新機能 NCache? 分散 Lucene を使用した GeoSpatial インデックスの詳細については、次を確認してください。 分散LuceneGeo-SpatialAPI.
コードに従うには、この投稿に書いた、私のチェック NCache デモ GitHub 上のリポジトリ。