Wie sortiere ich Suchergebnisse auf mehreren Feldern mithilfe einer Gewichtungsfunktion?

StackOverflow https://stackoverflow.com/questions/817998

  •  03-07-2019
  •  | 
  •  

Frage

Ich habe einen Lucene -Index, in dem jedes Dokument mehrere Felder enthält, die numerische Werte enthalten. Jetzt möchte ich das Suchergebnis auf einer gewichteten Summe dieses Feldes sortieren. Zum Beispiel:

field1=100
field2=002
field3=014

Und die Gewichtungsfunktion sieht aus wie:

f(d) = field1 * 0.5 + field2 * 1.4 + field3 * 1.8

Die Ergebnisse sollten von bestellt werden f(d) wo d repräsentiert das Dokument. Die Sortierfunktion sollte nicht statisch sein und kann von der Suche nach Suche unterscheiden, da die konstanten Faktoren vom Benutzer beeinflusst werden, der die Suche durchführt.

Hat jemand eine Idee, wie er dies lösen kann oder vielleicht eine Idee, wie dieses Ziel auf andere Weise erreicht werden kann?

War es hilfreich?

Lösung

Sie könnten versuchen, einen Brauch zu implementieren ScoredocComparator. Zum Beispiel:

public class ScaledScoreDocComparator implements ScoreDocComparator {

    private int[][] values;
    private float[] scalars;

    public ScaledScoreDocComparator(IndexReader reader, String[] fields, float[] scalars) throws IOException {
        this.scalars = scalars;
        this.values = new int[fields.length][];
        for (int i = 0; i < values.length; i++) {
            this.values[i] = FieldCache.DEFAULT.getInts(reader, fields[i]);
        }
    }

    protected float score(ScoreDoc scoreDoc) {
        int doc = scoreDoc.doc;

        float score = 0;
        for (int i = 0; i < values.length; i++) {
            int value = values[i][doc];
            float scalar = scalars[i];
            score += (value * scalar);
        }
        return score;
    }

    @Override
    public int compare(ScoreDoc i, ScoreDoc j) {
        float iScore = score(i);
        float jScore = score(j);
        return Float.compare(iScore, jScore);
    }

    @Override
    public int sortType() {
        return SortField.CUSTOM;
    }

    @Override
    public Comparable<?> sortValue(ScoreDoc i) {
        float score = score(i);
        return Float.valueOf(score);
    }

}

Hier ist ein Beispiel von ScaledScoreDocComparator in Aktion. Ich glaube, es funktioniert in meinem Test, aber ich ermutige Sie, es gegen Ihre Daten zu beweisen.

final String[] fields = new String[]{ "field1", "field2", "field3" };
final float[] scalars = new float[]{ 0.5f, 1.4f, 1.8f };

Sort sort = new Sort(
    new SortField(
        "",
        new SortComparatorSource() {
            public ScoreDocComparator newComparator(IndexReader reader, String fieldName) throws IOException {
                return new ScaledScoreDocComparator(reader, fields, scalars);
            }
        }
    )
);

IndexSearcher indexSearcher = ...;
Query query = ...;
Filter filter = ...; // can be null
int nDocs = 100;

TopFieldDocs topFieldDocs = indexSearcher.search(query, filter, nDocs, sort);
ScoreDoc[] scoreDocs = topFieldDocs.scoreDocs;

Bonus!

Es scheint, dass die Lucene -Entwickler das abwerten ScoreDocComparator Schnittstelle (Sie ist derzeit im Subversion -Repository veraltet). Hier ist ein Beispiel für das ScaledScoreDocComparator modifiziert, um sich an zu halten an ScoreDocComparatorNachfolger, FieldComparator:

public class ScaledComparator extends FieldComparator {

    private String[] fields;
    private float[] scalars;
    private int[][] slotValues;
    private int[][] currentReaderValues;
    private int bottomSlot;

    public ScaledComparator(int numHits, String[] fields, float[] scalars) {
        this.fields = fields;
        this.scalars = scalars;

        this.slotValues = new int[this.fields.length][];
        for (int fieldIndex = 0; fieldIndex < this.fields.length; fieldIndex++) {
            this.slotValues[fieldIndex] = new int[numHits];
        }

        this.currentReaderValues = new int[this.fields.length][];
    }

    protected float score(int[][] values, int secondaryIndex) {
        float score = 0;

        for (int fieldIndex = 0; fieldIndex < fields.length; fieldIndex++) {
            int value = values[fieldIndex][secondaryIndex];
            float scalar = scalars[fieldIndex];
            score += (value * scalar);
        }

        return score;
    }

    protected float scoreSlot(int slot) {
        return score(slotValues, slot);
    }

    protected float scoreDoc(int doc) {
        return score(currentReaderValues, doc);
    }

    @Override
    public int compare(int slot1, int slot2) {
        float score1 = scoreSlot(slot1);
        float score2 = scoreSlot(slot2);
        return Float.compare(score1, score2);
    }

    @Override
    public int compareBottom(int doc) throws IOException {
        float bottomScore = scoreSlot(bottomSlot);
        float docScore = scoreDoc(doc);
        return Float.compare(bottomScore, docScore);
    }

    @Override
    public void copy(int slot, int doc) throws IOException {
        for (int fieldIndex = 0; fieldIndex < fields.length; fieldIndex++) {
            slotValues[fieldIndex][slot] = currentReaderValues[fieldIndex][doc];
        }
    }

    @Override
    public void setBottom(int slot) {
        bottomSlot = slot;
    }

    @Override
    public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException {
        for (int fieldIndex = 0; fieldIndex < fields.length; fieldIndex++) {
            String field = fields[fieldIndex];
            currentReaderValues[fieldIndex] = FieldCache.DEFAULT.getInts(reader, field);
        }
    }

    @Override
    public int sortType() {
        return SortField.CUSTOM;
    }

    @Override
    public Comparable<?> value(int slot) {
        float score = scoreSlot(slot);
        return Float.valueOf(score);
    }

}

Die Verwendung dieser neuen Klasse ist dem Original sehr ähnlich, außer dass die Definition der Definition der sort Objekt ist etwas anders:

final String[] fields = new String[]{ "field1", "field2", "field3" };
final float[] scalars = new float[]{ 0.5f, 1.4f, 1.8f };

Sort sort = new Sort(
    new SortField(
        "",
        new FieldComparatorSource() {
            public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
                return new ScaledComparator(numHits, fields, scalars);
            }
        }
    )
);

Andere Tipps

Ich denke, eine Möglichkeit, dies zu tun, wäre, diese als Parameter für Ihre Sortierfunktion zu akzeptieren:

Anzahl der Felder, Array von Dokumenten, Liste der Gewichtsfaktoren (basierend auf der Anzahl der Felder)

Berechnen Sie die Waagefunktion für jedes Dokument und speichern Sie das Ergebnis in einem separaten Array in derselben Reihenfolge wie das Dokumentarray. Führen Sie dann jede Art, die Sie wünschen, (Schnellsort "wahrscheinlich am besten) und achten Sie darauf, dass Sie nicht nur das F (D) -Array, sondern auch das Dokumentarray sortieren. Geben Sie das sortierte Dokumentearray zurück und Sie sind fertig.

Implementieren Sie Ihre eigene Ähnlichkeitsklasse und überschreiben Sie IDF (Begriff, Sucher) Methode. Bei dieser Methode können Sie die Punktzahl wie folgt zurückgeben. if (Term.field.equals ("field1") {

    if (term.field.equals("field1") {
        score = 0.5 * Integer.parseInt(term.text());
    } else if (term.field.equals("field2") {
        score = 1.4 * Integer.parseInt(term.text());
    } // and so on
    return score;

Wenn Sie die Abfrage ausführen, stellen Sie sicher, dass sie sich auf allen Feldern befindet. Das ist eine Abfrage sollte so aussehen wie

Feld1: Term Feld2: Term Field3: Term

Die Endbewertung fügt auch einige Gewichte hinzu, die auf der Abfragenormalisierung basieren. Dies wirkt sich jedoch nicht auf das relative Ranking der Dokumente gemäß der von Ihnen gegebenen Gleichung aus.

Erstellen Sie einen Wrapper, der die Bewertung hält und vergleichbar ist. Etwas wie:

public void sort(Datum[] data) {
   Rating[] ratings = new Rating[data.length];
   for(int i=0;i<data.length;i++)
     rating[i] = new Rating(data[i]);
   Arrays.sort(rating);
   for(int i=0;i<data.length;i++)
     data[i] = rating[i].datum;
}

class Rating implements Comparable<Datum> {
   final double rating;
   final Datum datum;

   public Rating(Datum datum) {
      this.datum = datum;
      rating = datum.field1 * 0.5 + datum.field2 * 1.4 + datum.field3 * 1.8
   }

   public int compareTo(Datum d) {
      return Double.compare(rating, d.rating);
   }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top