package io.searchbox.core;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSyntaxException;
import io.searchbox.action.AbstractAction;
import io.searchbox.action.AbstractMultiTypeActionBuilder;
import io.searchbox.core.search.sort.Sort;
import io.searchbox.params.Parameters;
import io.searchbox.params.SearchType;
/**
* @author Dogukan Sonmez
* @author cihat keser
*/
public class Search extends AbstractAction<SearchResult> {
private String query;
private List<Sort> sortList = new LinkedList<Sort>();
protected List<String> includePatternList = new ArrayList<String>();
protected List<String> excludePatternList = new ArrayList<String>();
protected Search(Builder builder) {
super(builder);
this.query = builder.query;
this.sortList = builder.sortList;
this.includePatternList = builder.includePatternList;
this.excludePatternList = builder.excludePatternList;
setURI(buildURI());
}
protected Search(TemplateBuilder templatedBuilder) {
super(templatedBuilder);
//reuse query as it's just the request body of the POST
this.query = templatedBuilder.query;
this.sortList = templatedBuilder.sortList;
this.includePatternList = templatedBuilder.includePatternList;
this.excludePatternList = templatedBuilder.excludePatternList;
setURI(buildURI() + "/template");
}
@Override
public SearchResult createNewElasticSearchResult(String responseBody, int statusCode, String reasonPhrase, Gson gson) {
return createNewElasticSearchResult(new SearchResult(gson), responseBody, statusCode, reasonPhrase, gson);
}
public String getIndex() {
return this.indexName;
}
public String getType() {
return this.typeName;
}
@Override
protected String buildURI() {
return super.buildURI() + "/_search";
}
@Override
public String getPathToResult() {
return "hits/hits/_source";
}
@Override
public String getRestMethodName() {
return "POST";
}
@Override
public String getData(Gson gson) {
String data;
if (sortList.isEmpty() && includePatternList.isEmpty() && excludePatternList.isEmpty()) {
data = query;
} else {
JsonObject queryObject = gson.fromJson(query, JsonObject.class);
if (queryObject == null) {
queryObject = new JsonObject();
}
if (!sortList.isEmpty()) {
JsonArray sortArray = normalizeSortClause(queryObject);
for (Sort sort : sortList) {
sortArray.add(sort.toJsonObject());
}
}
if (!includePatternList.isEmpty() || !excludePatternList.isEmpty()) {
JsonObject sourceObject = normalizeSourceClause(queryObject);
addPatternListToSource(sourceObject, "includes", includePatternList);
addPatternListToSource(sourceObject, "excludes", excludePatternList);
}
data = gson.toJson(queryObject);
}
return data;
}
private static JsonArray normalizeSortClause(JsonObject queryObject) {
JsonArray sortArray;
if (queryObject.has("sort")) {
JsonElement sortElement = queryObject.get("sort");
if (sortElement.isJsonArray()) {
sortArray = sortElement.getAsJsonArray();
} else if (sortElement.isJsonObject()) {
sortArray = new JsonArray();
sortArray.add(sortElement.getAsJsonObject());
} else if (sortElement.isJsonPrimitive() && sortElement.getAsJsonPrimitive().isString()) {
String sortField = sortElement.getAsString();
sortArray = new JsonArray();
queryObject.add("sort", sortArray);
String order;
if ("_score".equals(sortField)) {
order = "desc";
} else {
order = "asc";
}
JsonObject sortOrder = new JsonObject();
sortOrder.add("order", new JsonPrimitive(order));
JsonObject sortDefinition = new JsonObject();
sortDefinition.add(sortField, sortOrder);
sortArray.add(sortDefinition);
} else {
throw new JsonSyntaxException("_source must be an array, an object or a string");
}
} else {
sortArray = new JsonArray();
}
queryObject.add("sort", sortArray);
return sortArray;
}
private static JsonObject normalizeSourceClause(JsonObject queryObject) {
JsonObject sourceObject;
if (queryObject.has("_source")) {
JsonElement sourceElement = queryObject.get("_source");
if (sourceElement.isJsonObject()) {
sourceObject = sourceElement.getAsJsonObject();
} else if (sourceElement.isJsonArray()) {
// in this case, the values of the array are includes
sourceObject = new JsonObject();
queryObject.add("_source", sourceObject);
sourceObject.add("includes", sourceElement.getAsJsonArray());
} else if (sourceElement.isJsonPrimitive() && sourceElement.getAsJsonPrimitive().isBoolean()) {
// if _source is a boolean, we override the configuration with include/exclude
sourceObject = new JsonObject();
} else {
throw new JsonSyntaxException("_source must be an object, an array or a boolean");
}
} else {
sourceObject = new JsonObject();
}
queryObject.add("_source", sourceObject);
return sourceObject;
}
private static void addPatternListToSource(JsonObject sourceObject, String rule, List<String> patternList) {
if (!patternList.isEmpty()) {
JsonArray ruleArray;
if (sourceObject.has(rule)) {
ruleArray = sourceObject.get(rule).getAsJsonArray();
} else {
ruleArray = new JsonArray();
sourceObject.add(rule, ruleArray);
}
for (String pattern : patternList) {
ruleArray.add(pattern);
}
}
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), query, sortList, includePatternList, excludePatternList);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (obj.getClass() != getClass()) {
return false;
}
Search rhs = (Search) obj;
return super.equals(obj)
&& Objects.equals(query, rhs.query)
&& Objects.equals(sortList, rhs.sortList)
&& Objects.equals(includePatternList, rhs.includePatternList)
&& Objects.equals(excludePatternList, rhs.excludePatternList);
}
public static class Builder extends AbstractMultiTypeActionBuilder<Search, Builder> {
protected String query;
protected List<Sort> sortList = new LinkedList<Sort>();
protected List<String> includePatternList = new ArrayList<String>();
protected List<String> excludePatternList = new ArrayList<String>();
public Builder(String query) {
this.query = query;
}
public Builder setSearchType(SearchType searchType) {
return setParameter(Parameters.SEARCH_TYPE, searchType);
}
public Builder enableTrackScores() {
this.setParameter(Parameters.TRACK_SCORES, true);
return this;
}
public Builder addSort(Sort sort) {
sortList.add(sort);
return this;
}
public Builder addSourceExcludePattern(String excludePattern) {
excludePatternList.add(excludePattern);
return this;
}
public Builder addSourceIncludePattern(String includePattern) {
includePatternList.add(includePattern);
return this;
}
public Builder addSort(Collection<Sort> sorts) {
sortList.addAll(sorts);
return this;
}
@Override
public Search build() {
return new Search(this);
}
}
public static class VersionBuilder extends Builder {
public VersionBuilder(String query) {
super(query);
this.setParameter(Parameters.VERSION, "true");
}
}
public static class TemplateBuilder extends Builder {
public TemplateBuilder(String templatedQuery) {
super(templatedQuery);
}
@Override
public Search build() {
return new Search(this);
}
}
}