Skip to content

Commit bdecbb5

Browse files
authored
Factor out sort values from InternalSearchHit (#22080)
This adds fromXContent method and unit test for sort values that are part of InternalSearchHit. In order to centralize serialisation and xContent parsing and rendering code, move all relevant parts to a new class which can be unit tested much better in isolation.This is part of the preparation for parsing search responses on the client side.
1 parent 9b22ec1 commit bdecbb5

File tree

4 files changed

+317
-87
lines changed

4 files changed

+317
-87
lines changed

core/src/main/java/org/elasticsearch/common/xcontent/XContentParserUtils.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.elasticsearch.common.ParsingException;
2323
import org.elasticsearch.common.xcontent.XContentParser.Token;
2424

25+
import java.io.IOException;
2526
import java.util.Locale;
2627
import java.util.function.Supplier;
2728

@@ -34,6 +35,19 @@ public final class XContentParserUtils {
3435
private XContentParserUtils() {
3536
}
3637

38+
/**
39+
* Makes sure that current token is of type {@link XContentParser.Token#FIELD_NAME} and the the field name is equal to the provided one
40+
* @throws ParsingException if the token is not of type {@link XContentParser.Token#FIELD_NAME} or is not equal to the given field name
41+
*/
42+
public static void ensureFieldName(XContentParser parser, Token token, String fieldName) throws IOException {
43+
ensureExpectedToken(Token.FIELD_NAME, token, parser::getTokenLocation);
44+
String currentName = parser.currentName();
45+
if (currentName.equals(fieldName) == false) {
46+
String message = "Failed to parse object: expecting field with name [%s] but found [%s]";
47+
throw new ParsingException(parser.getTokenLocation(), String.format(Locale.ROOT, message, fieldName, currentName));
48+
}
49+
}
50+
3751
/**
3852
* @throws ParsingException with a "unknown field found" reason
3953
*/

core/src/main/java/org/elasticsearch/search/internal/InternalSearchHit.java

Lines changed: 6 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
package org.elasticsearch.search.internal;
2121

2222
import org.apache.lucene.search.Explanation;
23-
import org.apache.lucene.util.BytesRef;
2423
import org.elasticsearch.ElasticsearchParseException;
2524
import org.elasticsearch.common.Nullable;
2625
import org.elasticsearch.common.ParseField;
@@ -48,7 +47,6 @@
4847

4948
import java.io.IOException;
5049
import java.util.ArrayList;
51-
import java.util.Arrays;
5250
import java.util.HashMap;
5351
import java.util.Iterator;
5452
import java.util.List;
@@ -67,8 +65,6 @@
6765

6866
public class InternalSearchHit implements SearchHit {
6967

70-
private static final Object[] EMPTY_SORT_VALUES = new Object[0];
71-
7268
private transient int docId;
7369

7470
private float score = Float.NEGATIVE_INFINITY;
@@ -86,7 +82,7 @@ public class InternalSearchHit implements SearchHit {
8682

8783
private Map<String, HighlightField> highlightFields = null;
8884

89-
private Object[] sortValues = EMPTY_SORT_VALUES;
85+
private SearchSortValues sortValues = SearchSortValues.EMPTY;
9086

9187
private String[] matchedQueries = Strings.EMPTY_ARRAY;
9288

@@ -343,17 +339,12 @@ public void highlightFields(Map<String, HighlightField> highlightFields) {
343339
}
344340

345341
public void sortValues(Object[] sortValues, DocValueFormat[] sortValueFormats) {
346-
this.sortValues = Arrays.copyOf(sortValues, sortValues.length);
347-
for (int i = 0; i < sortValues.length; ++i) {
348-
if (this.sortValues[i] instanceof BytesRef) {
349-
this.sortValues[i] = sortValueFormats[i].format((BytesRef) sortValues[i]);
350-
}
351-
}
342+
this.sortValues = new SearchSortValues(sortValues, sortValueFormats);
352343
}
353344

354345
@Override
355346
public Object[] sortValues() {
356-
return sortValues;
347+
return sortValues.sortValues();
357348
}
358349

359350
@Override
@@ -499,13 +490,7 @@ public XContentBuilder toInnerXContent(XContentBuilder builder, Params params) t
499490
}
500491
builder.endObject();
501492
}
502-
if (sortValues != null && sortValues.length > 0) {
503-
builder.startArray(Fields.SORT);
504-
for (Object sortValue : sortValues) {
505-
builder.value(sortValue);
506-
}
507-
builder.endArray();
508-
}
493+
sortValues.toXContent(builder, params);
509494
if (matchedQueries.length > 0) {
510495
builder.startArray(Fields.MATCHED_QUERIES);
511496
for (String matchedFilter : matchedQueries) {
@@ -603,34 +588,7 @@ public void readFrom(StreamInput in) throws IOException {
603588
this.highlightFields = unmodifiableMap(highlightFields);
604589
}
605590

606-
size = in.readVInt();
607-
if (size > 0) {
608-
sortValues = new Object[size];
609-
for (int i = 0; i < sortValues.length; i++) {
610-
byte type = in.readByte();
611-
if (type == 0) {
612-
sortValues[i] = null;
613-
} else if (type == 1) {
614-
sortValues[i] = in.readString();
615-
} else if (type == 2) {
616-
sortValues[i] = in.readInt();
617-
} else if (type == 3) {
618-
sortValues[i] = in.readLong();
619-
} else if (type == 4) {
620-
sortValues[i] = in.readFloat();
621-
} else if (type == 5) {
622-
sortValues[i] = in.readDouble();
623-
} else if (type == 6) {
624-
sortValues[i] = in.readByte();
625-
} else if (type == 7) {
626-
sortValues[i] = in.readShort();
627-
} else if (type == 8) {
628-
sortValues[i] = in.readBoolean();
629-
} else {
630-
throw new IOException("Can't match type [" + type + "]");
631-
}
632-
}
633-
}
591+
sortValues = new SearchSortValues(in);
634592

635593
size = in.readVInt();
636594
if (size > 0) {
@@ -681,46 +639,7 @@ public void writeTo(StreamOutput out) throws IOException {
681639
highlightField.writeTo(out);
682640
}
683641
}
684-
685-
if (sortValues.length == 0) {
686-
out.writeVInt(0);
687-
} else {
688-
out.writeVInt(sortValues.length);
689-
for (Object sortValue : sortValues) {
690-
if (sortValue == null) {
691-
out.writeByte((byte) 0);
692-
} else {
693-
Class type = sortValue.getClass();
694-
if (type == String.class) {
695-
out.writeByte((byte) 1);
696-
out.writeString((String) sortValue);
697-
} else if (type == Integer.class) {
698-
out.writeByte((byte) 2);
699-
out.writeInt((Integer) sortValue);
700-
} else if (type == Long.class) {
701-
out.writeByte((byte) 3);
702-
out.writeLong((Long) sortValue);
703-
} else if (type == Float.class) {
704-
out.writeByte((byte) 4);
705-
out.writeFloat((Float) sortValue);
706-
} else if (type == Double.class) {
707-
out.writeByte((byte) 5);
708-
out.writeDouble((Double) sortValue);
709-
} else if (type == Byte.class) {
710-
out.writeByte((byte) 6);
711-
out.writeByte((Byte) sortValue);
712-
} else if (type == Short.class) {
713-
out.writeByte((byte) 7);
714-
out.writeShort((Short) sortValue);
715-
} else if (type == Boolean.class) {
716-
out.writeByte((byte) 8);
717-
out.writeBoolean((Boolean) sortValue);
718-
} else {
719-
throw new IOException("Can't handle sort field value of type [" + type + "]");
720-
}
721-
}
722-
}
723-
}
642+
sortValues.writeTo(out);
724643

725644
if (matchedQueries.length == 0) {
726645
out.writeVInt(0);
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.search.internal;
21+
22+
import org.apache.lucene.util.BytesRef;
23+
import org.elasticsearch.common.io.stream.StreamInput;
24+
import org.elasticsearch.common.io.stream.StreamOutput;
25+
import org.elasticsearch.common.io.stream.Writeable;
26+
import org.elasticsearch.common.xcontent.ToXContent;
27+
import org.elasticsearch.common.xcontent.XContentBuilder;
28+
import org.elasticsearch.common.xcontent.XContentParser;
29+
import org.elasticsearch.common.xcontent.XContentParserUtils;
30+
import org.elasticsearch.search.DocValueFormat;
31+
import org.elasticsearch.search.internal.InternalSearchHit.Fields;
32+
33+
import java.io.IOException;
34+
import java.util.Arrays;
35+
import java.util.Objects;
36+
37+
public class SearchSortValues implements ToXContent, Writeable {
38+
39+
static final SearchSortValues EMPTY = new SearchSortValues(new Object[0]);
40+
private final Object[] sortValues;
41+
42+
SearchSortValues(Object[] sortValues) {
43+
this.sortValues = Objects.requireNonNull(sortValues, "sort values must not be empty");
44+
}
45+
46+
public SearchSortValues(Object[] sortValues, DocValueFormat[] sortValueFormats) {
47+
Objects.requireNonNull(sortValues);
48+
Objects.requireNonNull(sortValueFormats);
49+
this.sortValues = Arrays.copyOf(sortValues, sortValues.length);
50+
for (int i = 0; i < sortValues.length; ++i) {
51+
if (this.sortValues[i] instanceof BytesRef) {
52+
this.sortValues[i] = sortValueFormats[i].format((BytesRef) sortValues[i]);
53+
}
54+
}
55+
}
56+
57+
public SearchSortValues(StreamInput in) throws IOException {
58+
int size = in.readVInt();
59+
if (size > 0) {
60+
sortValues = new Object[size];
61+
for (int i = 0; i < sortValues.length; i++) {
62+
byte type = in.readByte();
63+
if (type == 0) {
64+
sortValues[i] = null;
65+
} else if (type == 1) {
66+
sortValues[i] = in.readString();
67+
} else if (type == 2) {
68+
sortValues[i] = in.readInt();
69+
} else if (type == 3) {
70+
sortValues[i] = in.readLong();
71+
} else if (type == 4) {
72+
sortValues[i] = in.readFloat();
73+
} else if (type == 5) {
74+
sortValues[i] = in.readDouble();
75+
} else if (type == 6) {
76+
sortValues[i] = in.readByte();
77+
} else if (type == 7) {
78+
sortValues[i] = in.readShort();
79+
} else if (type == 8) {
80+
sortValues[i] = in.readBoolean();
81+
} else {
82+
throw new IOException("Can't match type [" + type + "]");
83+
}
84+
}
85+
} else {
86+
sortValues = new Object[0];
87+
}
88+
}
89+
90+
@Override
91+
public void writeTo(StreamOutput out) throws IOException {
92+
out.writeVInt(sortValues.length);
93+
for (Object sortValue : sortValues) {
94+
if (sortValue == null) {
95+
out.writeByte((byte) 0);
96+
} else {
97+
Class type = sortValue.getClass();
98+
if (type == String.class) {
99+
out.writeByte((byte) 1);
100+
out.writeString((String) sortValue);
101+
} else if (type == Integer.class) {
102+
out.writeByte((byte) 2);
103+
out.writeInt((Integer) sortValue);
104+
} else if (type == Long.class) {
105+
out.writeByte((byte) 3);
106+
out.writeLong((Long) sortValue);
107+
} else if (type == Float.class) {
108+
out.writeByte((byte) 4);
109+
out.writeFloat((Float) sortValue);
110+
} else if (type == Double.class) {
111+
out.writeByte((byte) 5);
112+
out.writeDouble((Double) sortValue);
113+
} else if (type == Byte.class) {
114+
out.writeByte((byte) 6);
115+
out.writeByte((Byte) sortValue);
116+
} else if (type == Short.class) {
117+
out.writeByte((byte) 7);
118+
out.writeShort((Short) sortValue);
119+
} else if (type == Boolean.class) {
120+
out.writeByte((byte) 8);
121+
out.writeBoolean((Boolean) sortValue);
122+
} else {
123+
throw new IOException("Can't handle sort field value of type [" + type + "]");
124+
}
125+
}
126+
}
127+
}
128+
129+
@Override
130+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
131+
if (sortValues.length > 0) {
132+
builder.startArray(Fields.SORT);
133+
for (Object sortValue : sortValues) {
134+
builder.value(sortValue);
135+
}
136+
builder.endArray();
137+
}
138+
return builder;
139+
}
140+
141+
public static SearchSortValues fromXContent(XContentParser parser) throws IOException {
142+
XContentParserUtils.ensureFieldName(parser, parser.currentToken(), Fields.SORT);
143+
XContentParser.Token token = parser.nextToken();
144+
XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_ARRAY, token, parser::getTokenLocation);
145+
return new SearchSortValues(parser.list().toArray());
146+
}
147+
148+
public Object[] sortValues() {
149+
return sortValues;
150+
}
151+
152+
@Override
153+
public boolean equals(Object obj) {
154+
if (this == obj) {
155+
return true;
156+
}
157+
if (obj == null || getClass() != obj.getClass()) {
158+
return false;
159+
}
160+
SearchSortValues other = (SearchSortValues) obj;
161+
return Arrays.equals(sortValues, other.sortValues);
162+
}
163+
164+
@Override
165+
public int hashCode() {
166+
return Arrays.hashCode(sortValues);
167+
}
168+
}

0 commit comments

Comments
 (0)