mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-02-26 14:54:56 +00:00
Add fromXContent to InternalNestedIdentity
This adds a fromXContent method and unit test to InternalNestedIdentity so we can parse it as part of a search response. This is part of the preparation for parsing search responses on the client side.
This commit is contained in:
parent
db0660a7ea
commit
2592ff86ce
@ -23,16 +23,21 @@ import org.apache.lucene.search.Explanation;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.ParseFieldMatcher;
|
||||
import org.elasticsearch.common.ParseFieldMatcherSupplier;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.compress.CompressorFactory;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Streamable;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.text.Text;
|
||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.search.DocValueFormat;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.elasticsearch.search.SearchHitField;
|
||||
@ -48,12 +53,15 @@ import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static java.util.Collections.unmodifiableMap;
|
||||
import static org.elasticsearch.common.lucene.Lucene.readExplanation;
|
||||
import static org.elasticsearch.common.lucene.Lucene.writeExplanation;
|
||||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
|
||||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
|
||||
import static org.elasticsearch.search.fetch.subphase.highlight.HighlightField.readHighlightField;
|
||||
import static org.elasticsearch.search.internal.InternalSearchHitField.readSearchHitField;
|
||||
|
||||
@ -274,7 +282,6 @@ public class InternalSearchHit implements SearchHit {
|
||||
return sourceAsString();
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@Override
|
||||
public Map<String, Object> sourceAsMap() throws ElasticsearchParseException {
|
||||
if (source == null) {
|
||||
@ -468,7 +475,7 @@ public class InternalSearchHit implements SearchHit {
|
||||
builder.field(Fields._SCORE, score);
|
||||
}
|
||||
for (SearchHitField field : metaFields) {
|
||||
Object value = (Object) field.value();
|
||||
Object value = field.value();
|
||||
builder.field(field.name(), value);
|
||||
}
|
||||
if (source != null) {
|
||||
@ -557,7 +564,7 @@ public class InternalSearchHit implements SearchHit {
|
||||
score = in.readFloat();
|
||||
id = in.readOptionalText();
|
||||
type = in.readOptionalText();
|
||||
nestedIdentity = in.readOptionalStreamable(InternalNestedIdentity::new);
|
||||
nestedIdentity = in.readOptionalWriteable(InternalNestedIdentity::new);
|
||||
version = in.readLong();
|
||||
source = in.readBytesReference();
|
||||
if (source.length() == 0) {
|
||||
@ -649,7 +656,7 @@ public class InternalSearchHit implements SearchHit {
|
||||
out.writeFloat(score);
|
||||
out.writeOptionalText(id);
|
||||
out.writeOptionalText(type);
|
||||
out.writeOptionalStreamable(nestedIdentity);
|
||||
out.writeOptionalWriteable(nestedIdentity);
|
||||
out.writeLong(version);
|
||||
out.writeBytesReference(source);
|
||||
if (explanation == null) {
|
||||
@ -735,7 +742,7 @@ public class InternalSearchHit implements SearchHit {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class InternalNestedIdentity implements NestedIdentity, Streamable, ToXContent {
|
||||
public static final class InternalNestedIdentity implements NestedIdentity, Writeable, ToXContent {
|
||||
|
||||
private Text field;
|
||||
private int offset;
|
||||
@ -747,7 +754,10 @@ public class InternalSearchHit implements SearchHit {
|
||||
this.child = child;
|
||||
}
|
||||
|
||||
InternalNestedIdentity() {
|
||||
InternalNestedIdentity(StreamInput in) throws IOException {
|
||||
field = in.readOptionalText();
|
||||
offset = in.readInt();
|
||||
child = in.readOptionalWriteable(InternalNestedIdentity::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -765,23 +775,25 @@ public class InternalSearchHit implements SearchHit {
|
||||
return child;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
field = in.readOptionalText();
|
||||
offset = in.readInt();
|
||||
child = in.readOptionalStreamable(InternalNestedIdentity::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeOptionalText(field);
|
||||
out.writeInt(offset);
|
||||
out.writeOptionalStreamable(child);
|
||||
out.writeOptionalWriteable(child);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(Fields._NESTED);
|
||||
builder.field(Fields._NESTED);
|
||||
return innerToXContent(builder, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rendering of the inner XContent object without the leading field name. This way the structure innerToXContent renders and
|
||||
* fromXContent parses correspond to each other.
|
||||
*/
|
||||
XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
if (field != null) {
|
||||
builder.field(Fields._NESTED_FIELD, field);
|
||||
}
|
||||
@ -795,12 +807,42 @@ public class InternalSearchHit implements SearchHit {
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static class Fields {
|
||||
private static final ConstructingObjectParser<InternalNestedIdentity, ParseFieldMatcherSupplier> PARSER = new ConstructingObjectParser<>(
|
||||
"nested_identity",
|
||||
ctorArgs -> new InternalNestedIdentity((String) ctorArgs[0], (int) ctorArgs[1], (InternalNestedIdentity) ctorArgs[2]));
|
||||
static {
|
||||
PARSER.declareString(constructorArg(), new ParseField(Fields._NESTED_FIELD));
|
||||
PARSER.declareInt(constructorArg(), new ParseField(Fields._NESTED_OFFSET));
|
||||
PARSER.declareObject(optionalConstructorArg(), PARSER, new ParseField(Fields._NESTED));
|
||||
}
|
||||
|
||||
public static InternalNestedIdentity fromXContent(XContentParser parser) {
|
||||
return PARSER.apply(parser, () -> ParseFieldMatcher.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
InternalNestedIdentity other = (InternalNestedIdentity) obj;
|
||||
return Objects.equals(field, other.field) &&
|
||||
Objects.equals(offset, other.offset) &&
|
||||
Objects.equals(child, other.child);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(field, offset, child);
|
||||
}
|
||||
|
||||
public static class Fields {
|
||||
static final String _NESTED = "_nested";
|
||||
static final String _NESTED_FIELD = "field";
|
||||
static final String _NESTED_OFFSET = "offset";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch 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.elasticsearch.search.internal;
|
||||
|
||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
import org.elasticsearch.search.internal.InternalSearchHit.InternalNestedIdentity;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
|
||||
|
||||
public class InternalNestedIdentityTests extends ESTestCase {
|
||||
|
||||
public static InternalNestedIdentity createTestItem(int depth) {
|
||||
String field = frequently() ? randomAsciiOfLengthBetween(1, 20) : randomRealisticUnicodeOfCodepointLengthBetween(1, 20);
|
||||
int offset = randomInt(10);
|
||||
InternalNestedIdentity child = null;
|
||||
if (depth > 0) {
|
||||
child = createTestItem(depth - 1);
|
||||
}
|
||||
return new InternalNestedIdentity(field, offset, child);
|
||||
}
|
||||
|
||||
public void testFromXContent() throws IOException {
|
||||
InternalNestedIdentity nestedIdentity = createTestItem(randomInt(3));
|
||||
XContentType xcontentType = randomFrom(XContentType.values());
|
||||
XContentBuilder builder = XContentFactory.contentBuilder(xcontentType);
|
||||
if (randomBoolean()) {
|
||||
builder.prettyPrint();
|
||||
}
|
||||
builder = nestedIdentity.innerToXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
XContentParser parser = xcontentType.xContent().createParser(builder.bytes());
|
||||
InternalNestedIdentity parsedNestedIdentity = InternalNestedIdentity.fromXContent(parser);
|
||||
assertEquals(nestedIdentity, parsedNestedIdentity);
|
||||
assertNull(parser.nextToken());
|
||||
}
|
||||
|
||||
public void testToXContent() throws IOException {
|
||||
InternalNestedIdentity nestedIdentity = new InternalNestedIdentity("foo", 5, null);
|
||||
XContentBuilder builder = JsonXContent.contentBuilder();
|
||||
builder.prettyPrint();
|
||||
builder.startObject();
|
||||
nestedIdentity.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.endObject();
|
||||
assertEquals(
|
||||
"{\n" +
|
||||
" \"_nested\" : {\n" +
|
||||
" \"field\" : \"foo\",\n" +
|
||||
" \"offset\" : 5\n" +
|
||||
" }\n" +
|
||||
"}", builder.string());
|
||||
|
||||
nestedIdentity = new InternalNestedIdentity("foo", 5, new InternalNestedIdentity("bar", 3, null));
|
||||
builder = JsonXContent.contentBuilder();
|
||||
builder.prettyPrint();
|
||||
builder.startObject();
|
||||
nestedIdentity.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.endObject();
|
||||
assertEquals(
|
||||
"{\n" +
|
||||
" \"_nested\" : {\n" +
|
||||
" \"field\" : \"foo\",\n" +
|
||||
" \"offset\" : 5,\n" +
|
||||
" \"_nested\" : {\n" +
|
||||
" \"field\" : \"bar\",\n" +
|
||||
" \"offset\" : 3\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}", builder.string());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test equality and hashCode properties
|
||||
*/
|
||||
public void testEqualsAndHashcode() {
|
||||
checkEqualsAndHashCode(createTestItem(randomInt(3)), InternalNestedIdentityTests::copy, InternalNestedIdentityTests::mutate);
|
||||
}
|
||||
|
||||
public void testSerialization() throws IOException {
|
||||
InternalNestedIdentity nestedIdentity = createTestItem(randomInt(3));
|
||||
try (BytesStreamOutput output = new BytesStreamOutput()) {
|
||||
nestedIdentity.writeTo(output);
|
||||
try (StreamInput in = output.bytes().streamInput()) {
|
||||
InternalNestedIdentity deserializedCopy = new InternalNestedIdentity(in);
|
||||
assertEquals(nestedIdentity, deserializedCopy);
|
||||
assertEquals(nestedIdentity.hashCode(), deserializedCopy.hashCode());
|
||||
assertNotSame(nestedIdentity, deserializedCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static InternalNestedIdentity mutate(InternalNestedIdentity original) {
|
||||
if (original == null) {
|
||||
return createTestItem(0);
|
||||
}
|
||||
List<Supplier<InternalNestedIdentity>> mutations = new ArrayList<>();
|
||||
int offset = original.getOffset();
|
||||
InternalNestedIdentity child = (InternalNestedIdentity) original.getChild();
|
||||
String fieldName = original.getField().string();
|
||||
mutations.add(() ->
|
||||
new InternalNestedIdentity(original.getField().string() + "_prefix", offset, child));
|
||||
mutations.add(() -> new InternalNestedIdentity(fieldName, offset + 1, child));
|
||||
mutations.add(() -> new InternalNestedIdentity(fieldName, offset, mutate(child)));
|
||||
return randomFrom(mutations).get();
|
||||
}
|
||||
|
||||
private static InternalNestedIdentity copy(InternalNestedIdentity original) {
|
||||
InternalNestedIdentity child = (InternalNestedIdentity) original.getChild();
|
||||
return new InternalNestedIdentity(original.getField().string(), original.getOffset(), child != null ? copy(child) : null);
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user