가중 함수를 사용하여 여러 필드에서 검색 결과를 정렬하는 방법은 무엇입니까?

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

  •  03-07-2019
  •  | 
  •  

문제

모든 문서에는 숫자 값이 포함 된 여러 필드가있는 Lucene 지수가 있습니다. 이제이 필드의 가중 합계에 검색 결과를 정렬하고 싶습니다. 예를 들어:

field1=100
field2=002
field3=014

가중 함수는 다음과 같습니다.

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

결과는 주문해야합니다 f(d) 어디 d 문서를 나타냅니다. 정렬 함수는 정적이 아닌 것이어야하며 검색을 수행하는 사용자의 일정한 요인이 영향을 받기 때문에 검색마다 다를 수 있습니다.

이 문제를 해결하는 방법이나 다른 방식 으로이 목표를 달성하는 방법에 대한 아이디어가 있습니까?

도움이 되었습니까?

해결책

관습을 구현할 수 있습니다 스코어 코믹한 사람. 예를 들어:

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);
    }

}

다음은 다음과 같습니다 ScaledScoreDocComparator 행동 중. 테스트에서 작동한다고 생각하지만 귀하의 데이터에 대해 증명하는 것이 좋습니다.

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;

보너스!

Lucene 개발자가 더 이상 사용하지 않는 것으로 보입니다 ScoreDocComparator 인터페이스 (현재 전복 저장소에서 더 이상 사용되지 않음). 다음은 다음의 예입니다 ScaledScoreDocComparator 준수하도록 수정되었습니다 ScoreDocComparator후계자, 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);
    }

}

이 새로운 클래스를 사용하는 것은 원본과 매우 유사하지만 sort 객체는 약간 다릅니다.

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);
            }
        }
    )
);

다른 팁

이를 수행하는 한 가지 방법은 정렬 기능의 매개 변수로 수락하는 것입니다.

필드 수, 문서 배열, 중량 계수 목록 (필드 수에 따라)

각 문서의 계량 함수를 계산하여 결과를 문서 배열과 동일한 순서로 별도의 배열로 저장합니다. 그런 다음 원하는 모든 종류 (빠른 정렬이 가장 좋을 것입니다)을 수행하여 F (D) 배열뿐만 아니라 문서 배열도 정렬하도록합니다. 정렬 된 문서 배열을 반환하면 완료됩니다.

자신의 유사성 클래스를 구현하고 재정의하십시오 IDF (용어, 검색 자) 방법. 이 방법에서는 다음과 같이 점수를 반환 할 수 있습니다. 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;

쿼리를 실행할 때 모든 필드에 있는지 확인하십시오. 쿼리는 모양입니다

필드 1 : 용어 필드 2 : 용어 필드 3 : 용어

최종 점수는 쿼리 정규화에 따라 일부 가중치를 추가합니다. 그러나 이는 귀하가 제공 한 방정식에 따라 문서의 상대 순위에 영향을 미치지 않습니다.

등급을 유지하고 비슷한 래퍼를 만듭니다. 같은 것 :

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);
   }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top