在休眠搜索Elasticsearch中实现“随机”排序

时间:2019-02-20 09:05:40

标签: java elasticsearch hibernate-search

我想使用休眠搜索Elasticsearch库实现某种“随机”排序。我正在做的事情如下:

实施FieldComparator:

public class RandomOrderFieldComparator extends FieldComparator<Integer>  {

  private final Random randomGenerator = new Random();

  @Override
  public int compare(int slot1, int slot2) {
    return randomGenerator.nextInt();
  }

  @Override
  public void setTopValue(Integer value) {
    //not needed as the purpose is to generate random integers w
  }

  @Override
  public Integer value(int slot) {
    return randomGenerator.nextInt();
  }

  @Override
  public LeafFieldComparator getLeafComparator(LeafReaderContext context) throws IOException {
    return null;
  }
}

实施FieldComparatorSource

public class SampleFieldComparatorSource extends FieldComparatorSource {

  @Override
  public FieldComparator<?> newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
    return new RandomOrderFieldComparator();
  }
}

最后创建一个提供FieldComparatorSource的自定义SortField:

queryBuilder
        .sort()
        .byNative(
            new SortField(
                "id",
                new SampleFieldComparatorSource()
            )
        );

问题在于,它仍然仅使用“ id”字段上的常规排序来生成查询,并且从未命中比较器:

"sort": [
    {
      "id": {
        "order": "asc"
      }
    }
  ]

我在做什么错?使用休眠搜索库实现“随机”排序的最佳方法是什么?

3 个答案:

答案 0 :(得分:1)

Elasticsearch集成通过将Lucene对象转换为JSON并将其发送到Elasticsearch集群而起作用。它适用于简单的事情,例如按字段值排序,术语查询等,但绝对不能转换您自己实现的Java对象。

简而言之,一旦您使用Lucene接口的自定义实现,就可以确定它无法与Elasticsearch集成一起使用。因此,您的RandomOrderFieldComparator无法正常工作。

如果您需要执行Hibernate Search不会通过其API公开的高级内容(例如这种随机排序),则必须自己编写发送给Elasticsearch的JSON。 Stackoverflow为该问题提供了various solutions

编辑:我其余的答案是错误的。我已经忘记了本机排序功能,可惜了。参见other answer

答案 1 :(得分:1)

实际上,我找到了一种通过休眠搜索直接传递JSON并使用无痛脚本的方法:

queryBuilder.sort()
          .byNative("_script", "{"
                                  + "\"type\" : \"number\","
                                  + "\"script\" : {"
                                  + "   \"lang\": \"painless\","
                                  + "   \"source\": \"new Random().nextInt()\""
                                  + "},"
                                  + "\"order\" : \"asc\""
                                + "}");

答案 2 :(得分:0)

使用Hristo Angelov的答案,我无法获得分页工作的种子。我曾尝试使用new Random(long).nextInt(),但无论种子值如何,它都始终生成相同的顺序。遵循link in yrodiere's answer之后,我找到了这种解决方案,该解决方案完美无缺。

String fieldName = "id"; //replace with your search field
String seed = "hello world"; //generate a random string that persists across pages of the same search
sort = qb.sort()
         .byNative("_script", 
                   "{"
                 +    "\"script\" : \"(doc['" + fieldName + "'].value + '" + seed + "').hashCode()\","
                 +    "\"type\" : \"number\"," 
                 +    "\"order\" : \"asc\"" 
                 + "}")
         .createSort();