private Bson binaryCall(Call call) {
    Preconditions.checkArgument(call.operator().arity() == Operator.Arity.BINARY, "%s is not binary", call.operator());
    final Operator op = call.operator();
    Expression left = call.arguments().get(0);
    Expression right = call.arguments().get(1);

    if (!(left instanceof Path && right instanceof Constant)) {
      // special case when $expr has to be used
      return call.accept(new MongoExpr(naming, codecRegistry)).asDocument();
    }

    final String field = naming.name(Visitors.toPath(left));
    final Object value = Visitors.toConstant(right).value();
    if (op == Operators.EQUAL || op == Operators.NOT_EQUAL) {
      if ("".equals(value) && op == Operators.NOT_EQUAL) {
        // special case for empty string. string != "" should not return missing strings
        return Filters.and(Filters.nin(field, value, null), Filters.exists(field));
      }
      return op == Operators.EQUAL ? Filters.eq(field, value) : Filters.ne(field, value);
    }

    if (ComparableOperators.isComparable(op)) {
      if (op == ComparableOperators.GREATER_THAN) {
        return Filters.gt(field, value);
      } else if (op == ComparableOperators.GREATER_THAN_OR_EQUAL) {
        return Filters.gte(field, value);
      } else if (op == ComparableOperators.LESS_THAN) {
        return Filters.lt(field, value);
      } else if (op == ComparableOperators.LESS_THAN_OR_EQUAL) {
        return Filters.lte(field, value);
      }

      throw new UnsupportedOperationException("Unknown comparison " + call);
    }

    if (op == Operators.IN || op == Operators.NOT_IN) {
      final Collection<Object> values = ImmutableSet.copyOf(Visitors.toConstant(right).values());
      Preconditions.checkNotNull(values, "not expected to be null for %s", op);
      if (values.size() == 1) {
        // optimization: convert IN, NIN (where argument is a list with single element) into EQ / NE
        Operators newOperator = op == Operators.IN ? Operators.EQUAL : Operators.NOT_EQUAL;
        Call newCall = Expressions.binaryCall(newOperator, left, Expressions.constant(values.iterator().next()));
        return binaryCall(newCall);
      }
      return op == Operators.IN ? Filters.in(field, values) : Filters.nin(field, values);
    }

    if (op == StringOperators.MATCHES || op == StringOperators.CONTAINS) {
      Object newValue = value;
      if (op == StringOperators.CONTAINS) {
        // handle special case for string contains with regexp
        newValue = Pattern.compile(".*" + Pattern.quote(value.toString()) + ".*");
      }
      Preconditions.checkArgument(newValue instanceof Pattern, "%s is not regex pattern", value);
      return Filters.regex(field, (Pattern) newValue);
    }

    if (op == IterableOperators.HAS_SIZE) {
      Preconditions.checkArgument(value instanceof Number, "%s is not a number", value);
      int size = ((Number) value).intValue();
      return Filters.size(field, size);
    }

    if (op == IterableOperators.CONTAINS) {
      return Filters.eq(field, value);
    }

    if (op == StringOperators.HAS_LENGTH) {
      Preconditions.checkArgument(value instanceof Number, "%s is not a number", value);
      final int length = ((Number) value).intValue();
      // use strLenCP function
      // https://docs.mongodb.com/manual/reference/operator/aggregation/strLenCP/#exp._S_strLenCP
      final Bson lengthExpr  = Document.parse(String.format("{$expr:{$eq:[{$strLenCP: \"$%s\"}, %d]}}}", field, length));
      // field should exists and not be null
      return Filters.and(Filters.exists(field), Filters.ne(field, null), lengthExpr);
    }

    if (op == StringOperators.STARTS_WITH || op == StringOperators.ENDS_WITH) {
      // regular expression
      final String pattern = String.format("%s%s%s",
              op == StringOperators.STARTS_WITH ? "^": "",
              Pattern.quote(value.toString()),
              op == StringOperators.ENDS_WITH ? "$" : "");
      return Filters.regex(field, Pattern.compile(pattern));
    }


    throw new UnsupportedOperationException(String.format("Unsupported binary call %s", call));
  }
Converts find expression to Mongo BSON format
@see <a href="https://docs.mongodb.com/manual/tutorial/query-documents/">Query Documents</a>
criteria/mongo/src/org/immutables/criteria/mongo/FindVisitor.java