كيفية فرز نتائج البحث على حقول متعددة باستخدام وظيفة الترجيح؟

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 يمثل المستند. يجب أن تكون وظيفة الفرز غير منتظمة ويمكن أن تختلف عن البحث إلى البحث لأن العوامل الثابتة تتأثر بالمستخدم الذي يقوم بالبحث.

هل لدى أي شخص فكرة عن كيفية حل هذا أو ربما فكرة عن كيفية تحقيق هذا الهدف بطريقة أخرى؟

هل كانت مفيدة؟

المحلول

يمكنك محاولة تنفيذ العرف Scoredoccomparator. فمثلا:

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) فحسب ، بل صفيف المستند أيضًا. أعد مجموعة المستندات المصنفة وانتقلت.

تنفيذ فئة التشابه الخاصة بك والتجاوز جيش الدفاع الإسرائيلي (المصطلح ، الباحث) طريقة. في هذه الطريقة ، يمكنك إعادة النتيجة على النحو التالي. 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