[Update] Zugriff auf Wörter rund um eine Positionsübereinstimmung in Lucene
Vor einiger Zeit habe ich einen Artikel darüber geschrieben, wie man in Lucene auf Wörter rund um eine Positionsübereinstimmung zugreifen…
Vor einiger Zeit habe ich einen Artikel darüber geschrieben, wie man in Lucene auf Wörter rund um eine Positionsübereinstimmung zugreifen kann. Ein Freund fragte mich, wie man in Lucene 4 ähnliche Dinge tun kann. Da Lucene 4 eine Menge API-Änderungen mit sich bringt, dachte ich, dass es sich lohnen würde, das Beispiel für die neuen APIs zu aktualisieren. Ich bleibe zwar dem Geist meines ursprünglichen Beitrags treu, aber beachten Sie, dass es in Lucene 4 möglicherweise andere Möglichkeiten gibt, dies zu tun (ein Beitrag für einen anderen Tag), da es in Lucene 4 viel einfacher ist, Dinge wie Offsets, Payloads und andere Dinge direkt in der Posting-Liste zu speichern und so die teureren Term-Vektoren zu vermeiden, die hier verwendet werden. Ein Beispiel dafür finden Sie auf der neuen Seite PostingsHighlighter
. (Ich habe den Leistungsunterschied, wenn überhaupt, nicht gemessen).
Um es kurz zu machen: Der Code, den ich Ihnen jetzt zeige, hat das Ziel, ein Fenster mit Begriffen rund um einen bestimmten Suchbegriff (in diesem Fall „Fleece“) zu erstellen, um eine nachgelagerte Analyse dieses Fensters durchzuführen. Hier ist der grundlegende Code für die Erstellung der Dokumente, die ich verwende:
[code language=“java“]
public static String[] DOCS = {
„Der flinke Rotfuchs sprang über die faulen braunen Hunde.“,
„Maria hatte ein kleines Lamm, dessen Fell weiß wie Schnee war.“,
„Moby Dick ist eine Geschichte über einen Wal und einen besessenen Mann.“,
„Der Räuber trug eine schwarze Fleecejacke und eine Baseballmütze.“,
„Der englische Springer Spaniel ist der beste aller Hunde.“,
„Das Vlies war grün und rot“,
„Die Geschichte des goldenen Vlieses ist in der Geschichte sehr beliebt, aber die meisten Menschen sind anderer Meinung.“
};
[/code]
Bei der Aktualisierung meines Codes für Lucene 4 (speziell 4.3) haben die wichtigsten Unterschiede zu den vorherigen Beispielen mit der Verwendung einer AtomicReader-Instanz zu tun (weitere Einzelheiten finden Sie in Uwe Schindlers ausgezeichnetem Vortrag zu diesem Thema ). Außerdem habe ich einige neue Parameter an die Methode SpanTermQuery.getSpans() übergeben und schließlich erklärt, wie man auf die Termvektoren zugreift. Hier sind die relevanten Teile für die Erstellung des Spans-Objekts (der vollständige Code befindet sich unten) und ich werde später auf die Termvektoren eingehen:
[code language=“java“]
SpanTermQuery fleeceQ = new SpanTermQuery(new Term(„content“, „fleece“));
…
IndexReader reader = searcher.getIndexReader();
//Dies ist nicht die beste Methode, aber für das Beispiel funktioniert sie. Siehe http://www.slideshare.net/lucenerevolution/is-your-index-reader-really-atomic-or-maybe-slow für Ansätze mit höherer Leistung
AtomicReader wrapper = SlowCompositeReaderWrapper.wrap(reader);
Map<Term, TermContext> termContexts = new HashMap<Term, TermContext>();
Spans spans = fleeceQ.getSpans(wrapper.getContext(), new Bits.MatchAllBits(reader.numDocs()), termContexts);
[/code]
In der Methode getSpans() ermöglicht der erste Parameter im Wesentlichen den Zugriff auf den Reader, der zweite Parameter kann verwendet werden, um Dokumente herauszufiltern, und der dritte Parameter, die termContexts, kann verwendet werden, um eine bessere Leistung bei der Suche nach Begriffen zu ermöglichen.
Die andere große Änderung ist der Zugriff auf Termvektoren in der Suchschleife für die Spanne. Sie brauchen keine TermVectorMapper-Instanzen mehr zu verwenden, sondern nur noch Instanzen von Terms, TermsEnum und DocsAndPositionsEnum, wie in:
[code language=“java“]
Begriffe content = reader.getTermVector(spans.doc(), „content“);
TermsEnum termsEnum = content.iterator(null);
BytesRef Begriff;
while ((term = termsEnum.next()) != null) {
//könnte die BytesRef hier speichern, aber String ist für dieses Beispiel einfacher
String s = new String(term.bytes, term.offset, term.length);
DocsAndPositionsEnum positionsEnum = termsEnum.docsAndPositions(null, null);
if (positionsEnum.nextDoc() != DocIdSetIterator.NO_MORE_DOCS) {
int i = 0;
int Position = -1;
while (i < positionsEnum.freq() && (position = positionsEnum.nextPosition()) != -1) {
if (Position >= Start && Position <= Ende) {
entries.put(position, s);
}
i++;
}
}
}
[/code]
Wenn Sie diesen Code ausführen, sollte das Ergebnis sein:
Score Doc: doc=5 score=0.4725143 shardIndex=-1 Score Doc: doc=3 score=0.35438573 shardIndex=-1 Score Doc: doc=1 score=0.29532143 shardIndex=-1 Score Doc: doc=6 score=0.23625715 shardIndex=-1 Doc: 1 Start: 6 End: 7 Entries:{4=lamb, 5=whose, 6=fleece, 8=white} Doc: 3 Start: 5 End: 6 Entries:{4=black, 5=fleece, 6=jacket} Doc: 5 Start: 1 End: 2 Entries:{1=fleece, 3=green} Doc: 6 Start: 9 End: 10 Entries:{8=golden, 9=fleece, 11=most, 12=people}
Der vollständige Code ist unten aufgeführt. Wie Sie sehen, gibt er einige Informationen über die eigentliche Abfrage aus und zeigt dann einen Kontext über die Treffer an. Beachten Sie, dass Sie dies natürlich auch erweitern können, um auf Dinge wie Nutzdaten und mehr zuzugreifen.
[code language=“java“]
package com.lucidworks.noodles;
/**
* Lizenziert von der Apache Software Foundation (ASF) unter einer oder mehreren
* Lizenzvereinbarungen für Mitwirkende. Weitere Informationen zum Urheberrecht finden Sie in der NOTICE-Datei, die mit
* diesem Werk verteilt wird.
* Die ASF lizenziert diese Datei für Sie unter der Apache License, Version 2.0
* (die „Lizenz“); Sie dürfen diese Datei nur in Übereinstimmung mit
* der Lizenz verwenden. Eine Kopie der Lizenz erhalten Sie unter
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Sofern nicht durch geltendes Recht vorgeschrieben oder schriftlich vereinbart, wird die unter der Lizenz vertriebene Software
* auf einer „AS IS“ BASIS vertrieben,
* OHNE GARANTIEN ODER BEDINGUNGEN IRGENDEINER ART, weder ausdrücklich noch stillschweigend.
* Siehe die Lizenz für die spezifischen Bestimmungen zu den Berechtigungen und
* Einschränkungen unter der Lizenz.
*/
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.FieldType;
import org.apache.lucene.document.StringField;
import org.apache.lucene.index.AtomicReader;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.DocsAndPositionsEnum;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.SlowCompositeReaderWrapper;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.TermContext;
import org.apache.lucene.index.Terms;
import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.spans.SpanTermQuery;
import org.apache.lucene.search.spans.Spans;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.Version;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* Diese Klasse dient nur zu Demonstrationszwecken. Es wird keine Garantie, Gewährleistung, etc. übernommen.
* <p/>
* Dies ist kein Code in Produktionsqualität!
*/
public class TermVectorFun {
public static String[] DOCS = {
„Der schnelle rote Fuchs sprang über die faulen braunen Hunde.“,
„Maria hatte ein kleines Lamm, dessen Fell weiß wie Schnee war“,
„Moby Dick ist die Geschichte eines Wals und eines besessenen Mannes.“,
„Der Räuber trug eine schwarze Fleecejacke und eine Baseballkappe.“,
„Der English Springer Spaniel ist der beste aller Hunde“,
„Das Vlies war grün und rot“,
„Die Geschichte des Goldenen Vlieses wird in der Geschichte gern gesehen, aber die meisten Menschen sind anderer Meinung.
};
public static void main(String[] args) throws IOException {
RAMDirectory ramDir = new RAMDirectory();
//Indexieren Sie einen erfundenen Inhalt
IndexWriter writer = new IndexWriter(ramDir, new IndexWriterConfig(Version.LUCENE_43, new StandardAnalyzer(Version.LUCENE_43)));
//Position und Offset-Informationen speichern
FieldType type = new FieldType();
type.setStoreTermVectors(true);
type.setStoreTermVectorOffsets(true);
type.setStoreTermVectorPositions(true);
type.setIndexed(true);
type.setTokenized(true);
for (int i = 0; i < DOCS.length; i++) {
Document doc = new Document();
Feld id = new StringField(„id“, „doc_“ + i, Field.Store.YES);
doc.add(id);
Feld text = new Feld(„Inhalt“, DOCS[i], Typ);
doc.add(text);
writer.addDocument(doc);
}
writer.close();
//Suchen Sie einen Sucher
IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(ramDir));
// Führen Sie eine Suche mit SpanQuery durch
SpanTermQuery fleeceQ = new SpanTermQuery(new Term(„Inhalt“, „Vlies“));
TopDocs Ergebnisse = searcher.search(fleeceQ, 10);
for (int i = 0; i < results.scoreDocs.length; i++) {
ScoreDoc scoreDoc = results.scoreDocs[i];
System.out.println(„Score Doc: “ + scoreDoc);
}
IndexReader reader = searcher.getIndexReader();
//Dies ist nicht die beste Methode, aber für das Beispiel funktioniert sie. Siehe http://www.slideshare.net/lucenerevolution/is-your-index-reader-really-atomic-or-maybe-slow für leistungsfähigere Ansätze
AtomicReader wrapper = SlowCompositeReaderWrapper.wrap(reader);
Map<Term, TermContext> termContexts = new HashMap<Term, TermContext>();
Spans spans = fleeceQ.getSpans(wrapper.getContext(), new Bits.MatchAllBits(reader.numDocs()), termContexts);
int window = 2;//Ermittelt die Wörter innerhalb von zwei vom Treffer
while (spans.next() == true) {
Map<Integer, String> entries = new TreeMap<Integer, String>();
System.out.println(„Dokument: “ + spans.doc() + “ Start: “ + spans.start() + “ Ende: “ + spans.end());
int start = spans.start() – window;
int end = spans.end() + window;
Begriffe content = reader.getTermVector(spans.doc(), „content“);
TermsEnum termsEnum = content.iterator(null);
BytesRef Begriff;
while ((term = termsEnum.next()) != null) {
//könnte die BytesRef hier speichern, aber String ist für dieses Beispiel einfacher
String s = new String(term.bytes, term.offset, term.length);
DocsAndPositionsEnum positionsEnum = termsEnum.docsAndPositions(null, null);
if (positionsEnum.nextDoc() != DocIdSetIterator.NO_MORE_DOCS) {
int i = 0;
int Position = -1;
while (i < positionsEnum.freq() && (position = positionsEnum.nextPosition()) != -1) {
if (Position >= Start && Position <= Ende) {
entries.put(position, s);
}
i++;
}
}
}
System.out.println(„Einträge:“ + Einträge);
}
}
}
[/code]