org.molgenis.data.QueryRule Maven / Gradle / Ivy
package org.molgenis.data;
import static com.google.common.collect.Streams.stream;
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/** With this class an equation model can be described for a database-field (eg a column). */
@XmlRootElement
public class QueryRule {
/** The operator being applied to the field and value */
@XmlElement protected Operator operator;
/** The field-name (eq column-name) in the database */
@XmlElement protected String field = null;
/** The value to compare entries of the field in the database with */
@XmlElement protected Object value = null;
protected List nestedRules;
public QueryRule() {}
public QueryRule(List nestedRules) {
this.nestedRules = nestedRules;
operator = Operator.NESTED;
}
public QueryRule(QueryRule copy) {
operator = copy.operator;
field = copy.field;
value = copy.value;
}
/** Different types of rules that can be applied. */
public enum Operator {
/** 'field' like 'value', searches all fields if field is not defined */
SEARCH("search"),
/**
* 'field' equal to 'value'
*
* When 'field type' is 'Mref' its results are derived from the 'Contains' behavior.
* Examples:
* 1. ref1 OR ref2 can result in:
*
*
* - re1
*
- ref1, ref2
*
- ref1, ref2, ref3;
*
- ref2
*
- ref2, ref3
*
*
* 2. ref1 AND ref2 can result in:
*
*
* - ref1, ref2
*
- ref1, ref2, ref3
*
*/
EQUALS("="),
/** 'field' in 'value' (value being a list). */
IN("IN"),
/** 'field' less-than 'value' */
LESS("<"),
/** 'field' equal-or-less-than 'value' */
LESS_EQUAL("<="),
/** 'field' greater-than 'value' */
GREATER(">"),
/** 'field' equal-or-greater-than 'value' */
GREATER_EQUAL(">="),
/**
* 'field' equal-or-greater-than 'from value' and equal-or-less-than 'to value' (value being a
* list with 'from value' as first element and 'to value' as second element
*/
RANGE("RANGE"),
/** 'field' like 'value' (works like equals with wildcard before and after value) */
LIKE("LIKE"),
/** 'field' not-equal to 'value' */
NOT("!="),
/** AND operation */
AND("AND"),
/** OR operation */
OR("OR"),
/** indicates that 'value' is a nested array of QueryRule. The parameter 'field' is ommitted. */
NESTED(""),
/** Boolean query */
SHOULD("SHOULD"),
/** Disjunction max query */
DIS_MAX("DIS_MAX"),
/** Fuzzy match operator */
FUZZY_MATCH("FUZZY_MATCH"),
/** Fuzzy match operator */
FUZZY_MATCH_NGRAM("FUZZY_MATCH_NGRAM");
private String label;
/**
* Translate String label of the operator to Operator.
*
* @param label of the operator
*/
Operator(String label) {
this.label = label;
}
/** Get the String label of the Operator. */
@Override
public String toString() {
return label;
}
}
// constructor
/**
* Standard constructor.
*
* With this constructor the field, operator and value are set in one go, so there is no need
* for additional statements.
*
* @param field The field-name.
* @param operator The operator to use for comparing entries in the field with the value.
* @param value The value.
*/
public QueryRule(String field, Operator operator, Object value) {
if (operator == Operator.AND || operator == Operator.OR) {
throw new IllegalArgumentException(
format("QueryRule(): Operator.%s cannot be used with two arguments", operator));
}
this.field = field;
this.operator = operator;
setValue(value);
}
/** Specific constructor for rules that do not apply to a field such as LIMIT and OFFSET. */
@SuppressWarnings("unchecked")
public QueryRule(Operator operator, Object value) {
if (operator == Operator.SEARCH) {
this.operator = operator;
setValue(value);
} else if (Operator.NESTED.equals(operator)) {
boolean okay = true;
if (value instanceof List) {
for (Object o : (List>) value) {
if (!(o instanceof QueryRule)) okay = false;
}
} else {
okay = false;
}
if (!okay)
throw new IllegalArgumentException(
"QueryRule(NESTED, value): value should be List");
this.nestedRules = (List) value;
this.operator = operator;
} else {
throw new IllegalArgumentException(
format("QueryRule(): Operator.%s cannot be used with one argument", operator));
}
}
public QueryRule(Operator operator, QueryRule nestedRules) {
if (operator == Operator.NOT) {
this.operator = operator;
this.nestedRules = Arrays.asList(nestedRules);
} else {
throw new IllegalArgumentException(
format("QueryRule(): Operator.%s cannot be used with one argument", operator));
}
}
/** Specific constructor for rules that don't have a value or field such as LAST */
public QueryRule(Operator operator) {
if (operator == Operator.AND || operator == Operator.OR || operator == Operator.NOT) {
this.operator = operator;
} else {
throw new IllegalArgumentException(
format("QueryRule(): Operator.%s cannot be used without arguments", operator));
}
}
public QueryRule(String field, Operator equals, String value) {
this(field, equals, (Object) value);
}
/**
* Returns the field-name set for this rule.
*
* @return The field-name.
*/
public String getField() {
return field;
}
/**
* Sets a new field-name for this rule.
*
* @param field The new field-name.
*/
public void setField(String field) {
this.field = field;
}
/**
* Returns the operator set for this rule.
*
* @return The operator.
*/
public Operator getOperator() {
return operator;
}
/**
* Sets a new operator for this rule.
*
* @param operator The new operator.
*/
public void setOperator(Operator operator) {
this.operator = operator;
}
/**
* Returns the value set for this rule.
*
* @return The value.
*/
public Object getValue() {
return value;
}
/**
* Sets a new value for this rule.
*
* @param value The new value.
*/
public void setValue(Object value) {
if (value instanceof Iterable>) {
this.value = stream((Iterable>) value).map(this::toValue).collect(toList());
} else {
this.value = toValue(value);
}
}
private Object toValue(Object value) {
if (value instanceof Entity) {
return ((Entity) value).getIdValue();
}
return value;
}
/**
* Convenience function to return value as nested rule array.
*
* @return Nested rule set
*/
public List getNestedRules() {
if (nestedRules == null) {
return Collections.emptyList();
}
return nestedRules;
}
@Override
public String toString() {
StringBuilder strBuilder = new StringBuilder();
fieldToString(strBuilder);
operatorToString(strBuilder);
valueToString(strBuilder);
nestedRulesToString(strBuilder);
return strBuilder.toString();
}
private void fieldToString(StringBuilder strBuilder) {
if (field != null) {
strBuilder.append('\'').append(field).append('\'');
}
}
private void operatorToString(StringBuilder strBuilder) {
if (operator != null && operator != Operator.NESTED) {
if (strBuilder.length() > 0) {
strBuilder.append(' ');
}
strBuilder.append(operator);
}
}
private void valueToString(StringBuilder strBuilder) {
if (operator != Operator.AND
&& operator != Operator.OR
&& operator != Operator.NOT
&& operator != Operator.NESTED
&& operator != Operator.DIS_MAX
&& operator != Operator.SHOULD) {
if (strBuilder.length() > 0) {
strBuilder.append(' ');
}
if (operator != Operator.IN) {
strBuilder.append('\'').append(value).append('\'');
} else {
strBuilder.append(value);
}
}
}
private void nestedRulesToString(StringBuilder strBuilder) {
if (nestedRules != null && !nestedRules.isEmpty()) {
if (strBuilder.length() > 0) {
strBuilder.append(' ');
}
strBuilder.append('(');
for (Iterator it = nestedRules.iterator(); it.hasNext(); ) {
strBuilder.append(it.next());
if (it.hasNext()) {
strBuilder.append(", ");
}
}
strBuilder.append(')');
}
}
public static QueryRule eq(String name, Object value) {
return new QueryRule(name, Operator.EQUALS, value);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof QueryRule)) {
return false;
}
QueryRule queryRule = (QueryRule) o;
return getOperator() == queryRule.getOperator()
&& Objects.equals(getField(), queryRule.getField())
&& Objects.equals(getValue(), queryRule.getValue())
&& Objects.equals(getNestedRules(), queryRule.getNestedRules());
}
@Override
public int hashCode() {
return Objects.hash(getOperator(), getField(), getValue(), getNestedRules());
}
}