use of org.immutables.criteria.expression.Operator in project immutables by immutables.
the class AggregationQuery method accumulator.
private BsonField accumulator(String field, Expression expression) {
Preconditions.checkArgument(expression instanceof Call, "not a call %s", expression);
final Call call = (Call) expression;
final Operator op = call.operator();
Preconditions.checkArgument(AggregationOperators.isAggregation(op), "not an aggregation operator: %s", op);
final String name = "$" + naming.get(extractPath(expression));
if (op == AggregationOperators.AVG) {
return Accumulators.avg(field, name);
} else if (op == AggregationOperators.COUNT) {
return Accumulators.sum(field, 1);
} else if (op == AggregationOperators.MAX) {
return Accumulators.max(field, name);
} else if (op == AggregationOperators.MIN) {
return Accumulators.min(field, name);
} else if (op == AggregationOperators.SUM) {
return Accumulators.sum(field, name);
} else {
throw new IllegalArgumentException(String.format("Unknown aggregation operator %s from %s", op, expression));
}
}
use of org.immutables.criteria.expression.Operator in project immutables by immutables.
the class FindVisitor method visit.
@Override
public Bson visit(Call call) {
final Operator op = call.operator();
final List<Expression> args = call.arguments();
if (op == OptionalOperators.IS_ABSENT || op == OptionalOperators.IS_PRESENT) {
Preconditions.checkArgument(args.size() == 1, "Size should be 1 for %s but was %s", op, args.size());
final String field = naming.name(Visitors.toPath(args.get(0)));
Bson filter;
if (op == OptionalOperators.IS_PRESENT) {
filter = Filters.and(Filters.exists(field), Filters.ne(field, null));
} else {
// absent fields means null or missing
filter = Filters.or(Filters.exists(field, false), Filters.eq(field, null));
}
return filter;
}
if (op == Operators.AND || op == Operators.OR) {
final List<Bson> list = call.arguments().stream().map(a -> a.accept(this)).collect(Collectors.toList());
return op == Operators.AND ? Filters.and(list) : Filters.or(list);
}
if (op == Operators.NOT) {
return negate(args.get(0));
}
if (op == IterableOperators.IS_EMPTY || op == IterableOperators.NOT_EMPTY) {
Preconditions.checkArgument(args.size() == 1, "Size should be 1 for %s but was %s", op, args.size());
final String field = naming.name(Visitors.toPath(args.get(0)));
return op == IterableOperators.IS_EMPTY ? Filters.eq(field, Collections.emptyList()) : Filters.and(Filters.exists(field), Filters.ne(field, null), Filters.ne(field, Collections.emptyList()));
}
if (op.arity() == Operator.Arity.BINARY) {
return binaryCall(call);
}
throw new UnsupportedOperationException(String.format("Not yet supported (%s): %s", call.operator(), call));
}
use of org.immutables.criteria.expression.Operator in project immutables by immutables.
the class FindVisitor method binaryCall.
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));
}
use of org.immutables.criteria.expression.Operator in project immutables by immutables.
the class FindVisitor method negate.
/**
* see https://docs.mongodb.com/manual/reference/operator/query/not/
* NOT operator is mongo is a little specific and can't be applied on all levels
* $not: { $or ... } will not work and should be transformed to $nor
*/
private Bson negate(Expression expression) {
if (!(expression instanceof Call)) {
return Filters.not(expression.accept(this));
}
Call notCall = (Call) expression;
Operator notOperator = notCall.operator();
if (notOperator == Operators.NOT) {
// NOT NOT a == a
return notCall.arguments().get(0).accept(this);
} else if (notOperator == Operators.EQUAL) {
return newCall(notCall, Operators.NOT_EQUAL);
} else if (notOperator == Operators.NOT_EQUAL) {
return newCall(notCall, Operators.EQUAL);
} else if (notOperator == Operators.IN) {
return newCall(notCall, Operators.NOT_IN);
} else if (notOperator == Operators.NOT_IN) {
return newCall(notCall, Operators.IN);
} else if (notOperator == Operators.OR) {
return Filters.nor(notCall.arguments().stream().map(a -> a.accept(this)).collect(Collectors.toList()));
} else if (notOperator == Operators.AND) {
// NOT A and B == (NOT A) or (NOT B)
return Filters.or(notCall.arguments().stream().map(this::negate).collect(Collectors.toList()));
} else if (notOperator == OptionalOperators.IS_ABSENT || notOperator == OptionalOperators.IS_PRESENT) {
Operator newOp = notOperator == OptionalOperators.IS_ABSENT ? OptionalOperators.IS_PRESENT : OptionalOperators.IS_ABSENT;
return newCall(notCall, newOp);
} else if (notOperator == IterableOperators.IS_EMPTY || notOperator == IterableOperators.NOT_EMPTY) {
Operator newOp = notOperator == IterableOperators.IS_EMPTY ? IterableOperators.NOT_EMPTY : IterableOperators.IS_EMPTY;
return newCall(notCall, newOp);
}
// don't really know how to negate here
return Filters.not(notCall.accept(this));
}
use of org.immutables.criteria.expression.Operator in project immutables by immutables.
the class ElasticsearchQueryVisitor method visit.
@Override
public QueryBuilders.QueryBuilder visit(Call call) {
final Operator op = call.operator();
final List<Expression> args = call.arguments();
if (op == OptionalOperators.IS_PRESENT || op == OptionalOperators.IS_ABSENT) {
final String field = pathNaming.name(Visitors.toPath(args.get(0)));
QueryBuilders.QueryBuilder builder = QueryBuilders.existsQuery(field);
if (op == OptionalOperators.IS_ABSENT) {
builder = QueryBuilders.boolQuery().mustNot(builder);
}
return builder;
}
if (op == Operators.AND || op == Operators.OR) {
Preconditions.checkArgument(!args.isEmpty(), "Size should be >=1 for %s but was %s", op, args.size());
final QueryBuilders.BoolQueryBuilder builder = args.stream().map(a -> a.accept(this)).reduce(QueryBuilders.boolQuery(), (a, b) -> op == Operators.AND ? a.must(b) : a.should(b), (a, b) -> b);
return builder;
}
if (op == Operators.NOT) {
Preconditions.checkArgument(args.size() == 1, "Size should be 1 for %s but was %s", op, args.size());
final QueryBuilders.QueryBuilder builder = args.get(0).accept(this);
return QueryBuilders.boolQuery().mustNot(builder);
}
if (op.arity() == Operator.Arity.BINARY) {
return binaryCall(call);
}
throw new UnsupportedOperationException("Don't know how to handle " + call);
}
Aggregations