diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index da89bc1789b6..1bc6cfe3ccd9 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -114,7 +114,9 @@ API Changes New Features --------------------- -(No changes) + +* GITHUB#12054: Introduce a new KeywordField for simple and efficient + filtering, sorting and faceting. (Adrien Grand) Improvements --------------------- diff --git a/lucene/core/src/java/org/apache/lucene/document/KeywordField.java b/lucene/core/src/java/org/apache/lucene/document/KeywordField.java new file mode 100644 index 000000000000..70b27ad671af --- /dev/null +++ b/lucene/core/src/java/org/apache/lucene/document/KeywordField.java @@ -0,0 +1,188 @@ +/* + * 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. + */ +package org.apache.lucene.document; + +import java.util.Objects; +import org.apache.lucene.index.DocValuesType; +import org.apache.lucene.index.IndexOptions; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.ConstantScoreQuery; +import org.apache.lucene.search.IndexOrDocValuesQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.SortedSetSelector; +import org.apache.lucene.search.SortedSetSortField; +import org.apache.lucene.search.TermInSetQuery; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.util.BytesRef; + +/** + * Field that indexes a per-document String or {@link BytesRef} into an inverted index for fast + * filtering, stores values in a columnar fashion using {@link DocValuesType#SORTED_SET} doc values + * for sorting and faceting, and optionally stores values as stored fields for top-hits retrieval. + * This field does not support scoring: queries produce constant scores. If you need more + * fine-grained control you can use {@link StringField}, {@link SortedDocValuesField} or {@link + * SortedSetDocValuesField}, and {@link StoredField}. + * + *

This field defines static factory methods for creating common query objects: + * + *

+ */ +public class KeywordField extends Field { + + private static final FieldType FIELD_TYPE = new FieldType(); + private static final FieldType FIELD_TYPE_STORED; + + static { + FIELD_TYPE.setIndexOptions(IndexOptions.DOCS); + FIELD_TYPE.setOmitNorms(true); + FIELD_TYPE.setTokenized(false); + FIELD_TYPE.setDocValuesType(DocValuesType.SORTED_SET); + FIELD_TYPE.freeze(); + + FIELD_TYPE_STORED = new FieldType(FIELD_TYPE); + FIELD_TYPE_STORED.setStored(true); + FIELD_TYPE_STORED.freeze(); + } + + private final StoredValue storedValue; + + /** + * Creates a new KeywordField. + * + * @param name field name + * @param value the BytesRef value + * @param stored whether to store the field + * @throws IllegalArgumentException if the field name or value is null. + */ + public KeywordField(String name, BytesRef value, Store stored) { + super(name, value, stored == Field.Store.YES ? FIELD_TYPE_STORED : FIELD_TYPE); + if (stored == Store.YES) { + storedValue = new StoredValue(value); + } else { + storedValue = null; + } + } + + /** + * Creates a new KeywordField from a String value, by indexing its UTF-8 representation. + * + * @param name field name + * @param value the BytesRef value + * @param stored whether to store the field + * @throws IllegalArgumentException if the field name or value is null. + */ + public KeywordField(String name, String value, Store stored) { + super(name, value, stored == Field.Store.YES ? FIELD_TYPE_STORED : FIELD_TYPE); + if (stored == Store.YES) { + storedValue = new StoredValue(value); + } else { + storedValue = null; + } + } + + @Override + public BytesRef binaryValue() { + BytesRef binaryValue = super.binaryValue(); + if (binaryValue != null) { + return binaryValue; + } else { + return new BytesRef(stringValue()); + } + } + + @Override + public void setStringValue(String value) { + super.setStringValue(value); + if (storedValue != null) { + storedValue.setStringValue(value); + } + } + + @Override + public void setBytesValue(BytesRef value) { + super.setBytesValue(value); + if (storedValue != null) { + storedValue.setBinaryValue(value); + } + } + + @Override + public StoredValue storedValue() { + return storedValue; + } + + /** + * Create a query for matching an exact {@link BytesRef} value. + * + * @param field field name. must not be {@code null}. + * @param value exact value + * @throws NullPointerException if {@code field} is null. + * @return a query matching documents with this exact value + */ + public static Query newExactQuery(String field, BytesRef value) { + Objects.requireNonNull(field, "field must not be null"); + Objects.requireNonNull(value, "value must not be null"); + return new ConstantScoreQuery(new TermQuery(new Term(field, value))); + } + + /** + * Create a query for matching an exact {@link String} value. + * + * @param field field name. must not be {@code null}. + * @param value exact value + * @throws NullPointerException if {@code field} is null. + * @return a query matching documents with this exact value + */ + public static Query newExactQuery(String field, String value) { + Objects.requireNonNull(value, "value must not be null"); + return newExactQuery(field, new BytesRef(value)); + } + + /** + * Create a query for matching any of a set of provided {@link BytesRef} values. + * + * @param field field name. must not be {@code null}. + * @param values the set of values to match + * @throws NullPointerException if {@code field} is null. + * @return a query matching documents with this exact value + */ + public static Query newSetQuery(String field, BytesRef... values) { + Objects.requireNonNull(field, "field must not be null"); + Objects.requireNonNull(values, "values must not be null"); + return new IndexOrDocValuesQuery( + new TermInSetQuery(field, values), new SortedSetDocValuesSetQuery(field, values)); + } + + /** + * Create a new {@link SortField} for {@link BytesRef} values. + * + * @param field field name. must not be {@code null}. + * @param reverse true if natural order should be reversed. + * @param selector custom selector type for choosing the sort value from the set. + */ + public static SortField newSortField( + String field, boolean reverse, SortedSetSelector.Type selector) { + Objects.requireNonNull(field, "field must not be null"); + Objects.requireNonNull(selector, "selector must not be null"); + return new SortedSetSortField(field, reverse, selector); + } +} diff --git a/lucene/core/src/test/org/apache/lucene/document/TestKeywordField.java b/lucene/core/src/test/org/apache/lucene/document/TestKeywordField.java new file mode 100644 index 000000000000..6593a4509c6e --- /dev/null +++ b/lucene/core/src/test/org/apache/lucene/document/TestKeywordField.java @@ -0,0 +1,125 @@ +/* + * 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. + */ +package org.apache.lucene.document; + +import java.io.IOException; +import java.util.Collections; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.LeafReader; +import org.apache.lucene.index.SortedSetDocValues; +import org.apache.lucene.index.TermsEnum; +import org.apache.lucene.store.Directory; +import org.apache.lucene.tests.util.LuceneTestCase; +import org.apache.lucene.util.BytesRef; + +public class TestKeywordField extends LuceneTestCase { + + public void testSetBytesValue() { + Field[] fields = + new Field[] { + new KeywordField("name", newBytesRef("value"), Field.Store.NO), + new KeywordField("name", newBytesRef("value"), Field.Store.YES) + }; + for (Field field : fields) { + assertEquals(newBytesRef("value"), field.binaryValue()); + assertNull(field.stringValue()); + if (field.fieldType().stored()) { + assertEquals(newBytesRef("value"), field.storedValue().getBinaryValue()); + } else { + assertNull(field.storedValue()); + } + field.setBytesValue(newBytesRef("value2")); + assertEquals(newBytesRef("value2"), field.binaryValue()); + assertNull(field.stringValue()); + if (field.fieldType().stored()) { + assertEquals(newBytesRef("value2"), field.storedValue().getBinaryValue()); + } else { + assertNull(field.storedValue()); + } + } + } + + public void testSetStringValue() { + Field[] fields = + new Field[] { + new KeywordField("name", "value", Field.Store.NO), + new KeywordField("name", "value", Field.Store.YES) + }; + for (Field field : fields) { + assertEquals("value", field.stringValue()); + assertEquals(newBytesRef("value"), field.binaryValue()); + if (field.fieldType().stored()) { + assertEquals("value", field.storedValue().getStringValue()); + } else { + assertNull(field.storedValue()); + } + field.setStringValue("value2"); + assertEquals("value2", field.stringValue()); + assertEquals(newBytesRef("value2"), field.binaryValue()); + if (field.fieldType().stored()) { + assertEquals("value2", field.storedValue().getStringValue()); + } else { + assertNull(field.storedValue()); + } + } + } + + public void testIndexBytesValue() throws IOException { + Directory dir = newDirectory(); + IndexWriter w = new IndexWriter(dir, newIndexWriterConfig()); + w.addDocument( + Collections.singleton(new KeywordField("field", newBytesRef("value"), Field.Store.YES))); + IndexReader reader = DirectoryReader.open(w); + w.close(); + LeafReader leaf = getOnlyLeafReader(reader); + TermsEnum terms = leaf.terms("field").iterator(); + assertEquals(new BytesRef("value"), terms.next()); + assertNull(terms.next()); + SortedSetDocValues values = leaf.getSortedSetDocValues("field"); + assertTrue(values.advanceExact(0)); + assertEquals(1, values.docValueCount()); + assertEquals(0L, values.nextOrd()); + assertEquals(new BytesRef("value"), values.lookupOrd(0)); + Document storedDoc = leaf.storedFields().document(0); + assertEquals(new BytesRef("value"), storedDoc.getBinaryValue("field")); + reader.close(); + dir.close(); + } + + public void testIndexStringValue() throws IOException { + Directory dir = newDirectory(); + IndexWriter w = new IndexWriter(dir, newIndexWriterConfig()); + w.addDocument(Collections.singleton(new KeywordField("field", "value", Field.Store.YES))); + IndexReader reader = DirectoryReader.open(w); + w.close(); + LeafReader leaf = getOnlyLeafReader(reader); + TermsEnum terms = leaf.terms("field").iterator(); + assertEquals(new BytesRef("value"), terms.next()); + assertNull(terms.next()); + SortedSetDocValues values = leaf.getSortedSetDocValues("field"); + assertTrue(values.advanceExact(0)); + assertEquals(1, values.docValueCount()); + assertEquals(0L, values.nextOrd()); + assertEquals(new BytesRef("value"), values.lookupOrd(0)); + Document storedDoc = leaf.storedFields().document(0); + assertEquals("value", storedDoc.get("field")); + reader.close(); + dir.close(); + } +} diff --git a/lucene/core/src/test/org/apache/lucene/search/TestSortOptimization.java b/lucene/core/src/test/org/apache/lucene/search/TestSortOptimization.java index 5c0aad74d97f..d30146f39a3c 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestSortOptimization.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestSortOptimization.java @@ -26,15 +26,14 @@ import java.util.List; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; -import org.apache.lucene.document.Field.Store; import org.apache.lucene.document.FloatDocValuesField; import org.apache.lucene.document.FloatPoint; import org.apache.lucene.document.IntPoint; import org.apache.lucene.document.IntRange; +import org.apache.lucene.document.KeywordField; import org.apache.lucene.document.LongField; import org.apache.lucene.document.LongPoint; import org.apache.lucene.document.NumericDocValuesField; -import org.apache.lucene.document.SortedDocValuesField; import org.apache.lucene.document.StoredField; import org.apache.lucene.document.StringField; import org.apache.lucene.index.DirectoryReader; @@ -809,8 +808,8 @@ public void testSortOptimizationOnSortedNumericField() throws IOException { int value = random().nextInt(); int value2 = random().nextInt(); final Document doc = new Document(); - doc.add(new LongField("my_field", value, Store.NO)); - doc.add(new LongField("my_field", value2, Store.NO)); + doc.add(new LongField("my_field", value, Field.Store.NO)); + doc.add(new LongField("my_field", value2, Field.Store.NO)); writer.addDocument(doc); } final IndexReader reader = DirectoryReader.open(writer); @@ -891,8 +890,7 @@ public void testStringSortOptimization() throws IOException { for (int i = 0; i < numDocs; ++i) { final Document doc = new Document(); final BytesRef value = new BytesRef(Integer.toString(random().nextInt(1000))); - doc.add(new StringField("my_field", value, Store.NO)); - doc.add(new SortedDocValuesField("my_field", value)); + doc.add(new KeywordField("my_field", value, Field.Store.NO)); writer.addDocument(doc); if (i % 2000 == 0) writer.flush(); // multiple segments } @@ -916,8 +914,7 @@ public void testStringSortOptimizationWithMissingValues() throws IOException { final Document doc = new Document(); if (random().nextInt(2) == 0) { final BytesRef value = new BytesRef(Integer.toString(random().nextInt(1000))); - doc.add(new StringField("my_field", value, Store.NO)); - doc.add(new SortedDocValuesField("my_field", value)); + doc.add(new KeywordField("my_field", value, Field.Store.NO)); } writer.addDocument(doc); } @@ -936,7 +933,8 @@ private void doTestStringSortOptimization(DirectoryReader reader) throws IOExcep final int numHits = 5; { // simple ascending sort - SortField sortField = new SortField("my_field", SortField.Type.STRING); + SortField sortField = + KeywordField.newSortField("my_field", false, SortedSetSelector.Type.MIN); sortField.setMissingValue(SortField.STRING_LAST); Sort sort = new Sort(sortField); TopDocs topDocs = assertSort(reader, sort, numHits, null); @@ -944,7 +942,7 @@ private void doTestStringSortOptimization(DirectoryReader reader) throws IOExcep } { // simple descending sort - SortField sortField = new SortField("my_field", SortField.Type.STRING, true); + SortField sortField = KeywordField.newSortField("my_field", true, SortedSetSelector.Type.MIN); sortField.setMissingValue(SortField.STRING_FIRST); Sort sort = new Sort(sortField); TopDocs topDocs = assertSort(reader, sort, numHits, null); @@ -952,21 +950,23 @@ private void doTestStringSortOptimization(DirectoryReader reader) throws IOExcep } { // ascending sort that returns missing values first - SortField sortField = new SortField("my_field", SortField.Type.STRING); + SortField sortField = + KeywordField.newSortField("my_field", false, SortedSetSelector.Type.MIN); sortField.setMissingValue(SortField.STRING_FIRST); Sort sort = new Sort(sortField); assertSort(reader, sort, numHits, null); } { // descending sort that returns missing values last - SortField sortField = new SortField("my_field", SortField.Type.STRING, true); + SortField sortField = KeywordField.newSortField("my_field", true, SortedSetSelector.Type.MIN); sortField.setMissingValue(SortField.STRING_LAST); Sort sort = new Sort(sortField); assertSort(reader, sort, numHits, null); } { // paging ascending sort with after - SortField sortField = new SortField("my_field", SortField.Type.STRING); + SortField sortField = + KeywordField.newSortField("my_field", false, SortedSetSelector.Type.MIN); sortField.setMissingValue(SortField.STRING_LAST); Sort sort = new Sort(sortField); BytesRef afterValue = new BytesRef(random().nextBoolean() ? "23" : "230000000"); @@ -976,7 +976,7 @@ private void doTestStringSortOptimization(DirectoryReader reader) throws IOExcep } { // paging descending sort with after - SortField sortField = new SortField("my_field", SortField.Type.STRING, true); + SortField sortField = KeywordField.newSortField("my_field", true, SortedSetSelector.Type.MIN); sortField.setMissingValue(SortField.STRING_FIRST); Sort sort = new Sort(sortField); BytesRef afterValue = new BytesRef(random().nextBoolean() ? "17" : "170000000"); @@ -986,7 +986,8 @@ private void doTestStringSortOptimization(DirectoryReader reader) throws IOExcep } { // paging ascending sort with after that returns missing values first - SortField sortField = new SortField("my_field", SortField.Type.STRING); + SortField sortField = + KeywordField.newSortField("my_field", false, SortedSetSelector.Type.MIN); sortField.setMissingValue(SortField.STRING_FIRST); Sort sort = new Sort(sortField); BytesRef afterValue = new BytesRef(random().nextBoolean() ? "23" : "230000000"); @@ -996,7 +997,7 @@ private void doTestStringSortOptimization(DirectoryReader reader) throws IOExcep } { // paging descending sort with after that returns missing values first - SortField sortField = new SortField("my_field", SortField.Type.STRING, true); + SortField sortField = KeywordField.newSortField("my_field", true, SortedSetSelector.Type.MIN); sortField.setMissingValue(SortField.STRING_LAST); Sort sort = new Sort(sortField); BytesRef afterValue = new BytesRef(random().nextBoolean() ? "17" : "170000000"); @@ -1006,7 +1007,8 @@ private void doTestStringSortOptimization(DirectoryReader reader) throws IOExcep } { // test that if there is the secondary sort on _score, hits are still skipped - SortField sortField = new SortField("my_field", SortField.Type.STRING); + SortField sortField = + KeywordField.newSortField("my_field", false, SortedSetSelector.Type.MIN); sortField.setMissingValue(SortField.STRING_LAST); Sort sort = new Sort(sortField, FIELD_SCORE); TopDocs topDocs = assertSort(reader, sort, numHits, null); @@ -1014,7 +1016,8 @@ private void doTestStringSortOptimization(DirectoryReader reader) throws IOExcep } { // test that if string field is a secondary sort, no optimization is run - SortField sortField = new SortField("my_field", SortField.Type.STRING); + SortField sortField = + KeywordField.newSortField("my_field", false, SortedSetSelector.Type.MIN); sortField.setMissingValue(SortField.STRING_LAST); Sort sort = new Sort(FIELD_SCORE, sortField); TopDocs topDocs = assertSort(reader, sort, numHits, null); @@ -1025,10 +1028,7 @@ private void doTestStringSortOptimization(DirectoryReader reader) throws IOExcep } public void doTestStringSortOptimizationDisabled(DirectoryReader reader) throws IOException { - SortField sortField = - random().nextBoolean() - ? new SortedSetSortField("my_field", false) - : new SortField("my_field", SortField.Type.STRING); + SortField sortField = KeywordField.newSortField("my_field", false, SortedSetSelector.Type.MIN); sortField.setMissingValue(SortField.STRING_LAST); sortField.setOptimizeSortWithIndexedData(false); diff --git a/lucene/core/src/test/org/apache/lucene/search/TestSortedSetSortField.java b/lucene/core/src/test/org/apache/lucene/search/TestSortedSetSortField.java index aad6552ccf83..873d948373f1 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestSortedSetSortField.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestSortedSetSortField.java @@ -18,7 +18,7 @@ import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; -import org.apache.lucene.document.SortedSetDocValuesField; +import org.apache.lucene.document.KeywordField; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.MultiReader; import org.apache.lucene.index.Term; @@ -64,12 +64,12 @@ public void testForward() throws Exception { Directory dir = newDirectory(); RandomIndexWriter writer = new RandomIndexWriter(random(), dir); Document doc = new Document(); - doc.add(new SortedSetDocValuesField("value", newBytesRef("baz"))); + doc.add(new KeywordField("value", newBytesRef("baz"), Field.Store.NO)); doc.add(newStringField("id", "2", Field.Store.YES)); writer.addDocument(doc); doc = new Document(); - doc.add(new SortedSetDocValuesField("value", newBytesRef("foo"))); - doc.add(new SortedSetDocValuesField("value", newBytesRef("bar"))); + doc.add(new KeywordField("value", newBytesRef("foo"), Field.Store.NO)); + doc.add(new KeywordField("value", newBytesRef("bar"), Field.Store.NO)); doc.add(newStringField("id", "1", Field.Store.YES)); writer.addDocument(doc); IndexReader ir = writer.getReader(); @@ -92,12 +92,12 @@ public void testReverse() throws Exception { Directory dir = newDirectory(); RandomIndexWriter writer = new RandomIndexWriter(random(), dir); Document doc = new Document(); - doc.add(new SortedSetDocValuesField("value", newBytesRef("foo"))); - doc.add(new SortedSetDocValuesField("value", newBytesRef("bar"))); + doc.add(new KeywordField("value", newBytesRef("foo"), Field.Store.NO)); + doc.add(new KeywordField("value", newBytesRef("bar"), Field.Store.NO)); doc.add(newStringField("id", "1", Field.Store.YES)); writer.addDocument(doc); doc = new Document(); - doc.add(new SortedSetDocValuesField("value", newBytesRef("baz"))); + doc.add(new KeywordField("value", newBytesRef("baz"), Field.Store.NO)); doc.add(newStringField("id", "2", Field.Store.YES)); writer.addDocument(doc); @@ -121,12 +121,12 @@ public void testMissingFirst() throws Exception { Directory dir = newDirectory(); RandomIndexWriter writer = new RandomIndexWriter(random(), dir); Document doc = new Document(); - doc.add(new SortedSetDocValuesField("value", newBytesRef("baz"))); + doc.add(new KeywordField("value", newBytesRef("baz"), Field.Store.NO)); doc.add(newStringField("id", "2", Field.Store.YES)); writer.addDocument(doc); doc = new Document(); - doc.add(new SortedSetDocValuesField("value", newBytesRef("foo"))); - doc.add(new SortedSetDocValuesField("value", newBytesRef("bar"))); + doc.add(new KeywordField("value", newBytesRef("foo"), Field.Store.NO)); + doc.add(new KeywordField("value", newBytesRef("bar"), Field.Store.NO)); doc.add(newStringField("id", "1", Field.Store.YES)); writer.addDocument(doc); doc = new Document(); @@ -156,12 +156,12 @@ public void testMissingLast() throws Exception { Directory dir = newDirectory(); RandomIndexWriter writer = new RandomIndexWriter(random(), dir); Document doc = new Document(); - doc.add(new SortedSetDocValuesField("value", newBytesRef("baz"))); + doc.add(new KeywordField("value", newBytesRef("baz"), Field.Store.NO)); doc.add(newStringField("id", "2", Field.Store.YES)); writer.addDocument(doc); doc = new Document(); - doc.add(new SortedSetDocValuesField("value", newBytesRef("foo"))); - doc.add(new SortedSetDocValuesField("value", newBytesRef("bar"))); + doc.add(new KeywordField("value", newBytesRef("foo"), Field.Store.NO)); + doc.add(new KeywordField("value", newBytesRef("bar"), Field.Store.NO)); doc.add(newStringField("id", "1", Field.Store.YES)); writer.addDocument(doc); doc = new Document(); @@ -191,11 +191,11 @@ public void testSingleton() throws Exception { Directory dir = newDirectory(); RandomIndexWriter writer = new RandomIndexWriter(random(), dir); Document doc = new Document(); - doc.add(new SortedSetDocValuesField("value", newBytesRef("baz"))); + doc.add(new KeywordField("value", newBytesRef("baz"), Field.Store.NO)); doc.add(newStringField("id", "2", Field.Store.YES)); writer.addDocument(doc); doc = new Document(); - doc.add(new SortedSetDocValuesField("value", newBytesRef("bar"))); + doc.add(new KeywordField("value", newBytesRef("bar"), Field.Store.NO)); doc.add(newStringField("id", "1", Field.Store.YES)); writer.addDocument(doc); IndexReader ir = writer.getReader(); diff --git a/lucene/demo/src/java/org/apache/lucene/demo/IndexFiles.java b/lucene/demo/src/java/org/apache/lucene/demo/IndexFiles.java index 7b172d65d410..9c683d3937c9 100644 --- a/lucene/demo/src/java/org/apache/lucene/demo/IndexFiles.java +++ b/lucene/demo/src/java/org/apache/lucene/demo/IndexFiles.java @@ -34,9 +34,9 @@ import org.apache.lucene.demo.knn.KnnVectorDict; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; +import org.apache.lucene.document.KeywordField; import org.apache.lucene.document.KnnFloatVectorField; import org.apache.lucene.document.LongField; -import org.apache.lucene.document.StringField; import org.apache.lucene.document.TextField; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; @@ -234,8 +234,7 @@ void indexDoc(IndexWriter writer, Path file, long lastModified) throws IOExcepti // field that is indexed (i.e. searchable), but don't tokenize // the field into separate words and don't index term frequency // or positional information: - Field pathField = new StringField("path", file.toString(), Field.Store.YES); - doc.add(pathField); + doc.add(new KeywordField("path", file.toString(), Field.Store.YES)); // Add the last modified date of the file a field named "modified". // Use a LongField that is indexed with points and doc values, and is efficient