2014-07-26 2 views
6

Ich bin ein Grüngriff auf Lucene, und ich möchte Auto vorschlagen, genau wie Google, implementieren, wenn ich einen Charakter wie 'G' eingeben, würde es mir eine Liste geben, können Sie selbst versuchen.Wie implementiert man den automatischen Vorschlag mit der neuen AnalysingInfixSuggester-API von Lucene?

Ich habe Suche im ganzen Netz. Niemand hat dies getan, und es gibt uns einige neue Werkzeuge im Paket suggest

Aber ich brauche ein Beispiel, mir zu sagen, wie man die

Gibt es kann jemand helfen?

Antwort

33

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:

  1. Ranked Ergebnisse: Wir werden die beliebtesten übereinstimmenden Produkte zuerst vorschlagen.
  2. Regionsbeschränkte Ergebnisse: Wir empfehlen nur Produkte, die wir im Land des Kunden verkaufen.
  3. 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:

  1. ein Indexverzeichnis in RAM erstellen.
  2. Erstellen Sie eine StandardTokenizer.
  3. Erstellen Sie eine AnalyzingInfixSuggester mit dem RAM-Verzeichnis und Tokenizer.
  4. Index eine Anzahl von Produkten mit ProductIterator.
  5. 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 AnalyzingInfixSuggesteradd 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.]

+0

Wie verwenden Sie die Nutzlast? Können Sie zurück zum ursprünglichen Objekt konvertieren? – Marc

+0

Ich habe meine Antwort bearbeitet, um die Deserialisierung der Nutzlast zu demonstrieren. –

Verwandte Themen