Introduksjon til Apache Lucene

1. Oversikt

Apache Lucene er en søkemotor i fulltekst som kan brukes fra forskjellige programmeringsspråk.

I denne artikkelen vil vi prøve å forstå kjernekonseptene i biblioteket og lage en enkel applikasjon.

2. Maven-oppsett

For å komme i gang, la oss først legge til nødvendige avhengigheter:

 org.apache.lucene lucene-core 7.1.0 

Den siste versjonen finner du her.

For å analysere søkene våre trenger vi også:

 org.apache.lucene lucene-queryparser 7.1.0 

Se etter den nyeste versjonen her.

3. Kjernekonsepter

3.1. Indeksering

Enkelt sagt bruker Lucene en “invertert indeksering” av data - i stedet for å kartlegge sider til nøkkelord, tilordner det nøkkelord til sider akkurat som en ordliste på slutten av en hvilken som helst bok.

Dette gir raskere svar på søket, ettersom det søker gjennom en indeks, i stedet for å søke gjennom tekst direkte.

3.2. Dokumenter

Her er et dokument en samling felt, og hvert felt har en verdi knyttet til seg.

Indekser består vanligvis av ett eller flere dokumenter, og søkeresultatene er sett med best matchende dokumenter.

Det er ikke alltid et vanlig tekstdokument, det kan også være en databasetabell eller en samling.

3.3. Enger

Dokumenter kan ha feltdata, der et felt vanligvis er en nøkkel som inneholder en dataverdi:

tittel: Goodness of Tea body: Diskuterer godhet ved å drikke urtete ...

Legg merke til det her tittel og kropp er felt og kan søkes etter sammen eller hver for seg.

3.4. Analyse

En analyse konverterer den gitte teksten til mindre og presise enheter for å gjøre det enkelt å søke.

Teksten går gjennom ulike operasjoner for å trekke ut søkeord, fjerne vanlige ord og skilletegn, endre ord til små bokstaver, etc.

For dette formålet er det flere innebygde analysatorer:

  1. Standardanalysator - analyser basert på grunnleggende grammatikk, fjerner stoppord som “a”, “an” osv. Konverterer også med små bokstaver
  2. SimpleAnalyzer - bryter teksten basert på tegn uten bokstav og konverterer med små bokstaver
  3. WhiteSpaceAnalyzer - bryter teksten basert på hvite mellomrom

Det er flere analysatorer tilgjengelig for oss å bruke og tilpasse også.

3.5. Søker

Når en indeks er bygget, kan vi søke i den indeksen ved hjelp av a Spørsmål og en IndexSearcher. Søkeresultatet er vanligvis et resultatsett som inneholder de hentede dataene.

Merk at en IndexWritter er ansvarlig for å lage indeksen og en IndexSearcher for å søke i indeksen.

3.6. Spørringssyntaks

Lucene gir en veldig dynamisk og enkel å skrive syntaks.

For å søke i en fri tekst, bruker vi bare en tekst String som spørringen.

For å søke i en tekst i et bestemt felt, bruker vi:

feltnavn: tekst f.eks. tittel: te

Omfangssøk:

tidsstempel: [1509909322,1572981321] 

Vi kan også søke med jokertegn:

drikke

vil søke etter et enkelt tegn i stedet for jokertegnet “?”

d * k

søker etter ord som begynner med “d” og slutter med “k”, med flere tegn i mellom.

uni *

vil finne ord som begynner med “uni”.

Vi kan også kombinere disse spørsmålene og opprette mer komplekse spørsmål. Og inkluder logiske operatører som AND, NOT, OR:

tittel: "Te i frokosten" OG "kaffe"

Mer om spørringssyntaks her.

4. En enkel applikasjon

La oss lage et enkelt program og indeksere noen dokumenter.

Først oppretter vi en indeks i minnet og legger til noen dokumenter i den:

... KatalogminneIndex = ny RAMDirectory (); StandardAnalyzer analysator = ny StandardAnalyzer (); IndexWriterConfig indexWriterConfig = ny IndexWriterConfig (analysator); IndexWriter writter = ny IndexWriter (memoryIndex, indexWriterConfig); Dokumentdokument = nytt dokument (); document.add (ny TextField ("tittel", tittel, Field.Store.YES)); document.add (ny TextField ("body", body, Field.Store.YES)); writter.addDocument (dokument); writter.close (); 

Her lager vi et dokument med Tekstfelt og legg dem til indeksen ved hjelp av IndexWriter. Det tredje argumentet i Tekstfelt konstruktør indikerer om verdien av feltet også skal lagres eller ikke.

Analysatorer brukes til å dele dataene eller teksten i biter, og deretter filtrere ut stoppordene fra dem. Stoppord er ord som 'a', 'am', 'is' osv. Disse er helt avhengige av det gitte språket.

La oss deretter opprette et søk og søke i indeksen etter dokumentet som er lagt til:

public List searchIndex (String inField, String queryString) {Query query = new QueryParser (inField, analyzer) .parse (queryString); IndexReader indexReader = DirectoryReader.open (memoryIndex); IndexSearcher searcher = ny IndexSearcher (indexReader); TopDocs topDocs = searcher.search (spørring, 10); Liste dokumenter = ny ArrayList (); for (ScoreDoc scoreDoc: topDocs.scoreDocs) {document.add (searcher.doc (scoreDoc.doc)); } returnere dokumenter; }

I Søk() metode det andre heltallargumentet indikerer hvor mange topp søkeresultater det skal returnere.

La oss nå teste det:

@Test offentlig ugyldig gittSearchQueryWhenFetchedDocumentThenCorrect () {InMemoryLuceneIndex inMemoryLuceneIndex = new InMemoryLuceneIndex (new RAMDirectory (), new StandardAnalyzer ()); inMemoryLuceneIndex.indexDocument ("Hello world", "Some hallo world"); Liste dokumenter = inMemoryLuceneIndex.searchIndex ("body", "world"); assertEquals ("Hello world", documents.get (0) .get ("title")); }

Her legger vi til et enkelt dokument i indeksen, med to felt 'title' og 'body', og prøver deretter å søke det samme ved hjelp av et søk.

6. Lucene-spørsmål

Siden vi nå er komfortable med det grunnleggende om indeksering og søk, la oss grave litt dypere.

I tidligere seksjoner har vi sett den grunnleggende spørringssyntaksen, og hvordan du konverterer den til en Spørsmål eksempel bruker QueryParser.

Lucene tilbyr også forskjellige konkrete implementeringer:

6.1. TermQuery

EN Begrep er en grunnleggende enhet for søk, som inneholder feltnavnet sammen med teksten det skal søkes etter.

TermQuery er den enkleste av alle spørsmål som består av et enkelt begrep:

@Test public void givenTermQueryWhenFetchedDocumentThenCorrect () {InMemoryLuceneIndex inMemoryLuceneIndex = new InMemoryLuceneIndex (new RAMDirectory (), new StandardAnalyzer ()); inMemoryLuceneIndex.indexDocument ("aktivitet", "kjører i sporet"); inMemoryLuceneIndex.indexDocument ("aktivitet", "Biler kjører på veien"); Begrepsterm = nytt begrep ("kropp", "løpende"); Query query = new TermQuery (term); Liste dokumenter = inMemoryLuceneIndex.searchIndex (spørring); assertEquals (2, documents.size ()); }

6.2. PrefixQuery

Slik søker du i et dokument med et "starter med" ord:

@Test public void givenPrefixQueryWhenFetchedDocumentThenCorrect () {InMemoryLuceneIndex inMemoryLuceneIndex = new InMemoryLuceneIndex (new RAMDirectory (), new StandardAnalyzer ()); inMemoryLuceneIndex.indexDocument ("artikkel", "Lucene introduksjon"); inMemoryLuceneIndex.indexDocument ("artikkel", "Introduksjon til Lucene"); Begrepsterm = nytt begrep ("kropp", "intro"); Query query = new PrefixQuery (term); Liste dokumenter = inMemoryLuceneIndex.searchIndex (spørring); assertEquals (2, documents.size ()); }

6.3. WildcardQuery

Som navnet antyder, kan vi bruke jokertegn “*” eller “?” for å søke:

// ... Term term = new Term ("body", "intro *"); Query query = new WildcardQuery (term); // ...

6.4. PhraseQuery

Den brukes til å søke i en sekvens av tekster i et dokument:

// ... inMemoryLuceneIndex.indexDocument ("sitater", "En rose med noe annet navn vil lukte så søt."); Query query = new PhraseQuery (1, "body", new BytesRef ("smell"), new BytesRef ("sweet")); Liste dokumenter = inMemoryLuceneIndex.searchIndex (spørring); // ...

Legg merke til at det første argumentet til PhraseQuery konstruktør kalles slop, som er avstanden i antall ord, mellom ordene som skal matches.

6.5. FuzzyQuery

Vi kan bruke dette når vi søker etter noe lignende, men ikke nødvendigvis identisk:

// ... inMemoryLuceneIndex.indexDocument ("artikkel", "Halloween-festival"); inMemoryLuceneIndex.indexDocument ("dekorasjon", "Dekorasjoner til Halloween"); Begrepsterm = nytt begrep ("kropp", "hallowen"); Query query = new FuzzyQuery (term); Liste dokumenter = inMemoryLuceneIndex.searchIndex (spørring); // ...

Vi prøvde å søke etter teksten “Halloween”, men med feilstavet “hallowen”.

6.6. BooleanQuery

Noen ganger kan det hende vi trenger å utføre komplekse søk ved å kombinere to eller flere forskjellige spørsmål:

// ... inMemoryLuceneIndex.indexDocument ("Destination", "Las Vegas singapore car"); inMemoryLuceneIndex.indexDocument ("Pendler i singapore", "Bus Car Bikes"); Begrep term1 = nytt begrep ("kropp", "singapore"); Term term2 = ny Term ("kropp", "bil"); TermQuery query1 = ny TermQuery (term1); TermQuery query2 = ny TermQuery (term2); BooleanQuery booleanQuery = ny BooleanQuery.Builder () .add (query1, BooleanClause.Occur.MUST) .add (query2, BooleanClause.Occur.MUST) .build (); // ...

7. Sortering av søkeresultater

Vi kan også sortere søkeresultatdokumentene basert på visse felt:

@Test offentlig ugyldighet givenSortFieldWhenSortedThenCorrect () {InMemoryLuceneIndex inMemoryLuceneIndex = new InMemoryLuceneIndex (new RAMDirectory (), new StandardAnalyzer ()); inMemoryLuceneIndex.indexDocument ("Ganges", "River in India"); inMemoryLuceneIndex.indexDocument ("Mekong", "Denne elven renner i Sør-Asia"); inMemoryLuceneIndex.indexDocument ("Amazon", "Rain forest river"); inMemoryLuceneIndex.indexDocument ("Rhinen", "Tilhører Europa"); inMemoryLuceneIndex.indexDocument ("Nilen", "Lengste elv"); Begrepsterm = nytt begrep ("kropp", "elv"); Query query = new WildcardQuery (term); SortField sortField = new SortField ("title", SortField.Type.STRING_VAL, false); Sort sortByTitle = new Sort (sortField); Liste dokumenter = inMemoryLuceneIndex.searchIndex (spørring, sortByTitle); assertEquals (4, documents.size ()); assertEquals ("Amazon", documents.get (0) .getField ("title"). stringValue ()); }

Vi prøvde å sortere de hentede dokumentene etter tittelfelt, som er navnene på elvene. Det boolske argumentet til SortField konstruktør er for å reversere sorteringsrekkefølgen.

8. Fjern dokumenter fra indeksen

La oss prøve å fjerne noen dokumenter fra indeksen basert på en gitt Begrep:

// ... IndexWriterConfig indexWriterConfig = ny IndexWriterConfig (analysator); IndexWriter-forfatter = ny IndexWriter (memoryIndex, indexWriterConfig); writer.deleteDocuments (begrep); // ...

Vi tester dette:

@Test offentlig ugyldig nårDocumentDeletedThenCorrect () {InMemoryLuceneIndex inMemoryLuceneIndex = new InMemoryLuceneIndex (new RAMDirectory (), new StandardAnalyzer ()); inMemoryLuceneIndex.indexDocument ("Ganges", "River in India"); inMemoryLuceneIndex.indexDocument ("Mekong", "Denne elven renner i Sør-Asia"); Begrepsterm = nytt begrep ("tittel", "ganges"); inMemoryLuceneIndex.deleteDocument (begrep); Query query = new TermQuery (term); Liste dokumenter = inMemoryLuceneIndex.searchIndex (spørring); assertEquals (0, documents.size ()); }

9. Konklusjon

Denne artikkelen var en rask introduksjon til å komme i gang med Apache Lucene. Vi utførte også ulike spørsmål og sorterte de hentede dokumentene.

Som alltid kan koden for eksemplene finnes på Github.


$config[zx-auto] not found$config[zx-overlay] not found