Bevezetés az Apache Lucene-be
1. Áttekintés
Az Apache Lucene egy teljes szövegű keresőmotor, amely különböző programozási nyelvekről használható.
Ebben a cikkben megpróbáljuk megérteni a könyvtár alapfogalmait és létrehozni egy egyszerű alkalmazást.
2. Maven Setup
A kezdéshez először adjunk hozzá szükséges függőségeket:
org.apache.lucene lucene-core 7.1.0
A legújabb verzió itt található.
A keresési lekérdezések elemzéséhez a következőkre is szükségünk lesz:
org.apache.lucene lucene-queryparser 7.1.0
Itt tekintheti meg a legújabb verziót.
3. Alapfogalmak
3.1. Indexelés
Egyszerűen fogalmazva, a Lucene az adatok „fordított indexelését” használja - az oldalak kulcsszavakhoz való hozzárendelése helyett a kulcsszavakat oldalakhoz térképezi fel akárcsak egy szójegyzék minden könyv végén.
Ez gyorsabb keresési válaszokat tesz lehetővé, mivel az indexen keresztül keres, ahelyett, hogy közvetlenül a szövegben keresne.
3.2. Dokumentumok
Itt egy dokumentum mezők gyűjteménye, és minden mezőhöz tartozik egy érték.
Az indexek általában egy vagy több dokumentumból állnak, a keresési eredmények pedig a legjobban illeszkedő dokumentumok halmazai.
Nem mindig egyszerű szöveges dokumentum, lehet adatbázis tábla vagy gyűjtemény is.
3.3. Mezők
A dokumentumok mezőadatokkal rendelkezhetnek, ahol a mező tipikusan egy adatértéket tartalmazó kulcs:
cím: A tea testének jósága: A gyógyteás ital jóságának megvitatása ...
Figyelje meg itt cím és test mezők, és együtt vagy külön is kereshetők.
3.4. Elemzés
Egy elemzés az adott szöveget kisebb és pontos egységekké konvertálja a keresés megkönnyítése érdekében.
A szöveg különféle műveleteken megy keresztül a kulcsszavak kinyerésén, a gyakori szavak és írásjelek eltávolításán, a szavak kisbetűvé változtatásán stb.
Erre a célra több beépített analizátor létezik:
- StandardAnalyzer - elemzés az alapnyelvtan alapján, eltávolítja a leállítási szavakat, például „a”, „an” stb.
- SimpleAnalyzer - betű nélküli karakter alapján megtöri a szöveget, és kisbetűvé konvertálja
- WhiteSpaceAnalyzer - megtöri a szöveget a szóközök alapján
További elemzők állnak rendelkezésre számunkra, hogy felhasználhassuk és testre szabhassuk.
3.5. Keresés
Az index felépítése után az a segítségével kereshetünk az indexben Lekérdezés és egy IndexSearcher. A keresési eredmény általában egy eredményhalmaz, amely tartalmazza a visszakeresett adatokat.
Vegye figyelembe, hogy egy IndexWritter felelős az index és egy IndexSearcher az index kereséséhez.
3.6. Lekérdezés szintaxisa
A Lucene nagyon dinamikus és könnyen írható lekérdezési szintaxist biztosít.
Szabad szöveg keresésére csak egy szöveget használunk Húr mint a lekérdezés.
Szöveg kereséséhez egy adott mezőben a következőket használjuk:
mezőNév: szöveg pl .: cím: tea
Tartomány keresés:
időbélyeg: [1509909322,1572981321]
Helyettesítő karakterekkel is kereshetünk:
ital
egyetlen karaktert keresne a helyettesítő karakter helyett?
d * k
„d” betűvel kezdődő és „k” betűvel végződő szavakra keres, amelyek között több karakter van.
uni *
megtalálja az „uni” kezdetű szavakat.
Kombinálhatjuk ezeket a lekérdezéseket, és összetettebb lekérdezéseket hozhatunk létre. És tartalmazzon olyan logikai operátort, mint az AND, NOT, OR:
cím: "Tea a reggeliben" ÉS "kávé"
További információ a lekérdezés szintaxisáról itt.
4. Egyszerű alkalmazás
Hozzunk létre egy egyszerű alkalmazást, és indexeljünk néhány dokumentumot.
Először létrehozunk egy memóriában lévő indexet, és hozzáadunk néhány dokumentumot:
... Directory memoryIndex = new RAMDirectory (); StandardAnalyzer analizátor = new StandardAnalyzer (); IndexWriterConfig indexWriterConfig = új IndexWriterConfig (elemző); IndexWriter író = új IndexWriter (memoryIndex, indexWriterConfig); Dokumentumdokumentum = új Dokumentum (); document.add (új TextField ("cím", cím, Field.Store.YES)); document.add (new TextField ("body", body, Field.Store.YES)); writter.addDocument (dokumentum); író.zár ();
Itt létrehozunk egy dokumentumot a TextField és adja hozzá őket az indexhez a IndexWriter. A harmadik érv a TextField A konstruktor jelzi, hogy a mező értékét is tárolni kell-e vagy sem.
Az elemzőket az adatok vagy a szöveg darabokra osztására használják, majd kiszűrik belőlük a leállítási szavakat. A stop szavak olyan szavak, mint „a”, „vagyok”, „van” stb. Ezek teljesen függenek az adott nyelvtől.
Ezután hozzunk létre egy keresési lekérdezést, és keressük meg a hozzáadott dokumentum indexét:
public List searchIndex (String inField, String queryString) {Lekérdezés lekérdezése = new QueryParser (inField, analizátor) .parse (queryString); IndexReader indexReader = DirectoryReader.open (memoryIndex); IndexSearcher kereső = new IndexSearcher (indexReader); TopDocs topDocs = kereső.keresés (lekérdezés, 10); Dokumentumlista = new ArrayList (); for (ScoreDoc scoreDoc: topDocs.scoreDocs) {documents.add (kereső.doc (scoreDoc.doc)); } visszaküldési dokumentumok; }
Ban,-ben keresés() metódus, a második egész argumentum azt jelzi, hogy hány felső keresési eredménynek kell visszatérnie.
Most teszteljük:
@Test public void givenSearchQueryWhenFetchedDocumentThenCorrect () {InMemoryLuceneIndex inMemoryLuceneIndex = new InMemoryLuceneIndex (új RAMDirectory (), új StandardAnalyzer ()); inMemoryLuceneIndex.indexDocument ("Hello world", "Some hello world"); Listázza a dokumentumokat = inMemoryLuceneIndex.searchIndex ("body", "world"); assertEquals ("Hello world", documents.get (0) .get ("title")); }
Itt egy egyszerű dokumentumot adunk az indexhez, két mezővel: „title” és „body”, majd megpróbálunk ugyanabban keresni egy keresési lekérdezés segítségével.
6. Lucene lekérdezések
Mivel most már jól érezzük magunkat az indexelés és a keresés alapjaiban, ássunk egy kicsit mélyebbre.
Korábbi szakaszokban láthattuk az alapvető lekérdezési szintaxist, és annak átalakítását a-vá Lekérdezés például a QueryParser.
A Lucene különféle konkrét megvalósításokat is biztosít:
6.1. TermQuery
A Term a keresés alapegysége, amely tartalmazza a mező nevét és a keresendő szöveget.
TermQuery a legegyszerűbb egyetlen kifejezésből álló lekérdezés:
@Test public void givenTermQueryWhenFetchedDocumentThenCorrect () {InMemoryLuceneIndex inMemoryLuceneIndex = new InMemoryLuceneIndex (új RAMDirectory (), új StandardAnalyzer ()); inMemoryLuceneIndex.indexDocument ("tevékenység", "nyomon fut"); inMemoryLuceneIndex.indexDocument ("tevékenység", "Az autók úton vannak"); Term kifejezés = új kifejezés ("test", "fut"); Lekérdezés = új TermQuery (kifejezés); Listázza a dokumentumokat = inMemoryLuceneIndex.searchIndex (lekérdezés); assertEquals (2, dokumentumok.méret ()); }
6.2. PrefixQuery
Dokumentum keresése „kezdőbetűvel” szóval:
@Test public void givenPrefixQueryWhenFetchedDocumentThenCorrect () {InMemoryLuceneIndex inMemoryLuceneIndex = new InMemoryLuceneIndex (új RAMDirectory (), új StandardAnalyzer ()); inMemoryLuceneIndex.indexDocument ("cikk", "Lucene bevezetés"); inMemoryLuceneIndex.indexDocument ("cikk", "Bevezetés a Lucene-be"); Term kifejezés = új kifejezés ("body", "intro"); Lekérdezés = new PrefixQuery (kifejezés); Dokumentumok felsorolása = inMemoryLuceneIndex.searchIndex (lekérdezés); assertEquals (2, dokumentumok.méret ()); }
6.3. WildcardQuery
Ahogy a neve is sugallja, használhatunk helyettesítő karaktereket „*” vagy „?” kereséshez:
// ... Term kifejezés = új kifejezés ("body", "intro *"); Lekérdezés = new WildcardQuery (kifejezés); // ...
6.4. PhraseQuery
A dokumentum szövegsorainak keresésére szolgál:
// ... inMemoryLuceneIndex.indexDocument ("idézetek", "Bármely más nevű rózsa édes illatú lenne."); Lekérdezés = new PhraseQuery (1, "test", új BytesRef ("szag"), új BytesRef ("édes")); Dokumentumok felsorolása = inMemoryLuceneIndex.searchIndex (lekérdezés); // ...
Figyeljük meg, hogy a PhraseQuery konstruktort nevezzük tócsa, amely a szavak közötti távolság az egyeztetendő kifejezések között.
6.5. FuzzyQuery
Ezt akkor használhatjuk, amikor valami hasonló, de nem feltétlenül azonos keresést keresünk:
// ... inMemoryLuceneIndex.indexDocument ("cikk", "Halloween fesztivál"); inMemoryLuceneIndex.indexDocument ("dekoráció", "Halloween-dekorációk"); Term kifejezés = új kifejezés ("test", "hallowen"); Lekérdezés = new FuzzyQuery (kifejezés); Dokumentumok felsorolása = inMemoryLuceneIndex.searchIndex (lekérdezés); // ...
Megpróbáltuk keresni a „Halloween” szöveget, de hibásan írt „hallowen” -nel.
6.6. BooleanQuery
Előfordulhat, hogy összetett kereséseket kell végrehajtanunk, két vagy több különböző típusú lekérdezést kombinálva:
// ... inMemoryLuceneIndex.indexDocument ("Cél", "Las Vegas szingapúri autó"); inMemoryLuceneIndex.indexDocument ("Ingázás Szingapúrban", "Autóbuszok"); Term kifejezés1 = új kifejezés ("test", "szingapúr"); Term kifejezés2 = új kifejezés ("karosszéria", "autó"); TermQuery query1 = új TermQuery (term1); TermQuery query2 = új TermQuery (term2); BooleanQuery booleanQuery = new BooleanQuery.Builder () .add (query1, BooleanClause.Occur.MUST) .add (query2, BooleanClause.Occur.MUST) .build (); // ...
7. A keresési eredmények rendezése
Bizonyos mezők alapján is rendezhetjük a keresési eredményeket tartalmazó dokumentumokat:
@Test public void givenSortFieldWhenSortedThenCorrect () {InMemoryLuceneIndex inMemoryLuceneIndex = new InMemoryLuceneIndex (új RAMDirectory (), új StandardAnalyzer ()); inMemoryLuceneIndex.indexDocument ("Gangesz", "Indiai folyó"); inMemoryLuceneIndex.indexDocument ("Mekong", "Ez a folyó Dél-Ázsiában folyik"); inMemoryLuceneIndex.indexDocument ("Amazon", "Esőerdő folyó"); inMemoryLuceneIndex.indexDocument ("Rajna", "Európához tartozik"); inMemoryLuceneIndex.indexDocument ("Nílus", "Leghosszabb folyó"); Term kifejezés = új kifejezés ("test", "folyó"); Lekérdezés = new WildcardQuery (kifejezés); SortField sortField = új SortField ("cím", SortField.Type.STRING_VAL, hamis); Rendezés sortByTitle = új Rendezés (sortField); Listázza a dokumentumokat = inMemoryLuceneIndex.searchIndex (lekérdezés, sortByTitle); assertEquals (4, dokumentumok.méret ()); assertEquals ("Amazon", documents.get (0) .getField ("title"). stringValue ()); }
Megpróbáltuk a behúzott dokumentumokat címmezők szerint rendezni, amelyek a folyók neve. A logikai argumentum a SortField konstruktor a rendezési sorrend megfordítására szolgál.
8. Távolítsa el a dokumentumokat az indexből
Próbáljunk meg eltávolítani néhány dokumentumot az indexből egy adott alapján Időtartam:
// ... IndexWriterConfig indexWriterConfig = new IndexWriterConfig (analizátor); IndexWriter író = new IndexWriter (memoryIndex, indexWriterConfig); író.törliDokumentumok (kifejezés); // ...
Ezt teszteljük:
@Test public void whenDocumentDeletedThenCorrect () {InMemoryLuceneIndex inMemoryLuceneIndex = new InMemoryLuceneIndex (új RAMDirectory (), új StandardAnalyzer ()); inMemoryLuceneIndex.indexDocument ("Gangesz", "Indiai folyó"); inMemoryLuceneIndex.indexDocument ("Mekong", "Ez a folyó Dél-Ázsiában folyik"); Term kifejezés = új kifejezés ("cím", "bandák"); inMemoryLuceneIndex.deleteDocument (kifejezés); Lekérdezés = új TermQuery (kifejezés); Dokumentumok felsorolása = inMemoryLuceneIndex.searchIndex (lekérdezés); assertEquals (0, dokumentumok.méret ()); }
9. Következtetés
Ez a cikk gyors bevezetést jelentett az Apache Lucene használatának megkezdéséhez. Ezenkívül különböző lekérdezéseket hajtottunk végre, és rendeztük a visszakeresett dokumentumokat.
Mint mindig, a példák kódja megtalálható a Github oldalon.