Lucene est un moteur de recherche efficace et puissant qui prend en charge la recherche de texte intégral dans .NET. Il facilite la recherche de texte et vous fournit un ensemble complet d'API, pour des techniques de recherche de texte rapides et conviviales.
De plus, il y a une chose que Lucene n'offre pas, c'est l'évolutivité. Les applications Lucene écrivent généralement des données sur un fichier et les stockent sur le disque, ce qui entraîne une importante allocation de mémoire. NCache fournit une Lucène distribué fonctionnalité qui rend vos applications Lucene évolutives et distribuées. Il fournit également une alternative linéairement évolutive et distribuée au problème d'un point de défaillance unique.
NCache Détails Fonctionnement du Lucène distribué Documents Lucene distribués
Pour plus d'informations sur Distributed Lucene, cela fonctionne et pourquoi cela devrait être votre option de choix, jetez un œil à ceci Lucene distribué : recherche en texte intégral dans .NET pour l'évolutivité. Avant d'entrer dans les détails de Distributed Lucene, parcourons une solution Distributed Lucene illustrant le flux de travail de base.
L'exemple de code ci-dessous montre un Lucene distribué qui effectue principalement ces trois étapes :
- Initialisez le NCache Annuaire.
- Indexez les documents pour effectuer une recherche sur ceux-ci.
- Effectuez une recherche sur les documents indexés.
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 } |
Dans ce blog, nous nous concentrons sur les étapes détaillées et le fonctionnement de Lucene distribué pour la recherche en texte intégral.
NCache Détails Fonctionnement du Lucène distribué Composants Lucene et aperçu
Comment migrer vers Lucene distribué ?
Lucene distribué fonctionne exactement comme Lucene. L'un des principaux avantages de l'utilisation de Lucene distribué est qu'il vous offre la même API que Lucene. En tant qu'utilisateur Lucene, vous obtenez l'évolutivité que vous souhaitez avec un module complémentaire d'un changement de code de doublure unique. Vous n'avez qu'à utiliser NCache Annuaire et votre application est prête à partir. Il y a très peu de changements de comportement et d'API dans Lucene distribué qui sont répertoriés dans la documentation ici.
Examinons de plus près ces étapes d'un point de vue technique.
Étape 1 : connexion à NCache Annuaire
Commençons par présenter NCache dans votre application .NET à l'aide de Lucene. La première étape consiste à remplacer le package Lucene.NET Nuget de votre bibliothèque par NCacheLe paquet Nuget. NCache Directory, comme son nom l'indique, est une classe de base pour stocker les index afin de rendre les index évolutifs. Lucene.NET a plusieurs implémentations de répertoires à cette fin, mais la plus couramment utilisée est FSDirectory. Ainsi, la première étape consiste à établir une connexion avec le NCache Annuaire.
Ci-dessous le code qui vous connecte à un cache appelé "LuceneCache" et ouvre le répertoire fourni sur tous les serveurs existants à condition que ce répertoire existe déjà. Sinon, il crée simplement un nouveau répertoire avec le nom fourni.
1 |
var indexDirectory = NCacheDirectory.Open("luceneCache", new DirectoryInfo("lucene-index-path")); |
Avec ça, NCache nécessite une autre petite étape pour utiliser Distributed Lucene. À l'aide de n'importe quel outil de gestion, activez l'index Lucene pour le cache à l'aide de Distributed Lucene. Prière de se référer à this chapitre dans NCache Documentation pour les directives sur la façon de le faire.
Configurer Lucène Lucene distribué pour la recherche d'entreprise Documents Lucene distribués
Étape 2 : Création d'index Lucene distribués avec NCache
Maintenant que NCache est intégré à votre application Lucene, vient la phase d'écriture des données. Comme indiqué précédemment, Lucene indexe vos enregistrements sous forme d'index. Ces index sont maintenus sur les nœuds séparément, offrant une évolutivité linéaire. La répartition des documents entre les nœuds de cache est automatiquement gérée par NCache.
Les documents sont formés à partir de champs qui sont des paires clé-valeur. Chaque champ contient le texte qui doit être rendu consultable par vous. Les autres parties du constructeur du champ contiennent des instructions pour gérer un champ individuel.
Ces documents sont stockés dans le NCache Annuaire sous forme d'index inversés Lucene. Lucene décompose le texte sous forme de jetons. Ce processus de décomposition des textes pour l'indexation est effectué à l'aide d'analyseurs. Il existe plusieurs types d'analyseurs tels que analyseurs d'espaces blancs or analyseurs standards. Cette tokenisation des données permet une recherche de données plus rapide car le but principal des analyseurs est de supprimer les mots parasites et d'indexer le reste des données sur lesquelles la recherche est effectuée. Pour une connaissance complète des analyseurs, veuillez vous référer à cette page.
Les documents sont indexés dans le répertoire à l'aide d'IndexWriter. IndexWriter est chargé d'effectuer toutes les opérations d'écriture sur le cache. Plus précisément, vous pouvez ajouter d'autres propriétés de champ pour rendre la recherche efficace, comme FieldStore. Le code ci-dessous explique en détail l'écriture de documents (en bloc) dans le cache et, par conséquent, la création d'index sur tous les serveurs de cache. Veuillez vous assurer d'appeler Commit après chaque opération d'écriture, sinon l'opération n'est pas enregistrée pour effectuer la recherche.
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 Détails Fonctionnement du Lucène distribué Index géospatiaux pour Lucene distribué
Étape 3 : Recherche de texte intégral dans les données avec évolutivité
La tâche réelle de recherche de données dans le cache à l'aide de Lucene distribué intervient à cette étape. Semblable à l'indexation des données, les lectures sont également effectuées sur tous les nœuds du cache et les résultats de la recherche sont éventuellement fusionnés. Cela augmente également l'évolutivité de la lecture et accélère la recherche.
Semblable à IndexWriter dans Lucene, il existe un Recherche d'index qui fait tout le gros du travail pour effectuer les opérations de lecture de données en fonction de la recherche. IndexSearcher passe un analyseur, et il est fortement recommandé d'utiliser le même analyseur pour la recherche, qui a été précédemment utilisé pour écrire les données sinon, les résultats sont incohérents.
La prochaine chose que Lucene utilise pour effectuer des recherches est le requêtes. Il existe un large éventail de classes de requêtes intégrées dans Lucene selon vos besoins, ainsi qu'une syntaxe spécifique à Lucene. Ces requêtes rendent votre recherche assez efficace, par exemple, il existe une requête générique qui effectue une recherche à l'aide de caractères génériques et affiche les résultats de la recherche en conséquence. Certains autres types de requêtes sont TermQuery, BooleanQuery et SpanQuery.
Les résultats de la recherche sont renvoyés sous la forme de résultats, c'est-à-dire une liste de documents renvoyés à la suite de la recherche effectuée. Ces hits peuvent ensuite être itérés pour obtenir le document réel correspondant à la requête.
Le code fourni ci-dessous va vous aider à assister à la recherche effectuée sur les index Lucene. IndexSearcher utilise IndexReader pour récupérer les résultats. Une requête est utilisée pour effectuer la recherche proprement dite sur les données indexées. Un QueryParser analyse la requête textuelle fournie par l'utilisateur en fonction de l'analyseur et des termes de recherche et les résultats sont renvoyés sous la forme de TopDocs.
Dans l'exemple ci-dessous, un QueryWrapperFilter est appliqué sur la catégorie Produits et les résultats de la recherche sont triés par pertinence. La requête applique un flou à tous les termes pour fournir des résultats optimaux.
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); } |
Vous pouvez ensuite implémenter votre logique métier pour itérer les résultats de recherche à partir des résultats.
Conclusion
Lucene est un moteur de recherche très efficace pour effectuer des recherches en texte intégral sur vos données, mais il manque d'évolutivité. NCache peut être utilisé avec Lucene pour le rendre évolutif avec très peu d'effort. Lucene distribué évolutif rend votre application non seulement plus rapide, mais vous aide également à faire face à l'inconvénient majeur du point de défaillance unique. NCache peut être facilement branché à votre application .NET avec un changement de code sur une seule ligne, alors considérez-le comme la meilleure option possible pour votre application Lucene évolutive.