org.elasticsearch.index.query.InnerHitBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of elasticsearch Show documentation
Show all versions of elasticsearch Show documentation
Elasticsearch subproject :core
/*
* 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.index.query;
import org.elasticsearch.action.support.ToXContentToBytes;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.mapper.DocumentMapper;
import org.elasticsearch.index.mapper.ObjectMapper;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.SearchScript;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder.ScriptField;
import org.elasticsearch.search.fetch.StoredFieldsContext;
import org.elasticsearch.search.fetch.subphase.DocValueFieldsContext;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.fetch.subphase.InnerHitsContext;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.search.sort.SortAndFormats;
import org.elasticsearch.search.sort.SortBuilder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import static org.elasticsearch.common.xcontent.XContentParser.Token.END_OBJECT;
public final class InnerHitBuilder extends ToXContentToBytes implements Writeable {
public static final ParseField NAME_FIELD = new ParseField("name");
public static final ParseField INNER_HITS_FIELD = new ParseField("inner_hits");
public static final QueryBuilder DEFAULT_INNER_HIT_QUERY = new MatchAllQueryBuilder();
private static final ObjectParser PARSER = new ObjectParser<>("inner_hits", InnerHitBuilder::new);
static {
PARSER.declareString(InnerHitBuilder::setName, NAME_FIELD);
PARSER.declareInt(InnerHitBuilder::setFrom, SearchSourceBuilder.FROM_FIELD);
PARSER.declareInt(InnerHitBuilder::setSize, SearchSourceBuilder.SIZE_FIELD);
PARSER.declareBoolean(InnerHitBuilder::setExplain, SearchSourceBuilder.EXPLAIN_FIELD);
PARSER.declareBoolean(InnerHitBuilder::setVersion, SearchSourceBuilder.VERSION_FIELD);
PARSER.declareBoolean(InnerHitBuilder::setTrackScores, SearchSourceBuilder.TRACK_SCORES_FIELD);
PARSER.declareStringArray(InnerHitBuilder::setStoredFieldNames, SearchSourceBuilder.STORED_FIELDS_FIELD);
PARSER.declareField((p, i, c) -> {
throw new ParsingException(p.getTokenLocation(), "The field [" +
SearchSourceBuilder.FIELDS_FIELD + "] is no longer supported, please use [" +
SearchSourceBuilder.STORED_FIELDS_FIELD + "] to retrieve stored fields or _source filtering " +
"if the field is not stored");
}, SearchSourceBuilder.FIELDS_FIELD, ObjectParser.ValueType.STRING_ARRAY);
PARSER.declareStringArray(InnerHitBuilder::setDocValueFields, SearchSourceBuilder.DOCVALUE_FIELDS_FIELD);
PARSER.declareField((p, i, c) -> {
try {
Set scriptFields = new HashSet<>();
for (XContentParser.Token token = p.nextToken(); token != END_OBJECT; token = p.nextToken()) {
scriptFields.add(new ScriptField(c));
}
i.setScriptFields(scriptFields);
} catch (IOException e) {
throw new ParsingException(p.getTokenLocation(), "Could not parse inner script definition", e);
}
}, SearchSourceBuilder.SCRIPT_FIELDS_FIELD, ObjectParser.ValueType.OBJECT);
PARSER.declareField((p, i, c) -> i.setSorts(SortBuilder.fromXContent(c)), SearchSourceBuilder.SORT_FIELD,
ObjectParser.ValueType.OBJECT_ARRAY);
PARSER.declareField((p, i, c) -> {
try {
i.setFetchSourceContext(FetchSourceContext.parse(c.parser()));
} catch (IOException e) {
throw new ParsingException(p.getTokenLocation(), "Could not parse inner _source definition", e);
}
}, SearchSourceBuilder._SOURCE_FIELD, ObjectParser.ValueType.OBJECT_OR_BOOLEAN);
PARSER.declareObject(InnerHitBuilder::setHighlightBuilder, (p, c) -> HighlightBuilder.fromXContent(c),
SearchSourceBuilder.HIGHLIGHT_FIELD);
PARSER.declareObject(InnerHitBuilder::setChildInnerHits, (p, c) -> {
try {
Map innerHitBuilders = new HashMap<>();
String innerHitName = null;
for (XContentParser.Token token = p.nextToken(); token != XContentParser.Token.END_OBJECT; token = p.nextToken()) {
switch (token) {
case START_OBJECT:
InnerHitBuilder innerHitBuilder = InnerHitBuilder.fromXContent(c);
innerHitBuilder.setName(innerHitName);
innerHitBuilders.put(innerHitName, innerHitBuilder);
break;
case FIELD_NAME:
innerHitName = p.currentName();
break;
default:
throw new ParsingException(p.getTokenLocation(), "Expected [" + XContentParser.Token.START_OBJECT + "] in ["
+ p.currentName() + "] but found [" + token + "]", p.getTokenLocation());
}
}
return innerHitBuilders;
} catch (IOException e) {
throw new ParsingException(p.getTokenLocation(), "Could not parse inner query definition", e);
}
}, INNER_HITS_FIELD);
}
private String name;
private String nestedPath;
private String parentChildType;
private int from;
private int size = 3;
private boolean explain;
private boolean version;
private boolean trackScores;
private StoredFieldsContext storedFieldsContext;
private QueryBuilder query = DEFAULT_INNER_HIT_QUERY;
private List> sorts;
private List docValueFields;
private Set scriptFields;
private HighlightBuilder highlightBuilder;
private FetchSourceContext fetchSourceContext;
private Map childInnerHits;
public InnerHitBuilder() {
}
private InnerHitBuilder(InnerHitBuilder other) {
name = other.name;
from = other.from;
size = other.size;
explain = other.explain;
version = other.version;
trackScores = other.trackScores;
if (other.storedFieldsContext != null) {
storedFieldsContext = new StoredFieldsContext(other.storedFieldsContext);
}
if (other.docValueFields != null) {
docValueFields = new ArrayList<> (other.docValueFields);
}
if (other.scriptFields != null) {
scriptFields = new HashSet<> (other.scriptFields);
}
if (other.fetchSourceContext != null) {
fetchSourceContext = new FetchSourceContext(
other.fetchSourceContext.fetchSource(), other.fetchSourceContext.includes(), other.fetchSourceContext.excludes()
);
}
if (other.sorts != null) {
sorts = new ArrayList<>(other.sorts);
}
highlightBuilder = other.highlightBuilder;
if (other.childInnerHits != null) {
childInnerHits = new HashMap<>(other.childInnerHits);
}
}
InnerHitBuilder(InnerHitBuilder other, String nestedPath, QueryBuilder query) {
this(other);
this.query = query;
this.nestedPath = nestedPath;
if (name == null) {
this.name = nestedPath;
}
}
InnerHitBuilder(InnerHitBuilder other, QueryBuilder query, String parentChildType) {
this(other);
this.query = query;
this.parentChildType = parentChildType;
if (name == null) {
this.name = parentChildType;
}
}
/**
* Read from a stream.
*/
public InnerHitBuilder(StreamInput in) throws IOException {
name = in.readOptionalString();
nestedPath = in.readOptionalString();
parentChildType = in.readOptionalString();
from = in.readVInt();
size = in.readVInt();
explain = in.readBoolean();
version = in.readBoolean();
trackScores = in.readBoolean();
storedFieldsContext = in.readOptionalWriteable(StoredFieldsContext::new);
docValueFields = (List) in.readGenericValue();
if (in.readBoolean()) {
int size = in.readVInt();
scriptFields = new HashSet<>(size);
for (int i = 0; i < size; i++) {
scriptFields.add(new ScriptField(in));
}
}
fetchSourceContext = in.readOptionalWriteable(FetchSourceContext::new);
if (in.readBoolean()) {
int size = in.readVInt();
sorts = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
sorts.add(in.readNamedWriteable(SortBuilder.class));
}
}
highlightBuilder = in.readOptionalWriteable(HighlightBuilder::new);
query = in.readNamedWriteable(QueryBuilder.class);
if (in.readBoolean()) {
int size = in.readVInt();
childInnerHits = new HashMap<>(size);
for (int i = 0; i < size; i++) {
childInnerHits.put(in.readString(), new InnerHitBuilder(in));
}
}
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeOptionalString(name);
out.writeOptionalString(nestedPath);
out.writeOptionalString(parentChildType);
out.writeVInt(from);
out.writeVInt(size);
out.writeBoolean(explain);
out.writeBoolean(version);
out.writeBoolean(trackScores);
out.writeOptionalWriteable(storedFieldsContext);
out.writeGenericValue(docValueFields);
boolean hasScriptFields = scriptFields != null;
out.writeBoolean(hasScriptFields);
if (hasScriptFields) {
out.writeVInt(scriptFields.size());
for (ScriptField scriptField : scriptFields) {
scriptField.writeTo(out);
}
}
out.writeOptionalWriteable(fetchSourceContext);
boolean hasSorts = sorts != null;
out.writeBoolean(hasSorts);
if (hasSorts) {
out.writeVInt(sorts.size());
for (SortBuilder sort : sorts) {
out.writeNamedWriteable(sort);
}
}
out.writeOptionalWriteable(highlightBuilder);
out.writeNamedWriteable(query);
boolean hasChildInnerHits = childInnerHits != null;
out.writeBoolean(hasChildInnerHits);
if (hasChildInnerHits) {
out.writeVInt(childInnerHits.size());
for (Map.Entry entry : childInnerHits.entrySet()) {
out.writeString(entry.getKey());
entry.getValue().writeTo(out);
}
}
}
public String getName() {
return name;
}
public InnerHitBuilder setName(String name) {
this.name = Objects.requireNonNull(name);
return this;
}
public int getFrom() {
return from;
}
public InnerHitBuilder setFrom(int from) {
if (from < 0) {
throw new IllegalArgumentException("illegal from value, at least 0 or higher");
}
this.from = from;
return this;
}
public int getSize() {
return size;
}
public InnerHitBuilder setSize(int size) {
if (size < 0) {
throw new IllegalArgumentException("illegal size value, at least 0 or higher");
}
this.size = size;
return this;
}
public boolean isExplain() {
return explain;
}
public InnerHitBuilder setExplain(boolean explain) {
this.explain = explain;
return this;
}
public boolean isVersion() {
return version;
}
public InnerHitBuilder setVersion(boolean version) {
this.version = version;
return this;
}
public boolean isTrackScores() {
return trackScores;
}
public InnerHitBuilder setTrackScores(boolean trackScores) {
this.trackScores = trackScores;
return this;
}
/**
* Gets the stored fields to load and return.
*
* @deprecated Use {@link InnerHitBuilder#getStoredFieldsContext()} instead.
*/
@Deprecated
public List getFieldNames() {
return storedFieldsContext == null ? null : storedFieldsContext.fieldNames();
}
/**
* Sets the stored fields to load and return.
* If none are specified, the source of the document will be returned.
*
* @deprecated Use {@link InnerHitBuilder#setStoredFieldNames(List)} instead.
*/
@Deprecated
public InnerHitBuilder setFieldNames(List fieldNames) {
return setStoredFieldNames(fieldNames);
}
/**
* Gets the stored fields context.
*/
public StoredFieldsContext getStoredFieldsContext() {
return storedFieldsContext;
}
/**
* Sets the stored fields to load and return.
* If none are specified, the source of the document will be returned.
*/
public InnerHitBuilder setStoredFieldNames(List fieldNames) {
if (storedFieldsContext == null) {
storedFieldsContext = StoredFieldsContext.fromList(fieldNames);
} else {
storedFieldsContext.addFieldNames(fieldNames);
}
return this;
}
/**
* Gets the docvalue fields.
*
* @deprecated Use {@link InnerHitBuilder#getDocValueFields()} instead.
*/
@Deprecated
public List getFieldDataFields() {
return docValueFields;
}
/**
* Sets the stored fields to load from the docvalue and return.
*
* @deprecated Use {@link InnerHitBuilder#setDocValueFields(List)} instead.
*/
@Deprecated
public InnerHitBuilder setFieldDataFields(List fieldDataFields) {
this.docValueFields = fieldDataFields;
return this;
}
/**
* Adds a field to load from the docvalue and return.
*
* @deprecated Use {@link InnerHitBuilder#addDocValueField(String)} instead.
*/
@Deprecated
public InnerHitBuilder addFieldDataField(String field) {
if (docValueFields == null) {
docValueFields = new ArrayList<>();
}
docValueFields.add(field);
return this;
}
/**
* Gets the docvalue fields.
*/
public List getDocValueFields() {
return docValueFields;
}
/**
* Sets the stored fields to load from the docvalue and return.
*/
public InnerHitBuilder setDocValueFields(List docValueFields) {
this.docValueFields = docValueFields;
return this;
}
/**
* Adds a field to load from the docvalue and return.
*/
public InnerHitBuilder addDocValueField(String field) {
if (docValueFields == null) {
docValueFields = new ArrayList<>();
}
docValueFields.add(field);
return this;
}
public Set getScriptFields() {
return scriptFields;
}
public InnerHitBuilder setScriptFields(Set scriptFields) {
this.scriptFields = scriptFields;
return this;
}
public InnerHitBuilder addScriptField(String name, Script script) {
if (scriptFields == null) {
scriptFields = new HashSet<>();
}
scriptFields.add(new ScriptField(name, script, false));
return this;
}
public FetchSourceContext getFetchSourceContext() {
return fetchSourceContext;
}
public InnerHitBuilder setFetchSourceContext(FetchSourceContext fetchSourceContext) {
this.fetchSourceContext = fetchSourceContext;
return this;
}
public List> getSorts() {
return sorts;
}
public InnerHitBuilder setSorts(List> sorts) {
this.sorts = sorts;
return this;
}
public InnerHitBuilder addSort(SortBuilder sort) {
if (sorts == null) {
sorts = new ArrayList<>();
}
sorts.add(sort);
return this;
}
public HighlightBuilder getHighlightBuilder() {
return highlightBuilder;
}
public InnerHitBuilder setHighlightBuilder(HighlightBuilder highlightBuilder) {
this.highlightBuilder = highlightBuilder;
return this;
}
QueryBuilder getQuery() {
return query;
}
void setChildInnerHits(Map childInnerHits) {
this.childInnerHits = childInnerHits;
}
String getParentChildType() {
return parentChildType;
}
String getNestedPath() {
return nestedPath;
}
void addChildInnerHit(InnerHitBuilder innerHitBuilder) {
if (childInnerHits == null) {
childInnerHits = new HashMap<>();
}
this.childInnerHits.put(innerHitBuilder.getName(), innerHitBuilder);
}
public InnerHitsContext.BaseInnerHits build(SearchContext parentSearchContext,
InnerHitsContext innerHitsContext) throws IOException {
QueryShardContext queryShardContext = parentSearchContext.getQueryShardContext();
if (nestedPath != null) {
ObjectMapper nestedObjectMapper = queryShardContext.getObjectMapper(nestedPath);
ObjectMapper parentObjectMapper = queryShardContext.nestedScope().nextLevel(nestedObjectMapper);
InnerHitsContext.NestedInnerHits nestedInnerHits = new InnerHitsContext.NestedInnerHits(
name, parentSearchContext, parentObjectMapper, nestedObjectMapper
);
setupInnerHitsContext(queryShardContext, nestedInnerHits);
if (childInnerHits != null) {
buildChildInnerHits(parentSearchContext, nestedInnerHits);
}
queryShardContext.nestedScope().previousLevel();
innerHitsContext.addInnerHitDefinition(nestedInnerHits);
return nestedInnerHits;
} else if (parentChildType != null) {
DocumentMapper documentMapper = queryShardContext.getMapperService().documentMapper(parentChildType);
InnerHitsContext.ParentChildInnerHits parentChildInnerHits = new InnerHitsContext.ParentChildInnerHits(
name, parentSearchContext, queryShardContext.getMapperService(), documentMapper
);
setupInnerHitsContext(queryShardContext, parentChildInnerHits);
if (childInnerHits != null) {
buildChildInnerHits(parentSearchContext, parentChildInnerHits);
}
innerHitsContext.addInnerHitDefinition( parentChildInnerHits);
return parentChildInnerHits;
} else {
throw new IllegalStateException("Neither a nested or parent/child inner hit");
}
}
private void buildChildInnerHits(SearchContext parentSearchContext, InnerHitsContext.BaseInnerHits innerHits) throws IOException {
Map childInnerHits = new HashMap<>();
for (Map.Entry entry : this.childInnerHits.entrySet()) {
InnerHitsContext.BaseInnerHits childInnerHit = entry.getValue().build(
parentSearchContext, new InnerHitsContext()
);
childInnerHits.put(entry.getKey(), childInnerHit);
}
innerHits.setChildInnerHits(childInnerHits);
}
private void setupInnerHitsContext(QueryShardContext context, InnerHitsContext.BaseInnerHits innerHitsContext) throws IOException {
innerHitsContext.from(from);
innerHitsContext.size(size);
innerHitsContext.explain(explain);
innerHitsContext.version(version);
innerHitsContext.trackScores(trackScores);
if (storedFieldsContext != null) {
innerHitsContext.storedFieldsContext(storedFieldsContext);
}
if (docValueFields != null) {
innerHitsContext.docValueFieldsContext(new DocValueFieldsContext(docValueFields));
}
if (scriptFields != null) {
for (ScriptField field : scriptFields) {
SearchScript searchScript = innerHitsContext.getQueryShardContext().getSearchScript(field.script(),
ScriptContext.Standard.SEARCH);
innerHitsContext.scriptFields().add(new org.elasticsearch.search.fetch.subphase.ScriptFieldsContext.ScriptField(
field.fieldName(), searchScript, field.ignoreFailure()));
}
}
if (fetchSourceContext != null) {
innerHitsContext.fetchSourceContext(fetchSourceContext);
}
if (sorts != null) {
Optional optionalSort = SortBuilder.buildSort(sorts, context);
if (optionalSort.isPresent()) {
innerHitsContext.sort(optionalSort.get());
}
}
if (highlightBuilder != null) {
innerHitsContext.highlight(highlightBuilder.build(context));
}
ParsedQuery parsedQuery = new ParsedQuery(query.toQuery(context), context.copyNamedQueries());
innerHitsContext.parsedQuery(parsedQuery);
}
public void inlineInnerHits(Map innerHits) {
InnerHitBuilder copy = new InnerHitBuilder(this);
copy.parentChildType = this.parentChildType;
copy.nestedPath = this.nestedPath;
copy.query = this.query;
innerHits.put(copy.getName(), copy);
Map childInnerHits = new HashMap<>();
extractInnerHits(query, childInnerHits);
if (childInnerHits.size() > 0) {
copy.setChildInnerHits(childInnerHits);
}
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
if (name != null) {
builder.field(NAME_FIELD.getPreferredName(), name);
}
builder.field(SearchSourceBuilder.FROM_FIELD.getPreferredName(), from);
builder.field(SearchSourceBuilder.SIZE_FIELD.getPreferredName(), size);
builder.field(SearchSourceBuilder.VERSION_FIELD.getPreferredName(), version);
builder.field(SearchSourceBuilder.EXPLAIN_FIELD.getPreferredName(), explain);
builder.field(SearchSourceBuilder.TRACK_SCORES_FIELD.getPreferredName(), trackScores);
if (fetchSourceContext != null) {
builder.field(SearchSourceBuilder._SOURCE_FIELD.getPreferredName(), fetchSourceContext, params);
}
if (storedFieldsContext != null) {
storedFieldsContext.toXContent(SearchSourceBuilder.STORED_FIELDS_FIELD.getPreferredName(), builder);
}
if (docValueFields != null) {
builder.startArray(SearchSourceBuilder.DOCVALUE_FIELDS_FIELD.getPreferredName());
for (String fieldDataField : docValueFields) {
builder.value(fieldDataField);
}
builder.endArray();
}
if (scriptFields != null) {
builder.startObject(SearchSourceBuilder.SCRIPT_FIELDS_FIELD.getPreferredName());
for (ScriptField scriptField : scriptFields) {
scriptField.toXContent(builder, params);
}
builder.endObject();
}
if (sorts != null) {
builder.startArray(SearchSourceBuilder.SORT_FIELD.getPreferredName());
for (SortBuilder sort : sorts) {
sort.toXContent(builder, params);
}
builder.endArray();
}
if (highlightBuilder != null) {
builder.field(SearchSourceBuilder.HIGHLIGHT_FIELD.getPreferredName(), highlightBuilder, params);
}
if (childInnerHits != null) {
builder.startObject(INNER_HITS_FIELD.getPreferredName());
for (Map.Entry entry : childInnerHits.entrySet()) {
builder.field(entry.getKey(), entry.getValue(), params);
}
builder.endObject();
}
builder.endObject();
return builder;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
InnerHitBuilder that = (InnerHitBuilder) o;
return Objects.equals(name, that.name) &&
Objects.equals(nestedPath, that.nestedPath) &&
Objects.equals(parentChildType, that.parentChildType) &&
Objects.equals(from, that.from) &&
Objects.equals(size, that.size) &&
Objects.equals(explain, that.explain) &&
Objects.equals(version, that.version) &&
Objects.equals(trackScores, that.trackScores) &&
Objects.equals(storedFieldsContext, that.storedFieldsContext) &&
Objects.equals(docValueFields, that.docValueFields) &&
Objects.equals(scriptFields, that.scriptFields) &&
Objects.equals(fetchSourceContext, that.fetchSourceContext) &&
Objects.equals(sorts, that.sorts) &&
Objects.equals(highlightBuilder, that.highlightBuilder) &&
Objects.equals(query, that.query) &&
Objects.equals(childInnerHits, that.childInnerHits);
}
@Override
public int hashCode() {
return Objects.hash(name, nestedPath, parentChildType, from, size, explain, version, trackScores, storedFieldsContext,
docValueFields, scriptFields, fetchSourceContext, sorts, highlightBuilder, query, childInnerHits);
}
public static InnerHitBuilder fromXContent(QueryParseContext context) throws IOException {
return PARSER.parse(context.parser(), new InnerHitBuilder(), context);
}
public static void extractInnerHits(QueryBuilder query, Map innerHitBuilders) {
if (query instanceof AbstractQueryBuilder) {
((AbstractQueryBuilder) query).extractInnerHitBuilders(innerHitBuilders);
} else {
throw new IllegalStateException("provided query builder [" + query.getClass() +
"] class should inherit from AbstractQueryBuilder, but it doesn't");
}
}
static InnerHitBuilder rewrite(InnerHitBuilder original, QueryBuilder rewrittenQuery) {
if (original == null) {
return null;
}
InnerHitBuilder copy = new InnerHitBuilder(original);
copy.query = rewrittenQuery;
copy.parentChildType = original.parentChildType;
copy.nestedPath = original.nestedPath;
return copy;
}
}