private Query toTwoHandSidedQuery(Operation<?> operation, Occur occur, QueryMetadata metadata) {
   Query lhs = toQuery(operation.getArg(0), metadata);
   Query rhs = toQuery(operation.getArg(1), metadata);
   BooleanQuery bq = new BooleanQuery();
   bq.add(createBooleanClause(lhs, occur));
   bq.add(createBooleanClause(rhs, occur));
   return bq;
 }
 private Query toQuery(Operation<?> operation, QueryMetadata metadata) {
   Operator op = operation.getOperator();
   if (op == Ops.OR) {
     return toTwoHandSidedQuery(operation, Occur.SHOULD, metadata);
   } else if (op == Ops.AND) {
     return toTwoHandSidedQuery(operation, Occur.MUST, metadata);
   } else if (op == Ops.NOT) {
     BooleanQuery bq = new BooleanQuery();
     bq.add(new BooleanClause(toQuery(operation.getArg(0), metadata), Occur.MUST_NOT));
     bq.add(new BooleanClause(new MatchAllDocsQuery(), Occur.MUST));
     return bq;
   } else if (op == Ops.LIKE) {
     return like(operation, metadata);
   } else if (op == Ops.LIKE_IC) {
     throw new IgnoreCaseUnsupportedException();
   } else if (op == Ops.EQ) {
     return eq(operation, metadata, false);
   } else if (op == Ops.EQ_IGNORE_CASE) {
     throw new IgnoreCaseUnsupportedException();
   } else if (op == Ops.NE) {
     return ne(operation, metadata, false);
   } else if (op == Ops.STARTS_WITH) {
     return startsWith(metadata, operation, false);
   } else if (op == Ops.STARTS_WITH_IC) {
     throw new IgnoreCaseUnsupportedException();
   } else if (op == Ops.ENDS_WITH) {
     return endsWith(operation, metadata, false);
   } else if (op == Ops.ENDS_WITH_IC) {
     throw new IgnoreCaseUnsupportedException();
   } else if (op == Ops.STRING_CONTAINS) {
     return stringContains(operation, metadata, false);
   } else if (op == Ops.STRING_CONTAINS_IC) {
     throw new IgnoreCaseUnsupportedException();
   } else if (op == Ops.BETWEEN) {
     return between(operation, metadata);
   } else if (op == Ops.IN) {
     return in(operation, metadata, false);
   } else if (op == Ops.NOT_IN) {
     return notIn(operation, metadata, false);
   } else if (op == Ops.LT) {
     return lt(operation, metadata);
   } else if (op == Ops.GT) {
     return gt(operation, metadata);
   } else if (op == Ops.LOE) {
     return le(operation, metadata);
   } else if (op == Ops.GOE) {
     return ge(operation, metadata);
   } else if (op == LuceneOps.LUCENE_QUERY) {
     @SuppressWarnings("unchecked") // This is the expected type
     Query rv = ((Constant<Query>) operation.getArg(0)).getConstant();
     return rv;
   }
   throw new UnsupportedOperationException("Illegal operation " + operation);
 }
 protected Query like(Operation<?> operation, QueryMetadata metadata) {
   verifyArguments(operation);
   Path<?> path = getPath(operation.getArg(0));
   String field = toField(path);
   String[] terms = convert(path, operation.getArg(1));
   if (terms.length > 1) {
     BooleanQuery bq = new BooleanQuery();
     for (String s : terms) {
       bq.add(new WildcardQuery(new Term(field, "*" + s + "*")), Occur.MUST);
     }
     return bq;
   }
   return new WildcardQuery(new Term(field, terms[0]));
 }
 protected Query endsWith(Operation<?> operation, QueryMetadata metadata, boolean ignoreCase) {
   verifyArguments(operation);
   Path<?> path = getPath(operation.getArg(0));
   String field = toField(path);
   String[] terms = convertEscaped(path, operation.getArg(1), metadata);
   if (terms.length > 1) {
     BooleanQuery bq = new BooleanQuery();
     for (int i = 0; i < terms.length; ++i) {
       String s = i == terms.length - 1 ? "*" + terms[i] : "*" + terms[i] + "*";
       bq.add(new WildcardQuery(new Term(field, s)), Occur.MUST);
     }
     return bq;
   }
   return new WildcardQuery(new Term(field, "*" + terms[0]));
 }
 protected Query in(Operation<?> operation, QueryMetadata metadata, boolean ignoreCase) {
   Path<?> path = getPath(operation.getArg(0));
   String field = toField(path);
   @SuppressWarnings("unchecked") // This is the second argument type
   Constant<Collection<?>> collConstant = (Constant<Collection<?>>) operation.getArg(1);
   Collection<?> values = collConstant.getConstant();
   BooleanQuery bq = new BooleanQuery();
   if (Number.class.isAssignableFrom(path.getType())) {
     for (Object value : values) {
       TermQuery eq = new TermQuery(new Term(field, convertNumber((Number) value)));
       bq.add(eq, Occur.SHOULD);
     }
   } else {
     for (Object value : values) {
       String[] str = convert(path, value);
       bq.add(eq(field, str, ignoreCase), Occur.SHOULD);
     }
   }
   return bq;
 }
 protected Query ne(Operation<?> operation, QueryMetadata metadata, boolean ignoreCase) {
   BooleanQuery bq = new BooleanQuery();
   bq.add(new BooleanClause(eq(operation, metadata, ignoreCase), Occur.MUST_NOT));
   bq.add(new BooleanClause(new MatchAllDocsQuery(), Occur.MUST));
   return bq;
 }