Ich gebe Ihnen ein ziemlich vollständiges Beispiel, das Ihnen zeigt, wie Sie AnalyzingInfixSuggester
verwenden. In diesem Beispiel geben wir vor, dass wir Amazon sind, und wir möchten ein Produktsuchfeld automatisch vervollständigen. Wir nutzen die Funktionen des Lucene-Vorschlagssystems, um Folgendes zu implementieren:
- Ranked Ergebnisse: Wir werden die beliebtesten übereinstimmenden Produkte zuerst vorschlagen.
- Regionsbeschränkte Ergebnisse: Wir empfehlen nur Produkte, die wir im Land des Kunden verkaufen.
- Produktfotos: Wir speichern Produktfoto-URLs im Vorschlagsindex, damit wir sie in den Suchergebnissen anzeigen können, ohne eine zusätzliche Datenbanksuche durchführen zu müssen.
Zuerst werde ich eine einfache Klasse definiert Informationen über ein Produkt in Product.java zu halten:
import java.util.Set;
class Product implements java.io.Serializable
{
String name;
String image;
String[] regions;
int numberSold;
public Product(String name, String image, String[] regions,
int numberSold) {
this.name = name;
this.image = image;
this.regions = regions;
this.numberSold = numberSold;
}
}
Um Index-Datensätze mit den AnalyzingInfixSuggester
‚s build
Methode müssen Sie es um ein Objekt zu übergeben das implementiert die org.apache.lucene.search.suggest.InputIterator
Schnittstelle. Ein InputIterator
gibt Zugang zu den Schlüssel, Kontexten, Nutzlast und Gewicht für jeden Datensatz.
Der Schlüssel ist der Text, nach dem Sie eigentlich suchen und automatisch vervollständigen möchten. In unserem Beispiel wird es der Name des Produkts sein.
Die Kontexte sind eine Reihe von zusätzlichen, beliebigen Daten, die Sie zum Filtern von Datensätzen verwenden können. In unserem Beispiel sind die Kontexte die Menge der ISO-Codes für die Länder, in die wir ein bestimmtes Produkt liefern.
Die Nutzlast ist zusätzliche beliebige Daten, die Sie im Index für den Datensatz speichern möchten. In diesem Beispiel werden wir jede Instanz serialisieren und die resultierenden Bytes als Payload speichern. Wenn wir später nachschlagen, können wir die Nutzlast deserialisieren und auf Informationen in der Produktinstanz zugreifen, z. B. die Image-URL.
Das Gewicht wird verwendet, um Vorschlagsergebnisse zu bestellen; Ergebnisse mit einem höheren Gewicht werden zuerst zurückgegeben. Wir verwenden die Anzahl der Verkäufe für ein bestimmtes Produkt als sein Gewicht.
Hier ist der Inhalt von ProductIterator.java:
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.apache.lucene.search.suggest.InputIterator;
import org.apache.lucene.util.BytesRef;
class ProductIterator implements InputIterator
{
private Iterator<Product> productIterator;
private Product currentProduct;
ProductIterator(Iterator<Product> productIterator) {
this.productIterator = productIterator;
}
public boolean hasContexts() {
return true;
}
public boolean hasPayloads() {
return true;
}
public Comparator<BytesRef> getComparator() {
return null;
}
// This method needs to return the key for the record; this is the
// text we'll be autocompleting against.
public BytesRef next() {
if (productIterator.hasNext()) {
currentProduct = productIterator.next();
try {
return new BytesRef(currentProduct.name.getBytes("UTF8"));
} catch (UnsupportedEncodingException e) {
throw new Error("Couldn't convert to UTF-8");
}
} else {
return null;
}
}
// This method returns the payload for the record, which is
// additional data that can be associated with a record and
// returned when we do suggestion lookups. In this example the
// payload is a serialized Java object representing our product.
public BytesRef payload() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos);
out.writeObject(currentProduct);
out.close();
return new BytesRef(bos.toByteArray());
} catch (IOException e) {
throw new Error("Well that's unfortunate.");
}
}
// This method returns the contexts for the record, which we can
// use to restrict suggestions. In this example we use the
// regions in which a product is sold.
public Set<BytesRef> contexts() {
try {
Set<BytesRef> regions = new HashSet();
for (String region : currentProduct.regions) {
regions.add(new BytesRef(region.getBytes("UTF8")));
}
return regions;
} catch (UnsupportedEncodingException e) {
throw new Error("Couldn't convert to UTF-8");
}
}
// This method helps us order our suggestions. In this example we
// use the number of products of this type that we've sold.
public long weight() {
return currentProduct.numberSold;
}
}
In unserem Treiberprogramm werden wir die folgenden Dinge tun:
- ein Indexverzeichnis in RAM erstellen.
- Erstellen Sie eine
StandardTokenizer
.
- Erstellen Sie eine
AnalyzingInfixSuggester
mit dem RAM-Verzeichnis und Tokenizer.
- Index eine Anzahl von Produkten mit
ProductIterator
.
- Drucken Sie die Ergebnisse einiger Beispielsuchen.
Hier ist das Treiberprogramm, SuggestProducts.java:
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.search.suggest.analyzing.AnalyzingInfixSuggester;
import org.apache.lucene.search.suggest.Lookup;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.Version;
public class SuggestProducts
{
// Get suggestions given a prefix and a region.
private static void lookup(AnalyzingInfixSuggester suggester, String name,
String region) {
try {
List<Lookup.LookupResult> results;
HashSet<BytesRef> contexts = new HashSet<BytesRef>();
contexts.add(new BytesRef(region.getBytes("UTF8")));
// Do the actual lookup. We ask for the top 2 results.
results = suggester.lookup(name, contexts, 2, true, false);
System.out.println("-- \"" + name + "\" (" + region + "):");
for (Lookup.LookupResult result : results) {
System.out.println(result.key);
Product p = getProduct(result);
if (p != null) {
System.out.println(" image: " + p.image);
System.out.println(" # sold: " + p.numberSold);
}
}
} catch (IOException e) {
System.err.println("Error");
}
}
// Deserialize a Product from a LookupResult payload.
private static Product getProduct(Lookup.LookupResult result)
{
try {
BytesRef payload = result.payload;
if (payload != null) {
ByteArrayInputStream bis = new ByteArrayInputStream(payload.bytes);
ObjectInputStream in = new ObjectInputStream(bis);
Product p = (Product) in.readObject();
return p;
} else {
return null;
}
} catch (IOException|ClassNotFoundException e) {
throw new Error("Could not decode payload :(");
}
}
public static void main(String[] args) {
try {
RAMDirectory index_dir = new RAMDirectory();
StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_48);
AnalyzingInfixSuggester suggester = new AnalyzingInfixSuggester(
Version.LUCENE_48, index_dir, analyzer);
// Create our list of products.
ArrayList<Product> products = new ArrayList<Product>();
products.add(
new Product(
"Electric Guitar",
"http://images.example/electric-guitar.jpg",
new String[]{"US", "CA"},
100));
products.add(
new Product(
"Electric Train",
"http://images.example/train.jpg",
new String[]{"US", "CA"},
100));
products.add(
new Product(
"Acoustic Guitar",
"http://images.example/acoustic-guitar.jpg",
new String[]{"US", "ZA"},
80));
products.add(
new Product(
"Guarana Soda",
"http://images.example/soda.jpg",
new String[]{"ZA", "IE"},
130));
// Index the products with the suggester.
suggester.build(new ProductIterator(products.iterator()));
// Do some example lookups.
lookup(suggester, "Gu", "US");
lookup(suggester, "Gu", "ZA");
lookup(suggester, "Gui", "CA");
lookup(suggester, "Electric guit", "US");
} catch (IOException e) {
System.err.println("Error!");
}
}
}
Und hier ist die Ausgabe von dem Treiberprogramm:
-- "Gu" (US):
Electric Guitar
image: http://images.example/electric-guitar.jpg
# sold: 100
Acoustic Guitar
image: http://images.example/acoustic-guitar.jpg
# sold: 80
-- "Gu" (ZA):
Guarana Soda
image: http://images.example/soda.jpg
# sold: 130
Acoustic Guitar
image: http://images.example/acoustic-guitar.jpg
# sold: 80
-- "Gui" (CA):
Electric Guitar
image: http://images.example/electric-guitar.jpg
# sold: 100
-- "Electric guit" (US):
Electric Guitar
image: http://images.example/electric-guitar.jpg
# sold: 100
Anhang
Es gibt einen Weg schriftlich zu vermeiden eine volle InputIterator
, die Sie vielleicht einfacher finden. Sie können einen Stub InputIterator
schreiben, der null
von seinen Methoden next
, payload
und contexts
zurückgibt. Führen Sie eine Instanz davon zu AnalyzingInfixSuggester
‚s build
Methode:
suggester.build(new ProductIterator(new ArrayList<Product>().iterator()));
dann für jedes Element, das Sie indizieren möchten, rufen Sie die AnalyzingInfixSuggester
add
Methode:
suggester.add(text, contexts, weight, payload)
Nachdem Sie alles indiziert haben, rufen Sie refresh
:
suggester.refresh();
Wenn Sie große Datenmengen indexieren, ist es möglich, signifikant sp eedup Indexierung mit dieser Methode mit mehreren Threads: Rufen Sie build
, dann verwenden Sie mehrere Threads zu add
Elemente, schließlich rufen Sie refresh
.
[Bearbeitet 2015.04.23 Deserialisieren Informationen vom LookupResult
Nutzlast zu demonstrieren.]
Wie verwenden Sie die Nutzlast? Können Sie zurück zum ursprünglichen Objekt konvertieren? – Marc
Ich habe meine Antwort bearbeitet, um die Deserialisierung der Nutzlast zu demonstrieren. –