use of org.immutables.criteria.expression.Path in project immutables by immutables.
the class ReturnTypeTest method projectionTypeInternal.
/**
* Get compile-time version of projection. P parameter for {@code Projection<P>}
*/
private static Type projectionTypeInternal(Projection<?> projection) throws ClassNotFoundException, NoSuchFieldException {
Objects.requireNonNull(projection, "projection");
final Path path = (Path) Matchers.toExpression(projection);
AnnotatedElement element = path.element();
final Class<?> declaringClass;
if (element instanceof Field) {
declaringClass = ((Field) element).getDeclaringClass();
} else if (element instanceof Method) {
declaringClass = ((Method) element).getDeclaringClass();
} else {
throw new IllegalArgumentException(String.format("Expected %s to be either field or method but was %s", path, element.getClass().getName()));
}
// manually get criteria class
Class<?> criteriaClass = Class.forName(declaringClass.getPackage().getName() + "." + declaringClass.getSimpleName() + "Criteria");
Field field = criteriaClass.getField(path.toStringPath());
for (Type type : field.getType().getGenericInterfaces()) {
if (type instanceof ParameterizedType && ((ParameterizedType) type).getRawType() == Projection.class) {
// resolve Projection<P>
Type resolved = TypeToken.of(field.getGenericType()).resolveType(type).getType();
return ((ParameterizedType) resolved).getActualTypeArguments()[0];
}
}
throw new IllegalArgumentException(String.format("Couldn't resolve type variable (%s) for %s %s", Projection.class.getSimpleName(), path, element));
}
use of org.immutables.criteria.expression.Path in project immutables by immutables.
the class MongoSession method watch.
private <X> Publisher<WatchEvent<X>> watch(StandardOperations.Watch operation) {
final MongoCollection<X> collection = (MongoCollection<X>) this.collection;
if (operation.query().hasProjections()) {
return Flowable.error(new UnsupportedOperationException("Projections are not yet supported with watch operation"));
}
ChangeStreamPublisher<X> watch;
if (!operation.query().filter().isPresent()) {
// watch without filter
watch = collection.watch(collection.getDocumentClass());
} else {
// prefix all attributes with 'fullDocument.'
PathNaming naming = path -> "fullDocument." + this.pathNaming.name(path);
// reuse aggregation pipeline
AggregationQuery agg = new AggregationQuery(operation.query(), naming);
watch = collection.watch(agg.toPipeline(), collection.getDocumentClass());
}
return Flowable.fromPublisher(watch.fullDocument(FullDocument.UPDATE_LOOKUP)).map(MongoWatchEvent::fromChangeStream);
}
use of org.immutables.criteria.expression.Path in project immutables by immutables.
the class TupleCodecProviderTest method optionalAttribute_nickname.
/**
* Projection of an optional attribute
*/
@Test
@SuppressWarnings("unchecked")
public void optionalAttribute_nickname() {
Query query = Query.of(Person.class).addProjections(Matchers.toExpression(PersonCriteria.person.nickName));
Path idPath = Visitors.toPath(KeyExtractor.defaultFactory().create(Person.class).metadata().keys().get(0));
TupleCodecProvider provider = new TupleCodecProvider(query, new MongoPathNaming(idPath, PathNaming.defaultNaming()).toExpression());
Codec<ProjectedTuple> codec = provider.get(ProjectedTuple.class, registry);
ProjectedTuple tuple1 = codec.decode(new BsonDocumentReader(new BsonDocument("nickName", new BsonString("aaa"))), DecoderContext.builder().build());
check(tuple1.values()).hasSize(1);
check((Optional<String>) tuple1.values().get(0)).is(Optional.of("aaa"));
ProjectedTuple tuple2 = codec.decode(new BsonDocumentReader(new BsonDocument()), DecoderContext.builder().build());
check(tuple2.values()).hasSize(1);
check((Optional<String>) tuple2.values().get(0)).is(Optional.empty());
}
use of org.immutables.criteria.expression.Path 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.Path in project immutables by immutables.
the class TupleExtractor method extract.
ProjectedTuple extract(Object instance) {
List<Object> values = new ArrayList<>();
for (Expression expr : query.projections()) {
Path path = (Path) expr;
Object value = pathExtractor.extract(path, instance);
value = maybeWrapOptional(value, path);
values.add(value);
}
return ProjectedTuple.of(query.projections(), values);
}
Aggregations