Lucene ist eine effiziente und leistungsstarke Suchmaschine, die die Volltextsuche in .NET unterstützt. Es erleichtert die Textsuche und bietet Ihnen eine Vielzahl von APIs für schnelle und benutzerfreundliche Textsuchtechniken.
Darüber hinaus gibt es eine Sache, die Lucene nicht bietet, und das ist Skalierbarkeit. Lucene-Anwendungen schreiben normalerweise Daten in eine Datei und speichern sie auf der Festplatte, was zu einer erheblichen Speicherzuweisung führt. NCache stellt ein Verteiltes Lucene Funktion, die Ihre Lucene-Anwendungen skalierbar und verteilt macht. Es bietet auch eine linear skalierbare und verteilte Alternative zum Thema Single Point of Failure.
NCache Details Funktionsweise von Distributed Lucene Verteilte Lucene-Dokumente
Weitere Informationen zu Distributed Lucene, es funktioniert, und warum es Ihre erste Wahl sein sollte, finden Sie hier Verteiltes Lucene: Volltextsuche in .NET für Skalierbarkeit. Bevor wir uns mit den Details von Distributed Lucene befassen, lassen Sie uns eine Distributed Lucene-Lösung durchgehen, die den grundlegenden Arbeitsablauf demonstriert.
Das folgende Codebeispiel zeigt ein verteiltes Lucene, das hauptsächlich diese drei Schritte ausführt:
- Initialisieren Sie die NCache Verzeichnis.
- Indizieren Sie die Dokumente, um sie zu durchsuchen.
- Führen Sie eine Suche in den indizierten Dokumenten durch.
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 |
{ // Specify the cache name that is used for Lucene string cache = "LuceneCache"; // Specify the index name to create the indexes string indexName = "ProductIndex"; // Create a directory and open it on the cache and the index path Directory directory = NCacheDirectory.Open(cache, indexName); // Specify the analyzer used to analyze data Analyzer analyzer = new StandardAnalyzer(LuceneVersion.LUCENE_48); // Create an indexWriterConfig which holds all the configurations to create an instance of the writer IndexWriterConfig config = new IndexWriterConfig(LuceneVersion.LUCENE_48, analyzer); // Create the indexWriter with the analyzer and the configuration IndexWriter indexWriter = new IndexWriter(directory, config); // Add the products information that is to be indexed Product[] products = FetchProductsFromDB(); foreach (var prod in products) { // Create a document and add fields to it Document doc = new Document(); doc.Add(new TextField("id", prod.ProductID(), Field.Store.YES)); doc.Add(new TextField("name", prod.ProductName, Field.Store.NO)); doc.Add(new TextField("category", prod.Category, Field.Store.YES)); doc.Add(new TextField("description", prod.Description, Field.Store.YES)); // Writer is created previously indexWriter.AddDocument(doc); // Call ‘Commit’ to save the changes indexWriter.Commit(); } // Open a new reader instance IndexReader reader = indexWriter.GetReader(true); // A searcher is opened to perform searching IndexSearcher indexSearcher = new IndexSearcher(reader); // Specify the searchTerm and the fieldName string searchTerm = "Beverages"; string fieldName = "Category"; LuceneVersion version = LuceneVersion.LUCENE_48; // Create a query parser and parse the query with the parser // Analyzer is whitespace analyzer as specified beforehand QueryParser parser = new QueryParser(version, fieldName, analyzer); Query query = parser.Parse(searchTerm); // Returns the top 10 hits from the result set ScoreDoc[] docsFound = indexSearcher.Search(query, 10).ScoreDocs; // Closes all the files associated with this index reader.Dispose(); } catch(Exception ex) { // Handle Lucene exceptions } |
In diesem Blog konzentrieren wir uns auf die detaillierten Schritte und die Funktionsweise von verteiltem Lucene für die Volltextsuche.
NCache Details Funktionsweise von Distributed Lucene Lucene-Komponenten und Übersicht
Wie migriere ich zu verteiltem Lucene?
Verteiltes Lucene funktioniert genauso wie Lucene. Ein großer Vorteil bei der Verwendung von verteiltem Lucene besteht darin, dass Sie dieselbe API wie Lucene erhalten. Als Lucene-Benutzer erhalten Sie die Skalierbarkeit, die Sie sich wünschen, mit einem Add-on einer einzigen Liner-Code-Änderung. Sie müssen nur verwenden NCache Directory und Ihre Anwendung ist startklar. Es gibt nur sehr wenige Verhaltens- und API-Änderungen in verteiltem Lucene, die in der Dokumentation aufgeführt sind hier.
Sehen wir uns diese Schritte aus technischer Sicht genauer an.
Schritt 1: Verbinden mit NCache Verzeichnis
Beginnen wir mit der Einführung NCache in Ihrer .NET-Anwendung mit Lucene. Der erste Schritt besteht darin, das Lucene.NET-Nuget-Paket aus Ihrer Bibliothek durch zu ersetzen NCache's Nuget-Paket. NCache Verzeichnis ist, wie der Name schon sagt, eine Basisklasse zum Speichern der Indizes, um die Indizes skalierbar zu machen. Lucene.NET verfügt zu diesem Zweck über mehrere Verzeichnisimplementierungen, aber die am häufigsten verwendete ist FSDirectory. Der erste Schritt besteht also darin, eine Verbindung mit dem herzustellen NCache Verzeichnis.
Unten ist der Code, der Sie mit einem Cache namens verbindet „luceneCache“ und öffnet das bereitgestellte Verzeichnis auf allen vorhandenen Servern, sofern dieses Verzeichnis bereits existiert. Andernfalls wird nur ein neues Verzeichnis mit dem angegebenen Namen erstellt.
1 |
var indexDirectory = NCacheDirectory.Open("luceneCache", new DirectoryInfo("lucene-index-path")); |
Mit diesem, NCache erfordert einen weiteren kleinen Schritt, um Distributed Lucene zu verwenden. Aktivieren Sie mit einem beliebigen Verwaltungstool den Lucene-Index für den Cache mit Distributed Lucene. Bitte beziehen Sie sich auf fehlen uns die Worte. Kapitel in NCache Dokumentation für Richtlinien dazu.
Lucene einrichten Verteiltes Lucene für Enterprise Search Verteilte Lucene-Dokumente
Schritt 2: Erstellen verteilter Lucene-Indizes mit NCache
Nun NCache in Ihre Lucene-Anwendung integriert ist, beginnt die Datenschreibphase. Wie bereits erwähnt, indiziert Lucene Ihre Datensätze in Form von Indizes. Diese Indizes werden auf den Knoten separat verwaltet, wodurch eine lineare Skalierbarkeit bereitgestellt wird. Die Verteilung der Dokumente auf die Cache-Knoten erfolgt automatisch durch NCache.
Dokumente werden aus Feldern gebildet, die Schlüssel-Wert-Paare sind. Jedes Feld enthält den Text, der von Ihnen durchsuchbar gemacht werden soll. Die anderen Teile des Feldkonstruktors enthalten Anweisungen zur Behandlung eines einzelnen Felds.
Diese Dokumente werden in der gespeichert NCache Verzeichnis in Form von invertierten Lucene-Indizes. Lucene zerlegt den Text in Form von Tokens. Dieser Prozess des Aufschlüsselns von Texten für die Indizierung erfolgt mithilfe von Analysetools. Es gibt mehrere Arten von Analysatoren wie z Whitespace-Analysatoren or Standard-Analysatoren. Diese Tokenisierung von Daten hilft bei der schnelleren Datensuche, da der Hauptzweck von Analysatoren darin besteht, die Füllwörter zu entfernen und den Rest der Daten zu indizieren, auf denen die Suche durchgeführt wird. Umfassende Informationen zu Analysatoren finden Sie auf dieser Seite.
Dokumente werden im Verzeichnis mit IndexWriter indiziert. IndexWriter ist für die Ausführung aller Schreibvorgänge im Cache verantwortlich. Genauer gesagt können Sie weitere Feldeigenschaften hinzufügen, um die Suche effizienter zu gestalten, z. B. FieldStore. Der folgende Code erklärt ausführlich das Schreiben von Dokumenten (als Bulk) in den Cache und als Ergebnis das Erstellen von Indizes auf allen Cache-Servern. Bitte achten Sie darauf, Commit nach jedem Schreibvorgang aufzurufen, da sonst der Vorgang nicht für die Suche aufgezeichnet wird.
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 |
public int AddDocuments () { var analyzer = new StandardAnalyzer(LuceneVersion.LUCENE_48); var writerConfig = new IndexWriterConfig (LuceneVersion.LUCENE_48, analyzer); var indexWriter = new IndexWriter (indexDirectory, writerConfig); var docCount = 0; // Fetching repository info from data source using (var enumerator = dataProvider.GetProductFTSEnumerator ()) { while (enumerator.MoveNext ()) { docs.Add (enumerator.Current.GetLuceneDocument ()); docCount++; // Perform commit after the chunk of code if (docs.Count == BULK_SIZE) { indexWriter.AddDocuments (docs); //Flush and make it ready for search indexWriter.Commit (); docs.Clear (); //Remove the added documents } } } // Return the count of added documents return docCount; } |
NCache Details Funktionsweise von Distributed Lucene GeoSpatial-Indizes für verteiltes Lucene
Schritt 3: Volltextsuche in den Daten mit Skalierbarkeit
Die eigentliche Aufgabe, Daten im Cache mit verteiltem Lucene zu durchsuchen, kommt in diesem Schritt. Ähnlich wie bei der Datenindizierung werden die Lesevorgänge auch auf allen Knoten des Caches durchgeführt und die Suchergebnisse schließlich zusammengeführt. Dies erhöht auch die Leseskalierbarkeit und macht die Suche schneller.
Ähnlich wie bei IndexWriter in Lucene gibt es eine IndexSucher das erledigt die ganze schwere Arbeit zum Durchführen der Datenleseoperationen basierend auf der Suche. IndexSearcher besteht einen Analysator, und es wird dringend empfohlen, denselben Analysator für die Suche zu verwenden, der zuvor zum Schreiben der Daten verwendet wurde, da sonst die Ergebnisse inkonsistent sind.
Das nächste, was Lucene zum Durchführen von Suchen verwendet, ist die Abfragen. Es gibt eine große Auswahl an integrierten Abfrageklassen in Lucene gemäß Ihren Anforderungen zusammen mit einer Lucene-spezifischen Syntax. Diese Abfragen machen Ihre Suche sehr effizient, beispielsweise gibt es eine Wildcard-Abfrage, die eine Suche mit Wildcards durchführt und die Suchergebnisse entsprechend rendert. Einige andere Abfragetypen sind TermQuery, BooleanQuery und SpanQuery.
Die Suchergebnisse werden in Form von Treffern zurückgegeben, bei denen es sich um eine Liste von Dokumenten handelt, die als Ergebnis der durchgeführten Suche zurückgegeben werden. Diese Treffer können dann iteriert werden, um das tatsächliche Dokument zu erhalten, das der Abfrage entspricht.
Der unten bereitgestellte Code hilft Ihnen dabei, die in den Lucene-Indizes durchgeführte Suche zu beobachten. IndexSearcher verwendet den IndexReader zum Abrufen der Ergebnisse. Eine Abfrage wird verwendet, um die eigentliche Suche nach den indizierten Daten durchzuführen. Ein QueryParser analysiert die vom Benutzer bereitgestellte Textabfrage gemäß dem Analysator und den Suchbegriffen, und die Ergebnisse werden in Form von TopDocs zurückgegeben.
Im unten angegebenen Beispiel wird ein QueryWrapperFilter auf die Produktkategorie angewendet und die Suchergebnisse werden nach Relevanz sortiert. Die Abfrage wendet Unschärfe auf alle Begriffe an, um optimale Ergebnisse zu liefern.
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 |
public Tuple<long, List<RepositoryInfo>> Search (string searchTerm, int top = 100, string category = null) { long totalHints = 0; var repoList = new List<ProductFTS> (); // Now create the IndexSearcher instance to search the data // directoryReader is the IndexReader instance which uses the IndexWriter var searcher = new IndexSearcher (directoryReader); try { TopDocs topDocs = null; var queryParser = new MultiFieldQueryParser (Version, Fields, analyzer); var query = new BooleanQuery (); // Split the search term into multiple search words to make fuzzy query string[] terms = searchTerm.Split (new [] { " " }, StringSplitOptions.RemoveEmptyEntries); foreach (string term in terms) query.Add (queryParser.Parse (term.Replace ("~", "") + "~"), Occur.MUST); // remove the duplicate ~, if already exits if (!string.IsNullOrEmpty (category)) { var filter = new QueryWrapperFilter (new TermQuery (new Term (CATEGORY_FIELD, category))); // Performs the search based on the filter applied topDocs = searcher.Search (query, filter, top, Sort.RELEVANCE); } else { topDocs = searcher.Search (query, top, sort : Sort.RELEVANCE); } totalHits = topDocs.TotalHits; repoList = GetSearchedDocs (searcher, topDocs); } return new Tuple<long, List<ProductFTS>> (totalHits, repoList); } |
Anschließend können Sie Ihre Geschäftslogik implementieren, um die Suchergebnisse aus den Treffern zu iterieren.
Zusammenfassung
Lucene ist eine hocheffiziente Suchmaschine für die Volltextsuche in Ihren Daten, aber es mangelt ihr an Skalierbarkeit. NCache kann mit Lucene verwendet werden, um es mit sehr wenig Aufwand skalierbar zu machen. Skalierbares verteiltes Lucene macht Ihre Anwendung nicht nur schneller, sondern hilft Ihnen auch, mit dem großen Rückschlag des Single Point of Failure fertig zu werden. NCache kann mit einer Codeänderung in einer einzigen Zeile einfach in Ihre .NET-Anwendung integriert werden, betrachten Sie es also als die bestmögliche Option für Ihre skalierbare Lucene-Anwendung.