package io.searchbox.core;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.searchbox.client.JestResult;
import io.searchbox.cloning.CloneUtils;
import io.searchbox.core.search.aggregation.MetricAggregation;
import io.searchbox.core.search.aggregation.RootAggregation;
import java.util.*;
/**
* @author cihat keser
*/
public class SearchResult extends JestResult {
public static final String EXPLANATION_KEY = "_explanation";
public static final String HIGHLIGHT_KEY = "highlight";
public static final String SORT_KEY = "sort";
public static final String[] PATH_TO_TOTAL = "hits/total".split("/");
public static final String[] PATH_TO_MAX_SCORE = "hits/max_score".split("/");
public SearchResult(SearchResult searchResult) {
super(searchResult);
}
public SearchResult(Gson gson) {
super(gson);
}
@Override
@Deprecated
public <T> T getSourceAsObject(Class<T> clazz) {
return super.getSourceAsObject(clazz);
}
@Override
@Deprecated
public <T> List<T> getSourceAsObjectList(Class<T> type) {
return super.getSourceAsObjectList(type);
}
public <T> Hit<T, Void> getFirstHit(Class<T> sourceType) {
return getFirstHit(sourceType, Void.class);
}
public <T, K> Hit<T, K> getFirstHit(Class<T> sourceType, Class<K> explanationType) {
Hit<T, K> hit = null;
List<Hit<T, K>> hits = getHits(sourceType, explanationType, true);
if (!hits.isEmpty()) {
hit = hits.get(0);
}
return hit;
}
public <T> List<Hit<T, Void>> getHits(Class<T> sourceType) {
return getHits(sourceType, Void.class);
}
public <T, K> List<Hit<T, K>> getHits(Class<T> sourceType, Class<K> explanationType) {
return getHits(sourceType, explanationType, false);
}
protected <T, K> List<Hit<T, K>> getHits(Class<T> sourceType, Class<K> explanationType, boolean returnSingle) {
List<Hit<T, K>> sourceList = new ArrayList<Hit<T, K>>();
if (jsonObject != null) {
String[] keys = getKeys();
if (keys != null) { // keys would never be null in a standard search scenario (i.e.: unless search class is overwritten)
String sourceKey = keys[keys.length - 1];
JsonElement obj = jsonObject.get(keys[0]);
for (int i = 1; i < keys.length - 1; i++) {
obj = ((JsonObject) obj).get(keys[i]);
}
if (obj.isJsonObject()) {
sourceList.add(extractHit(sourceType, explanationType, obj, sourceKey));
} else if (obj.isJsonArray()) {
for (JsonElement hitElement : obj.getAsJsonArray()) {
sourceList.add(extractHit(sourceType, explanationType, hitElement, sourceKey));
if (returnSingle) break;
}
}
}
}
return sourceList;
}
protected <T, K> Hit<T, K> extractHit(Class<T> sourceType, Class<K> explanationType, JsonElement hitElement, String sourceKey) {
Hit<T, K> hit = null;
if (hitElement.isJsonObject()) {
JsonObject hitObject = hitElement.getAsJsonObject();
JsonObject source = hitObject.getAsJsonObject(sourceKey);
if (source != null) {
String index = hitObject.get("_index").getAsString();
String type = hitObject.get("_type").getAsString();
String id = hitObject.get("_id").getAsString();
Double score = null;
if (hitObject.has("_score") && !hitObject.get("_score").isJsonNull()) {
score = hitObject.get("_score").getAsDouble();
}
JsonElement explanation = hitObject.get(EXPLANATION_KEY);
Map<String, List<String>> highlight = extractHighlight(hitObject.getAsJsonObject(HIGHLIGHT_KEY));
List<String> sort = extractSort(hitObject.getAsJsonArray(SORT_KEY));
JsonObject clonedSource = null;
for (MetaField metaField : META_FIELDS) {
JsonElement metaElement = hitObject.get(metaField.esFieldName);
if (metaElement != null) {
if (clonedSource == null) {
clonedSource = (JsonObject) CloneUtils.deepClone(source);
}
clonedSource.add(metaField.internalFieldName, metaElement);
}
}
if (clonedSource != null) {
source = clonedSource;
}
hit = new Hit<T, K>(
sourceType,
source,
explanationType,
explanation,
highlight,
sort,
index,
type,
id,
score
);
}
}
return hit;
}
protected List<String> extractSort(JsonArray sort) {
if (sort == null) {
return null;
}
List<String> retval = new ArrayList<String>(sort.size());
for (JsonElement sortValue : sort) {
retval.add(sortValue.isJsonNull() ? "" : sortValue.getAsString());
}
return retval;
}
protected Map<String, List<String>> extractHighlight(JsonObject highlight) {
Map<String, List<String>> retval = null;
if (highlight != null) {
Set<Map.Entry<String, JsonElement>> highlightSet = highlight.entrySet();
retval = new HashMap<String, List<String>>(highlightSet.size());
for (Map.Entry<String, JsonElement> entry : highlightSet) {
List<String> fragments = new ArrayList<String>();
for (JsonElement element : entry.getValue().getAsJsonArray()) {
fragments.add(element.getAsString());
}
retval.put(entry.getKey(), fragments);
}
}
return retval;
}
public Integer getTotal() {
Integer total = null;
JsonElement obj = getPath(PATH_TO_TOTAL);
if (obj != null) total = obj.getAsInt();
return total;
}
public Float getMaxScore() {
Float maxScore = null;
JsonElement obj = getPath(PATH_TO_MAX_SCORE);
if (obj != null) maxScore = obj.getAsFloat();
return maxScore;
}
protected JsonElement getPath(String[] path) {
JsonElement retval = null;
if (jsonObject != null) {
JsonElement obj = jsonObject;
for (String component : path) {
if (obj == null) break;
obj = ((JsonObject) obj).get(component);
}
retval = obj;
}
return retval;
}
public MetricAggregation getAggregations() {
final String rootAggrgationName = "aggs";
if (jsonObject == null) return new RootAggregation(rootAggrgationName, new JsonObject());
if (jsonObject.has("aggregations"))
return new RootAggregation(rootAggrgationName, jsonObject.getAsJsonObject("aggregations"));
if (jsonObject.has("aggs")) return new RootAggregation(rootAggrgationName, jsonObject.getAsJsonObject("aggs"));
return new RootAggregation(rootAggrgationName, new JsonObject());
}
/**
* Immutable class representing a search hit.
*
* @param <T> type of source
* @param <K> type of explanation
* @author cihat keser
*/
public class Hit<T, K> {
public final T source;
public final K explanation;
public final Map<String, List<String>> highlight;
public final List<String> sort;
public final String index;
public final String type;
public final String id;
public final Double score;
public Hit(Class<T> sourceType, JsonElement source) {
this(sourceType, source, null, null);
}
public Hit(Class<T> sourceType, JsonElement source, Class<K> explanationType, JsonElement explanation) {
this(sourceType, source, explanationType, explanation, null, null);
}
public Hit(Class<T> sourceType, JsonElement source, Class<K> explanationType, JsonElement explanation,
Map<String, List<String>> highlight, List<String> sort) {
this(sourceType, source, explanationType, explanation, highlight, sort, null, null, null, null);
}
public Hit(Class<T> sourceType, JsonElement source, Class<K> explanationType, JsonElement explanation,
Map<String, List<String>> highlight, List<String> sort, String index, String type, String id, Double score) {
if (source == null) {
this.source = null;
} else {
this.source = createSourceObject(source, sourceType);
}
if (explanation == null) {
this.explanation = null;
} else {
this.explanation = createSourceObject(explanation, explanationType);
}
this.highlight = highlight;
this.sort = sort;
this.index = index;
this.type = type;
this.id = id;
this.score = score;
}
public Hit(T source) {
this(source, null, null, null);
}
public Hit(T source, K explanation) {
this(source, explanation, null, null);
}
public Hit(T source, K explanation, Map<String, List<String>> highlight, List<String> sort) {
this(source, explanation, highlight, sort, null, null, null, null);
}
public Hit(T source, K explanation, Map<String, List<String>> highlight, List<String> sort, String index, String type, String id, Double score) {
this.source = source;
this.explanation = explanation;
this.highlight = highlight;
this.sort = sort;
this.index = index;
this.type = type;
this.id = id;
this.score = score;
}
@Override
public int hashCode() {
return Objects.hash(
source,
explanation,
highlight,
sort,
index,
type,
id);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (obj.getClass() != getClass()) {
return false;
}
Hit rhs = (Hit) obj;
return Objects.equals(source, rhs.source)
&& Objects.equals(explanation, rhs.explanation)
&& Objects.equals(highlight, rhs.highlight)
&& Objects.equals(sort, rhs.sort)
&& Objects.equals(index, rhs.index)
&& Objects.equals(type, rhs.type)
&& Objects.equals(id, rhs.id);
}
}
}