/* * Copyright (C) 2014 Indeed Inc. * * 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. */ package com.indeed.flamdex.query; import com.google.common.base.Joiner; import java.util.Arrays; import java.util.List; /** * @author jsgroth */ public class Query { private final BooleanOp operator; private final List<Query> operands; private final Term startTerm; private final Term endTerm; private final boolean isMaxInclusive; private Query(BooleanOp operator, List<Query> operands) { this.operator = operator; this.operands = operands; startTerm = null; endTerm = null; isMaxInclusive = false; } private Query(Term startTerm) { operator = null; operands = null; this.startTerm = startTerm; endTerm = null; isMaxInclusive = false; } private Query(Term startTerm, Term endTerm, boolean maxInclusive) { operator = null; operands = null; this.startTerm = startTerm; this.endTerm = endTerm; isMaxInclusive = maxInclusive; } public static Query newBooleanQuery(BooleanOp operator, List<Query> operands) { if (operator == null || operands == null || operands.isEmpty()) { throw new IllegalArgumentException("invalid arguments: operator=" + operator + ", operands = "+operands); } if (operator == BooleanOp.NOT && operands.size() > 1) { return new Query(BooleanOp.NOT, Arrays.asList(newBooleanQuery(BooleanOp.OR, operands))); } return new Query(operator, operands); } public static Query newTermQuery(Term term) { if (term == null) { throw new IllegalArgumentException("term cannot be null"); } return new Query(term); } public static Query newRangeQuery(String field, long startTerm, long endTerm, boolean isMaxInclusive) { return newRangeQuery(new Term(field, true, startTerm, ""), new Term(field, true, endTerm, ""), isMaxInclusive); } public static Query newRangeQuery(String field, String startTerm, String endTerm, boolean isMaxInclusive) { return newRangeQuery(new Term(field, false, 0, startTerm), new Term(field, false, 0, endTerm), isMaxInclusive); } public static Query newRangeQuery(Term startTerm, Term endTerm, boolean isMaxInclusive) { if (startTerm == null || endTerm == null) { throw new IllegalArgumentException("term arguments cannot be null"); } if (!startTerm.getFieldName().equals(endTerm.getFieldName())) { throw new IllegalArgumentException("field names do not match"); } if (startTerm.isIntField() != endTerm.isIntField()) { throw new IllegalArgumentException("terms do not have the same field type"); } if (endTerm.getTermIntVal() < startTerm.getTermIntVal()) { throw new IllegalArgumentException("end term must be >= start term"); } return new Query(startTerm, endTerm, isMaxInclusive); } public QueryType getQueryType() { if (operator != null) return QueryType.BOOLEAN; if (endTerm != null) return QueryType.RANGE; return QueryType.TERM; } public BooleanOp getOperator() { return operator; } public List<Query> getOperands() { return operands; } public Term getStartTerm() { return startTerm; } public Term getEndTerm() { return endTerm; } public boolean isMaxInclusive() { return isMaxInclusive; } @Override public String toString() { switch (getQueryType()) { case TERM: return startTerm.toString(); case BOOLEAN: if (operator == BooleanOp.NOT) { return "NOT (" + Joiner.on(", ").join(operands) + ")"; } else { return "(" + Joiner.on(" " + operator.toString() + " ").join(operands) + ")"; } case RANGE: return (startTerm.isIntField() ? "int:" : "str:") + startTerm.getFieldName() + ":[" + (startTerm.isIntField() ? startTerm.getTermIntVal() : startTerm.getTermStringVal()) + " TO " + (endTerm.isIntField() ? endTerm.getTermIntVal() : endTerm.getTermStringVal()) + (isMaxInclusive ? "]" : ")"); default: return super.toString(); } } @Override public boolean equals(Object o) { if (!(o instanceof Query)) return false; Query other = (Query) o; final QueryType queryType = getQueryType(); if (other.getQueryType() != queryType) return false; switch (queryType) { case TERM: return startTerm.equals(other.startTerm); case BOOLEAN: return operator == other.operator && operands.equals(other.operands); case RANGE: return startTerm.equals(other.startTerm) && endTerm.equals(other.endTerm) && isMaxInclusive == other.isMaxInclusive; default: throw new AssertionError("can't happen"); } } @Override public int hashCode() { final QueryType queryType = getQueryType(); int hashCode = queryType.hashCode(); switch (queryType) { case TERM: hashCode *= 31; hashCode += startTerm.hashCode(); break; case BOOLEAN: hashCode *= 31; hashCode += operator.hashCode(); hashCode *= 31; hashCode += operands.hashCode(); break; case RANGE: hashCode *= 31; hashCode += startTerm.hashCode(); hashCode *= 31; hashCode += endTerm.hashCode(); hashCode *= 31; hashCode += isMaxInclusive ? 1231 : 1237; break; default: throw new AssertionError("can't happen"); } return hashCode; } }