/** * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved. * Copyright © 2017-2018 Amdocs * ================================================================================ * Licensed 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. * ============LICENSE_END========================================================= */ package org.onap.aai.sa.searchdbabstraction.searchapi; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; /** * This class represents a simple term query. * *
* A term query takes an operator, a field to apply the query to and a value to match against the query contents. * *
* Valid operators include: *
* *
* // Single Field Match Query: * { * "match": {"field": "searchTags", "value": "abcd"} * } * * // Single Field Not-Match query: * { * "not-match": {"field": "searchTags", "value": "efgh"} * } ** *
* *
* // Multi Field Match Query With A Single Value: * { * "match": {"field": "entityType searchTags", "value": "pserver"} * } * * // Multi Field Match Query With Multiple Values: * { * "match": {"field": "entityType searchTags", "value": "pserver tenant"} * } **/ public class TermQuery { /** * The name of the field to apply the term query to. */ private String field; /** * The value which the field must contain in order to have a match. */ private Object value; /** * For multi field queries only. Determines the rules for whether or not a document matches the query, as follows: * *
* "and" - At least one occurrence of every supplied value must be present in any of the supplied fields. * *
* "or" - At least one occurrence of any of the supplied values must be present in any of the supplied fields.
*/
private String operator;
@JsonProperty("analyzer")
private String searchAnalyzer;
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
private boolean isNumericValue() {
return ((value instanceof Integer) || (value instanceof Double));
}
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator;
}
public String getSearchAnalyzer() {
return searchAnalyzer;
}
public void setSearchAnalyzer(String searchAnalyzer) {
this.searchAnalyzer = searchAnalyzer;
}
/**
* This method returns a string which represents this query in syntax that is understandable by ElasticSearch and is
* suitable for inclusion in an ElasticSearch query string.
*
* @return - ElasticSearch syntax string.
*/
public String toElasticSearch() {
StringBuilder sb = new StringBuilder();
sb.append("{");
// Are we generating a multi field query?
if (isMultiFieldQuery()) {
// For multi field queries, we have to be careful about how we handle
// nested fields, so check to see if any of the specified fields are
// nested.
if (field.contains(".")) {
// Build the equivalent of a multi match query across one or more nested fields.
toElasticSearchNestedMultiMatchQuery(sb);
} else {
// Build a real multi match query, since we don't need to worry about nested fields.
toElasticSearchMultiFieldQuery(sb);
}
} else {
// Single field query.
// Add the necessary wrapping if this is a query against a nested field.
if (fieldIsNested(field)) {
sb.append("{\"nested\": { \"path\": \"").append(pathForNestedField(field)).append("\", \"query\": ");
}
// Build the query.
toElasticSearchSingleFieldQuery(sb);
if (fieldIsNested(field)) {
sb.append("}}");
}
}
sb.append("}");
return sb.toString();
}
/**
* Determines whether or not the client has specified a term query with multiple fields.
*
* @return - true if the query is referencing multiple fields, false, otherwise.
*/
private boolean isMultiFieldQuery() {
return (field.split(" ").length > 1);
}
/**
* Constructs a single field term query in ElasticSearch syntax.
*
* @param sb - The string builder to assemble the query string with.
* @return - The single term query.
*/
private void toElasticSearchSingleFieldQuery(StringBuilder sb) {
sb.append("\"term\": {\"").append(field).append("\" : ");
// For numeric values, don't enclose the value in quotes.
if (!isNumericValue()) {
sb.append("\"").append(value).append("\"");
} else {
sb.append(value);
}
sb.append("}");
}
/**
* Constructs a multi field query in ElasticSearch syntax.
*
* @param sb - The string builder to assemble the query string with.
* @return - The multi field query.
*/
private void toElasticSearchMultiFieldQuery(StringBuilder sb) {
sb.append("\"multi_match\": {");
sb.append("\"query\": \"").append(value).append("\", ");
sb.append("\"type\": \"cross_fields\",");
sb.append("\"fields\": [");
List
* Since ElasticSearch doesn't really let you do that, we have to be clever and construct an equivalent query using
* boolean operators to produce the same result.
*
* @param sb - The string builder to use to build the query.
*/
public void toElasticSearchNestedMultiMatchQuery(StringBuilder sb) {
// Break out our whitespace delimited list of fields and values into a actual lists.
List