private QueryBuilders.QueryBuilder binaryCall(Call call) {
    final List<Expression> arguments = call.arguments();
    Preconditions.checkArgument(arguments.size() == 2, "Size should be 2 for %s but was %s",
            call.operator(), arguments.size());
    final Operator op = call.operator();
    final Path path = Visitors.toPath(arguments.get(0));
    final String field = pathNaming.name(path);
    final Object value = Visitors.toConstant(arguments.get(1)).value();

    if (op == Operators.EQUAL || op == Operators.NOT_EQUAL) {
      QueryBuilders.QueryBuilder builder;
      if (idPredicate.test(path)) {
        // use more efficient ids query for keys
        builder = QueryBuilders.idsQuery(Collections.singleton(value));
      } else {
        builder = QueryBuilders.termQuery(field, value);
      }

      if (op == Operators.NOT_EQUAL) {
        QueryBuilders.BoolQueryBuilder bool = QueryBuilders.boolQuery().mustNot(builder);
        if ("".equals(value)) {
          // string is not empty (should also exists)
          bool = bool.should(QueryBuilders.existsQuery(field));
        }
        builder = bool;
      }

      return builder;
    }

    if (op == Operators.IN || op == Operators.NOT_IN) {
      final Collection<Object> values = ImmutableSet.copyOf(Visitors.toConstant(arguments.get(1)).values());

      QueryBuilders.QueryBuilder builder;
      if (idPredicate.test(path)) {
        // more efficient query by ID
        builder = QueryBuilders.idsQuery(values);
      } else {
        builder = QueryBuilders.termsQuery(field, values);
      }

      if (op == Operators.NOT_IN) {
        builder = QueryBuilders.boolQuery().mustNot(builder);
      }

      return builder;
    }

    if (ComparableOperators.isComparable(op)) {
      final QueryBuilders.RangeQueryBuilder builder = QueryBuilders.rangeQuery(field);

      if (op == ComparableOperators.GREATER_THAN) {
        builder.gt(value);
      } else if (op == ComparableOperators.GREATER_THAN_OR_EQUAL) {
        builder.gte(value);
      } else if (op == ComparableOperators.LESS_THAN) {
        builder.lt(value);
      } else if (op == ComparableOperators.LESS_THAN_OR_EQUAL) {
        builder.lte(value);
      } else {
        throw new UnsupportedOperationException("Unknown comparison " + call);
      }

      return builder;
    }

    if (op == StringOperators.STARTS_WITH) {
      return QueryBuilders.prefixQuery(field, value.toString());
    }

    if (op == StringOperators.MATCHES) {
      Preconditions.checkArgument(value instanceof Pattern, "%s is not regex pattern", value);
      // In elastic / lucene, patterns match the entire string.
      return QueryBuilders.regexpQuery(field, ((Pattern) value).pattern());
    }
    if (op == StringOperators.ENDS_WITH || op == StringOperators.CONTAINS) {
      return QueryBuilders.wildcardQuery(field, "*" + value.toString() + (op == StringOperators.CONTAINS ? "*" : ""));
    }

    throw new UnsupportedOperationException(String.format("Call %s not supported", call));
  }
Evaluating expression into <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html">Elastic Query DSL</a>
criteria/elasticsearch/src/org/immutables/criteria/elasticsearch/ElasticsearchQueryVisitor.java