【问题标题】:Sort Index using DocValues for integers?使用 DocValues 对整数排序索引?
【发布时间】:2021-03-05 12:27:57
【问题描述】:

我将 Lucene 用于支持多种语言和多组选项的文本字段的自动完成机制。每个组有大约 2k 到 5k 个不同的值。

目前我查询所有命中并手动根据整数值对它们进行排序。由于这效率低下,我需要使用 doc-values 创建索引。我理解这个理论,但我找不到一个好的代码 sn-p 让它工作。我带来并阅读了两本书,它们要么没有覆盖,要么覆盖得不好(一小部分只有一行代码)。

我的目标是为每个文档索引一个整数值并按降序排序。

另外我想问一下我是否错过了市长文件来源? Lucene 文档既不全面也不易于访问。我曾经在行动中使用 Lucene,但这本书已有十年之久,而 Lucene 的最新变化在 API 方面非常引人注目。

举个例子:

  • {name:"A1", number:1000}
  • {名称:“A2”,编号:1001}
  • {名称:“A3”,编号:990}
  • {name:"B1", number:300}

= 查询:A* + 按数字排序 + top2 => A3, A1

总结:我目前获取所有文档并在代码中进行排序和修剪(限制),并且希望 Lucene 这样做。


实现使用 Java。由于我只使用一小部分信息,但使用多种语言,我使用 RAMDirectory 创建了一个索引(是的,我知道它已被弃用,但它可以工作)并使用标准分析器将每个文档添加到标准索引编写器。

据我了解需求,我需要定义和使用存储在列中的字段以允许使用 Lucene 进行排序。我尝试了几个小时,但由于获取所有信息而放弃了,并在内存中查找数据并排序+修剪它成功了,但它并不令人满意。

所以只需要在索引中添加一个整数字段,以便在 lucene 中进行排序。

【问题讨论】:

  • 您能否更具体地说明您希望代码示例演示的内容?
  • @RonC 我扩展了帖子。
  • 您使用哪种语言/库?您现在如何索引/查询文档?
  • 好吧,认为 lucene 是一个 java 库,但我知道如何使用不同的语言/实现。我会添加更多细节。
  • 感谢 Stackoverflow 不允许编辑 cmets.. NewVersion:Lucene 是一个 Java 库,但我知道人们如何使用不同的语言/实现并且还缺少 Java 标记...。我添加了更多细节。感谢您的提示!

标签: java lucene


【解决方案1】:

你说得对,Lucene 的文档可能有点挑战性,因为 Lucene In Action 一书的最新修订版是针对 3.0 版的,而 Lucene 4.0 中进行了非常重大的更改。我找到了一本涵盖 Lucene 4 的书,名为 Lucene 4 Cookbook,但它并没有想太多,只是它的排序范围仅限于一页,但它确实提供了一个示例。

了解 Lucene 的一个重要来源是存储在项目中的单元测试。这是我在下面找到示例的地方。此示例显示如何将您的号码存储为 NumericDocValue,然后按其排序。单元测试通常不适合剪切和粘贴应用程序使用,但它们很好地展示了我们如何使用该功能。例如,这个单元测试使用RandomIndexWriter,而您将使用IndexWriter

这种排序方法利用了 DocValues。关于 DocValues 要记住的一件事是它们不与文档一起存储,而是由 DocValue 字段存储在一起。这就是它们特别适合分拣的原因。但是,当您读回文档时,它不会是字段之一,除非您将该值作为字段存储在文档中。这就是示例将值存储两次的原因,一次作为NumericDocValuesField,一次作为StringField

  
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

 /** Tests sorting on type int */
  public void testInt() throws IOException {
    Directory dir = newDirectory();
    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);

    Document doc = new Document();
    doc.add(new NumericDocValuesField("value", 300000));
    doc.add(newStringField("value", "300000", Field.Store.YES));
    writer.addDocument(doc);
    
    doc = new Document();
    doc.add(new NumericDocValuesField("value", -1));
    doc.add(newStringField("value", "-1", Field.Store.YES));
    writer.addDocument(doc);

    doc = new Document();
    doc.add(new NumericDocValuesField("value", 4));
    doc.add(newStringField("value", "4", Field.Store.YES));
    writer.addDocument(doc);
    
    IndexReader ir = writer.getReader();
    writer.close();
    
    IndexSearcher searcher = newSearcher(ir);
    Sort sort = new Sort(new SortField("value", SortField.Type.INT));

    TopDocs td = searcher.search(new MatchAllDocsQuery(), 10, sort);
    assertEquals(3, td.totalHits.value);
    // numeric order
    assertEquals("-1", searcher.doc(td.scoreDocs[0].doc).get("value"));
    assertEquals("4", searcher.doc(td.scoreDocs[1].doc).get("value"));
    assertEquals("300000", searcher.doc(td.scoreDocs[2].doc).get("value"));

    ir.close();
    dir.close();
  }

来源:Lucene Unit Test on GitHub

不幸的是,我是 c# 开发人员而不是 Java 开发人员,所以我很难为您编写一个更接近您要求使用 java 的示例,因为我还没有一种简单的方法来测试 Java Lucene 代码。但我在下面提供了一个使用 LuceneNet 的 C# 示例,我认为您会发现它很容易转换为 Java。

public void NumericDocValueSort() {

            Analyzer standardAnalyzer = new StandardAnalyzer(LuceneVersion.LUCENE_48);
            Directory indexDir = new RAMDirectory();
            IndexWriterConfig iwc = new IndexWriterConfig(LuceneVersion.LUCENE_48, standardAnalyzer);

            IndexWriter indexWriter = new IndexWriter(indexDir, iwc);

            Document doc = new Document();

            doc.Add(new TextField("name", "A1", Field.Store.YES));
            //doc.Add(new StoredField("number", 1000L));              //uncomment this line to optionally be able to retrieve it from the doc later, can be  done for every doc
            doc.Add(new NumericDocValuesField("number", 1000L));
            indexWriter.AddDocument(doc);

            doc.Fields.Clear();
            doc.Add(new TextField("name", "A2", Field.Store.YES));
            doc.Add(new NumericDocValuesField("number", 1001L));
            indexWriter.AddDocument(doc);

            doc.Fields.Clear();
            doc.Add(new TextField("name", "A3", Field.Store.YES));
            doc.Add(new NumericDocValuesField("number", 990L));
            indexWriter.AddDocument(doc);

            doc.Fields.Clear();
            doc.Add(new TextField("name", "A4", Field.Store.YES));
            doc.Add(new NumericDocValuesField("number", 300L));
            indexWriter.AddDocument(doc);

            indexWriter.Commit();

            IndexReader reader = indexWriter.GetReader(applyAllDeletes: true);
            IndexSearcher searcher = new IndexSearcher(reader);

            Sort sort;
            TopDocs docs;
            SortField sortField = new SortField("number", SortFieldType.INT64);
            sort = new Sort(sortField);

            docs = searcher.Search(new MatchAllDocsQuery(), 1000, sort);
            

            foreach (ScoreDoc scoreDoc in docs.ScoreDocs) {
                Document curDoc = searcher.Doc(scoreDoc.Doc);
                string name = curDoc.Get("name");
            }

            reader.Dispose();               //reader.close() in java
        }

我在我的机器上运行了这段代码,它以正确的数字顺序返回 for 循环中的文档。请注意,我使用NumericDocValuesField 而不是SortedNumericSortField 的原因是,仅当单个文档包含该字段的多个值时才需要后者。你的例子没有,所以 NumericDocValuesField 在这种情况下是你想要的。

人们经常对名称SortedNumericSortField 中的“排序”一词感到困惑。 在这种情况下,这意味着如果该字段包含文档中该字段的多个值,则这些值将按排序顺序列在文档的字段中。它与需要按排序顺序排列的 文档 的想法无关。是的,我知道,不是最好的命名方法,有点令人困惑。无论如何,希望能为您解决问题。

【讨论】:

  • 感谢 RonC 的努力。我在开发服务时自己尝试过。除了列索引字段排序之外,我的一切工作都很快。我试了两个小时就放弃了。我拥有你自己提到的两本书。不多看。从 2015 年到 Lucene 3.0 之前,你会发现很多,但今天我们使用 Lucene 7 或 8,从那以后他们多次炸毁 API。我会测试你的解决方案,如果我成功了,我会奖励你。
  • 有效。感谢您的帮助和非常好的回应。
【解决方案2】:

使用SortedNumericDocValuesField 将该字段添加到您的文档中。

在搜索查询中使用具有相同名称的 SortedNumericSortField

Sort sort = new Sort(new SortedNumericSortField("number", SortField.Type.LONG, true));
TopDocs docs = searcher.search(new MatchAllDocsQuery(), 100, sort);

查看这个相关问题: How to sort Numeric field in Lucene 6

TBH 我刚刚搜索了您的用例。

【讨论】:

  • 我知道这一点,但未能创建索引并放弃了,因为我有一个可行的解决方案,但使用排序 + 限制而不是获取所有结果的原因并不那么优雅。
猜你喜欢
  • 2021-08-06
  • 1970-01-01
  • 2013-02-11
  • 2016-01-01
  • 1970-01-01
  • 2022-09-23
  • 2015-10-05
  • 1970-01-01
  • 2023-04-04
相关资源
最近更新 更多