Skip to content

Commit 6054295

Browse files
committed
Retrieve value from DocValues in a flat_object filed
Signed-off-by: kkewwei <kewei.11@bytedance.com> Signed-off-by: kkewwei <kkewwei@163.com>
1 parent 98dbc4a commit 6054295

File tree

5 files changed

+136
-5
lines changed

5 files changed

+136
-5
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
2121
- Support prefix list for remote repository attributes([#16271](https://github.com/opensearch-project/OpenSearch/pull/16271))
2222
- Add new configuration setting `synonym_analyzer`, to the `synonym` and `synonym_graph` filters, enabling the specification of a custom analyzer for reading the synonym file ([#16488](https://github.com/opensearch-project/OpenSearch/pull/16488)).
2323
- Add stats for remote publication failure and move download failure stats to remote methods([#16682](https://github.com/opensearch-project/OpenSearch/pull/16682/))
24+
- Added ability to retrieve value from DocValues in a flat_object filed([#16802](https://github.com/opensearch-project/OpenSearch/pull/16802))
2425

2526
### Dependencies
2627
- Bump `com.google.cloud:google-cloud-core-http` from 2.23.0 to 2.47.0 ([#16504](https://github.com/opensearch-project/OpenSearch/pull/16504))

rest-api-spec/src/main/resources/rest-api-spec/test/index/92_flat_object_support_doc_values.yml

+49-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
---
22
# The test setup includes:
3-
# - Create flat_object mapping for flat_object_doc_values_test index
4-
# - Index 9 example documents
5-
# - Search tests about doc_values and index
3+
# - 1.Create flat_object mapping for flat_object_doc_values_test index
4+
# - 2.Index 9 example documents
5+
# - 3.Search tests about doc_values and index
6+
# - 4.Fetch doc_value from flat_object field
67

78
setup:
89
- skip:
@@ -786,3 +787,48 @@ teardown:
786787

787788
- length: { hits.hits: 1 }
788789
- match: { hits.hits.0._source.order: "order8" }
790+
791+
# Stored Fields with exact dot path.
792+
- do:
793+
search:
794+
body: {
795+
_source: false,
796+
query: {
797+
bool: {
798+
must: [
799+
{
800+
term: {
801+
order: "order0"
802+
}
803+
}
804+
]
805+
}
806+
},
807+
stored_fields: "_none_",
808+
docvalue_fields: [ "issue.labels.name","order" ]
809+
}
810+
811+
- length: { hits.hits: 1 }
812+
- match: { hits.hits.0.fields: { "order" : [ "order0" ], "issue.labels.name": [ "abc0" ] } }
813+
814+
- do:
815+
search:
816+
body: {
817+
_source: false,
818+
query: {
819+
bool: {
820+
must: [
821+
{
822+
term: {
823+
order: "order0"
824+
}
825+
}
826+
]
827+
}
828+
},
829+
stored_fields: "_none_",
830+
docvalue_fields: [ "issue.labels.name" ]
831+
}
832+
833+
- length: { hits.hits: 1 }
834+
- match: { hits.hits.0.fields: { "issue.labels.name": [ "abc0" ] } }

server/src/main/java/org/opensearch/index/mapper/DocValueFetcher.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import java.util.List;
4444

4545
import static java.util.Collections.emptyList;
46+
import static org.opensearch.index.mapper.FlatObjectFieldMapper.NO_VALUE;
4647

4748
/**
4849
* Value fetcher that loads from doc values.
@@ -70,7 +71,10 @@ public List<Object> fetchValues(SourceLookup lookup) throws IOException {
7071
}
7172
List<Object> result = new ArrayList<Object>(leaf.docValueCount());
7273
for (int i = 0, count = leaf.docValueCount(); i < count; ++i) {
73-
result.add(leaf.nextValue());
74+
Object value = leaf.nextValue();
75+
if (value != NO_VALUE) {
76+
result.add(value);
77+
}
7478
}
7579
return result;
7680
}

server/src/main/java/org/opensearch/index/mapper/FlatObjectFieldMapper.java

+59-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.opensearch.common.unit.Fuzziness;
2929
import org.opensearch.common.xcontent.JsonToStringXContentParser;
3030
import org.opensearch.core.common.ParsingException;
31+
import org.opensearch.core.common.io.stream.StreamOutput;
3132
import org.opensearch.core.xcontent.DeprecationHandler;
3233
import org.opensearch.core.xcontent.NamedXContentRegistry;
3334
import org.opensearch.core.xcontent.XContentParser;
@@ -36,11 +37,13 @@
3637
import org.opensearch.index.fielddata.plain.SortedSetOrdinalsIndexFieldData;
3738
import org.opensearch.index.mapper.KeywordFieldMapper.KeywordFieldType;
3839
import org.opensearch.index.query.QueryShardContext;
40+
import org.opensearch.search.DocValueFormat;
3941
import org.opensearch.search.aggregations.support.CoreValuesSourceType;
4042
import org.opensearch.search.lookup.SearchLookup;
4143

4244
import java.io.IOException;
4345
import java.io.UncheckedIOException;
46+
import java.time.ZoneId;
4447
import java.util.ArrayList;
4548
import java.util.Collections;
4649
import java.util.Iterator;
@@ -63,6 +66,7 @@
6366
public final class FlatObjectFieldMapper extends DynamicKeyFieldMapper {
6467

6568
public static final String CONTENT_TYPE = "flat_object";
69+
public static final String NO_VALUE = new String("");
6670

6771
/**
6872
* In flat_object field mapper, field type is similar to keyword field type
@@ -272,7 +276,7 @@ NamedAnalyzer normalizer() {
272276
@Override
273277
public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName, Supplier<SearchLookup> searchLookup) {
274278
failIfNoDocValues();
275-
return new SortedSetOrdinalsIndexFieldData.Builder(name(), CoreValuesSourceType.BYTES);
279+
return new SortedSetOrdinalsIndexFieldData.Builder(valueFieldType().name(), CoreValuesSourceType.BYTES);
276280
}
277281

278282
@Override
@@ -304,6 +308,30 @@ protected String parseSourceValue(Object value) {
304308
};
305309
}
306310

311+
@Override
312+
public DocValueFormat docValueFormat(@Nullable String format, ZoneId timeZone) {
313+
if (format != null) {
314+
throw new IllegalArgumentException("Field [" + name() + "] of type [" + typeName() + "] does not support custom formats");
315+
}
316+
if (timeZone != null) {
317+
throw new IllegalArgumentException(
318+
"Field [" + name() + "] of type [" + typeName() + "] does not support custom time zones"
319+
);
320+
}
321+
if (mappedFieldTypeName != null) {
322+
return new FlatObjectDocValueFormat(mappedFieldTypeName + DOT_SYMBOL + name() + EQUAL_SYMBOL);
323+
} else {
324+
throw new IllegalArgumentException(
325+
"Field [" + name() + "] of type [" + typeName() + "] does not support doc_value in root field"
326+
);
327+
}
328+
}
329+
330+
@Override
331+
public boolean isAggregatable() {
332+
return false;
333+
}
334+
307335
@Override
308336
public Object valueForDisplay(Object value) {
309337
if (value == null) {
@@ -530,6 +558,36 @@ public Query wildcardQuery(
530558
return valueFieldType().wildcardQuery(rewriteValue(value), method, caseInsensitve, context);
531559
}
532560

561+
public class FlatObjectDocValueFormat implements DocValueFormat {
562+
private static final String NAME = "flat_object";
563+
private final String prefix;
564+
565+
public FlatObjectDocValueFormat(String prefix) {
566+
this.prefix = prefix;
567+
}
568+
569+
@Override
570+
public String getWriteableName() {
571+
return NAME;
572+
}
573+
574+
@Override
575+
public void writeTo(StreamOutput out) {}
576+
577+
@Override
578+
public String format(BytesRef value) {
579+
String parsedValue = inputToString(value);
580+
if (parsedValue.startsWith(prefix) == false) {
581+
return NO_VALUE;
582+
}
583+
return parsedValue.substring(prefix.length());
584+
}
585+
586+
@Override
587+
public BytesRef parseBytesRef(String value) {
588+
return new BytesRef((String) valueFieldType.rewriteForDocValue(rewriteValue(value)));
589+
}
590+
}
533591
}
534592

535593
private final ValueFieldMapper valueFieldMapper;

server/src/test/java/org/opensearch/index/mapper/FlatObjectFieldMapperTests.java

+22
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.opensearch.core.xcontent.ToXContent;
2222
import org.opensearch.core.xcontent.XContentBuilder;
2323
import org.opensearch.index.query.QueryShardContext;
24+
import org.opensearch.search.DocValueFormat;
2425

2526
import java.io.IOException;
2627

@@ -397,6 +398,27 @@ public void testDeduplicationValue() throws IOException {
397398
assertEquals(new BytesRef("field.labels=3"), fieldValueAndPaths[4].binaryValue());
398399
}
399400

401+
public void testFetchDocValues() throws IOException {
402+
MapperService mapperService = createMapperService(fieldMapping(b -> b.field("type", "flat_object")));
403+
{
404+
// test valueWithPathField
405+
MappedFieldType ft = mapperService.fieldType("field.name");
406+
DocValueFormat format = ft.docValueFormat(null, null);
407+
String storedValue = "field.field.name=1234";
408+
409+
Object object = format.format(new BytesRef(storedValue));
410+
assertEquals("1234", object);
411+
}
412+
413+
{
414+
// test valueField
415+
MappedFieldType ft = mapperService.fieldType("field");
416+
Throwable throwable = assertThrows(IllegalArgumentException.class, () -> ft.docValueFormat(null, null));
417+
assertEquals("Field [field] of type [flat_object] does not support doc_value in root field", throwable.getMessage());
418+
}
419+
420+
}
421+
400422
@Override
401423
protected void registerParameters(ParameterChecker checker) throws IOException {
402424
// In the future we will want to make sure parameter updates are covered.

0 commit comments

Comments
 (0)