Search in sources :

Example 1 with Path

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));
}
Also used : Path(org.immutables.criteria.expression.Path) ParameterizedType(java.lang.reflect.ParameterizedType) Field(java.lang.reflect.Field) ParameterizedType(java.lang.reflect.ParameterizedType) Type(java.lang.reflect.Type) AnnotatedElement(java.lang.reflect.AnnotatedElement) Method(java.lang.reflect.Method)

Example 2 with Path

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);
}
Also used : Document(org.bson.Document) Iterables(com.google.common.collect.Iterables) ReplaceOneModel(com.mongodb.client.model.ReplaceOneModel) Path(org.immutables.criteria.expression.Path) PathNaming(org.immutables.criteria.backend.PathNaming) Projections(com.mongodb.client.model.Projections) StandardOperations(org.immutables.criteria.backend.StandardOperations) ReplaceOptions(com.mongodb.client.model.ReplaceOptions) BsonDocumentWriter(org.bson.BsonDocumentWriter) DefaultResult(org.immutables.criteria.backend.DefaultResult) MongoCollection(com.mongodb.reactivestreams.client.MongoCollection) CodecRegistry(org.bson.codecs.configuration.CodecRegistry) ExpressionNaming(org.immutables.criteria.backend.ExpressionNaming) Backend(org.immutables.criteria.backend.Backend) Function(java.util.function.Function) FlowableTransformer(io.reactivex.FlowableTransformer) BsonDocument(org.bson.BsonDocument) BsonValue(org.bson.BsonValue) Query(org.immutables.criteria.expression.Query) Level(java.util.logging.Level) FullDocument(com.mongodb.client.model.changestream.FullDocument) Bson(org.bson.conversions.Bson) ChangeStreamPublisher(com.mongodb.reactivestreams.client.ChangeStreamPublisher) Flowable(io.reactivex.Flowable) EncoderContext(org.bson.codecs.EncoderContext) WatchEvent(org.immutables.criteria.backend.WatchEvent) ProjectedTuple(org.immutables.criteria.backend.ProjectedTuple) Collation(org.immutables.criteria.expression.Collation) ExpressionConverter(org.immutables.criteria.expression.ExpressionConverter) MongoException(com.mongodb.MongoException) Publisher(org.reactivestreams.Publisher) CodecRegistries(org.bson.codecs.configuration.CodecRegistries) Logger(java.util.logging.Logger) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) KeyExtractor(org.immutables.criteria.backend.KeyExtractor) Visitors(org.immutables.criteria.expression.Visitors) List(java.util.List) Sorts(com.mongodb.client.model.Sorts) BulkWriteResult(com.mongodb.bulk.BulkWriteResult) UniqueCachedNaming(org.immutables.criteria.backend.UniqueCachedNaming) FindPublisher(com.mongodb.reactivestreams.client.FindPublisher) Codec(org.bson.codecs.Codec) WriteResult(org.immutables.criteria.backend.WriteResult) Optional(java.util.Optional) BackendException(org.immutables.criteria.backend.BackendException) MongoCollection(com.mongodb.reactivestreams.client.MongoCollection) PathNaming(org.immutables.criteria.backend.PathNaming)

Example 3 with Path

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());
}
Also used : Path(org.immutables.criteria.expression.Path) Query(org.immutables.criteria.expression.Query) BsonDocument(org.bson.BsonDocument) Optional(java.util.Optional) BsonString(org.bson.BsonString) BsonDocumentReader(org.bson.BsonDocumentReader) ProjectedTuple(org.immutables.criteria.backend.ProjectedTuple) Person(org.immutables.criteria.personmodel.Person) Test(org.junit.jupiter.api.Test)

Example 4 with Path

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));
}
Also used : Operator(org.immutables.criteria.expression.Operator) Path(org.immutables.criteria.expression.Path) Call(org.immutables.criteria.expression.Call) Pattern(java.util.regex.Pattern) IterableOperators(org.immutables.criteria.expression.IterableOperators) StringOperators(org.immutables.criteria.expression.StringOperators) ComparableOperators(org.immutables.criteria.expression.ComparableOperators) Operators(org.immutables.criteria.expression.Operators) OptionalOperators(org.immutables.criteria.expression.OptionalOperators) Constant(org.immutables.criteria.expression.Constant) BsonString(org.bson.BsonString) Bson(org.bson.conversions.Bson) Expression(org.immutables.criteria.expression.Expression)

Example 5 with Path

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);
}
Also used : Path(org.immutables.criteria.expression.Path) Expression(org.immutables.criteria.expression.Expression) ArrayList(java.util.ArrayList)

Aggregations

Path (org.immutables.criteria.expression.Path)15 Expression (org.immutables.criteria.expression.Expression)8 BsonDocument (org.bson.BsonDocument)5 ProjectedTuple (org.immutables.criteria.backend.ProjectedTuple)5 Query (org.immutables.criteria.expression.Query)5 Iterables (com.google.common.collect.Iterables)3 Objects (java.util.Objects)3 Optional (java.util.Optional)3 BsonString (org.bson.BsonString)3 Test (org.junit.jupiter.api.Test)3 ObjectNode (com.fasterxml.jackson.databind.node.ObjectNode)2 MongoException (com.mongodb.MongoException)2 BulkWriteResult (com.mongodb.bulk.BulkWriteResult)2 Projections (com.mongodb.client.model.Projections)2 ReplaceOneModel (com.mongodb.client.model.ReplaceOneModel)2 ReplaceOptions (com.mongodb.client.model.ReplaceOptions)2 Sorts (com.mongodb.client.model.Sorts)2 FullDocument (com.mongodb.client.model.changestream.FullDocument)2 ChangeStreamPublisher (com.mongodb.reactivestreams.client.ChangeStreamPublisher)2 FindPublisher (com.mongodb.reactivestreams.client.FindPublisher)2