使用整数的 DocValues 对索引进行排序?
Sort Index using DocValues for integers?
我将 Lucene 用于支持多种语言和多组选项的文本字段的自动完成机制。每组大约有 2k 到 5k 个不同的值。
目前我查询所有匹配项并根据整数值手动对这些匹配项进行排序。由于这是低效的,我需要使用 doc-values 创建一个索引。我理解这个理论,但我找不到一个好的代码片段来让它工作。我带来并阅读了两本书,但它们要么没有涵盖,要么涵盖得很差(一小节,一行代码)。
我的目标是为每个文档索引一个整数值并按降序排序。
另外请问我是否错过了市长文件来源? Lucene 文档既不全面也不易于访问。我曾经在 Action 中使用 Lucene,但这本书已有十年历史,而且 Lucene 最近的变化在 API.
方面非常显着
举个例子:
- {姓名:"A1", number:1000}
- {名称:"A2", number:1001}
- {名称:"A3", number:990}
- {名称:"B1", number:300}
= 查询:A* + 按编号排序 + top2 => A3, A1
总结:我目前正在获取所有文档并在代码中进行排序和 trimming(限制),我更希望 Lucene 这样做。
实施使用 Java。由于我只使用一小部分信息,但使用多种语言,我使用 RAMDirectory 创建了一个索引(是的,我知道它已被弃用,但它可以工作)并使用标准分析器将每个文档添加到标准索引编写器中。
据我设法理解需求,我需要定义和使用存储在列中的字段以允许使用 Lucene 进行排序。我尝试了几个小时,但由于获取所有信息并在内存中查找数据而放弃了,然后排序+trim 它成功了,但令人不满意。
所以只需要在索引中添加一个整数字段,以便在 lucene 中进行排序。
使用 SortedNumericDocValuesField
将字段添加到您的文档。
在您的搜索查询中使用具有相同名称的 SortedNumericSortField
:
Sort sort = new Sort(new SortedNumericSortField("number", SortField.Type.LONG, true));
TopDocs docs = searcher.search(new MatchAllDocsQuery(), 100, sort);
查看这个相关问题:
老实说,我刚刚在谷歌上搜索了您的用例。
你是对的,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();
}
不幸的是,我是一名 c# 开发人员而不是 Java 开发人员,所以我很难为您写一个更接近您使用 java 要求的示例,因为我不' 还没有一个简单的方法来测试 Java Lucene 代码。但我在下面提供了一个使用 LuceneNet 的 C# 示例,我认为您会发现它很容易 t运行slate 到 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
}
我在我的机器上 运行 这段代码,它 returns for 循环中的文档以正确的编号顺序排列。请注意,我使用 NumericDocValuesField
而不是 SortedNumericSortField
的原因是因为仅当单个文档包含该字段的多个值时才需要后者。你的例子没有,所以 NumericDocValuesField
就是你想要的那种情况。
人们经常对名称中的排序一词感到困惑 SortedNumericSortField
。
在此上下文中,这意味着如果该字段包含文档中该字段的多个值,这些值将按排序顺序列在 文档的字段 中。它与按排序顺序需要 documents 的想法无关。是的,我知道,这不是最好的命名方法,有点令人困惑。无论如何,希望这能为您解决问题。
我将 Lucene 用于支持多种语言和多组选项的文本字段的自动完成机制。每组大约有 2k 到 5k 个不同的值。
目前我查询所有匹配项并根据整数值手动对这些匹配项进行排序。由于这是低效的,我需要使用 doc-values 创建一个索引。我理解这个理论,但我找不到一个好的代码片段来让它工作。我带来并阅读了两本书,但它们要么没有涵盖,要么涵盖得很差(一小节,一行代码)。
我的目标是为每个文档索引一个整数值并按降序排序。
另外请问我是否错过了市长文件来源? Lucene 文档既不全面也不易于访问。我曾经在 Action 中使用 Lucene,但这本书已有十年历史,而且 Lucene 最近的变化在 API.
方面非常显着举个例子:
- {姓名:"A1", number:1000}
- {名称:"A2", number:1001}
- {名称:"A3", number:990}
- {名称:"B1", number:300}
= 查询:A* + 按编号排序 + top2 => A3, A1
总结:我目前正在获取所有文档并在代码中进行排序和 trimming(限制),我更希望 Lucene 这样做。
实施使用 Java。由于我只使用一小部分信息,但使用多种语言,我使用 RAMDirectory 创建了一个索引(是的,我知道它已被弃用,但它可以工作)并使用标准分析器将每个文档添加到标准索引编写器中。
据我设法理解需求,我需要定义和使用存储在列中的字段以允许使用 Lucene 进行排序。我尝试了几个小时,但由于获取所有信息并在内存中查找数据而放弃了,然后排序+trim 它成功了,但令人不满意。
所以只需要在索引中添加一个整数字段,以便在 lucene 中进行排序。
使用 SortedNumericDocValuesField
将字段添加到您的文档。
在您的搜索查询中使用具有相同名称的 SortedNumericSortField
:
Sort sort = new Sort(new SortedNumericSortField("number", SortField.Type.LONG, true));
TopDocs docs = searcher.search(new MatchAllDocsQuery(), 100, sort);
查看这个相关问题:
老实说,我刚刚在谷歌上搜索了您的用例。
你是对的,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();
}
不幸的是,我是一名 c# 开发人员而不是 Java 开发人员,所以我很难为您写一个更接近您使用 java 要求的示例,因为我不' 还没有一个简单的方法来测试 Java Lucene 代码。但我在下面提供了一个使用 LuceneNet 的 C# 示例,我认为您会发现它很容易 t运行slate 到 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
}
我在我的机器上 运行 这段代码,它 returns for 循环中的文档以正确的编号顺序排列。请注意,我使用 NumericDocValuesField
而不是 SortedNumericSortField
的原因是因为仅当单个文档包含该字段的多个值时才需要后者。你的例子没有,所以 NumericDocValuesField
就是你想要的那种情况。
人们经常对名称中的排序一词感到困惑 SortedNumericSortField
。
在此上下文中,这意味着如果该字段包含文档中该字段的多个值,这些值将按排序顺序列在 文档的字段 中。它与按排序顺序需要 documents 的想法无关。是的,我知道,这不是最好的命名方法,有点令人困惑。无论如何,希望这能为您解决问题。