All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sirius.db.mongo.Finder Maven / Gradle / Ivy

Go to download

Provides a modern and highly flexible ORM and lightweight connectivity for JDBC, MongoDB, Redis, Elasticsearch.

There is a newer version: 7.4
Show newest version
/*
 * Made with all the love in the world
 * by scireum in Remshalden, Germany
 *
 * Copyright by scireum GmbH
 * http://www.scireum.de - [email protected]
 */

package sirius.db.mongo;

import com.google.common.collect.ImmutableList;
import com.mongodb.BasicDBObject;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCursor;
import org.bson.Document;
import sirius.db.mixing.Mapping;
import sirius.kernel.async.TaskContext;
import sirius.kernel.commons.Monoflop;
import sirius.kernel.commons.Value;
import sirius.kernel.commons.Watch;
import sirius.kernel.health.Microtiming;

import javax.annotation.Nonnull;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * Fluent builder to build a find statement.
 */
public class Finder extends QueryBuilder {

    private static final String KEY_MONGO = "mongo";
    private Document fields;
    private Document orderBy;
    private int skip;
    private int limit;
    private int batchSize;

    protected Finder(Mongo mongo) {
        super(mongo);
    }

    /**
     * Limits the fields being returned to the given list.
     *
     * @param fieldsToReturn specified the list of fields to return
     * @return the builder itself for fluent method calls
     */
    public Finder selectFields(Mapping... fieldsToReturn) {
        fields = new Document();
        for (Mapping field : fieldsToReturn) {
            fields.put(field.toString(), 1);
        }

        return this;
    }

    /**
     * Limits the fields being returned to the given list.
     *
     * @param fieldsToReturn specified the list of fields to return
     * @return the builder itself for fluent method calls
     */
    public Finder selectFields(String... fieldsToReturn) {
        fields = new Document();
        for (String field : fieldsToReturn) {
            fields.put(field, 1);
        }

        return this;
    }

    /**
     * Adds a sort constraint to order by the given field ascending.
     *
     * @param field the field to order by.
     * @return the builder itself for fluent method calls
     */
    public Finder orderByAsc(Mapping field) {
        return orderByAsc(field.toString());
    }

    /**
     * Adds a sort constraint to order by the given field ascending.
     *
     * @param field the field to order by.
     * @return the builder itself for fluent method calls
     */
    public Finder orderByAsc(String field) {
        if (orderBy == null) {
            orderBy = new Document();
        }
        orderBy.put(field, 1);

        return this;
    }

    /**
     * Adds a sort constraint to order by the given field descending.
     *
     * @param field the field to order by.
     * @return the builder itself for fluent method calls
     */
    public Finder orderByDesc(Mapping field) {
        return orderByDesc(field.toString());
    }

    /**
     * Adds a sort constraint to order by the given field descending.
     *
     * @param field the field to order by.
     * @return the builder itself for fluent method calls
     */
    public Finder orderByDesc(String field) {
        if (orderBy == null) {
            orderBy = new Document();
        }
        orderBy.put(field, -1);

        return this;
    }

    /**
     * Adds a limit to the query.
     *
     * @param skip  the number of items to skip (used for pagination).
     * @param limit the max. number of items to return (exluding those who have been skipped).
     * @return the builder itself for fluent method calls
     */
    public Finder limit(int skip, int limit) {
        this.skip = skip;
        this.limit = limit;

        return this;
    }

    /**
     * Adds a limit to the query.
     *
     * @param limit the max. number of items to return
     * @return the builder itself for fluent method calls
     */
    public Finder limit(int limit) {
        this.limit = limit;

        return this;
    }

    /**
     * Specifies the number of items to skip before items are added to the result.
     *
     * @param skip the number of items to skip. Values <= 0 are ignored.
     * @return the query itself for fluent method calls
     */
    public Finder skip(int skip) {
        this.skip = skip;

        return this;
    }

    /**
     * Specifies the number of items per batch. This has no effect on the result. Low batchSizes can be used
     * to prevent cursor timeouts when using a time consuming processor, but will be slower because the cursor
     * makes more requests to the server.
     *
     * @param batchSize the number of items per batch. Values <= 0 are ignored.
     * @return the query itself for fluent method calls
     */
    public Finder batchSize(int batchSize) {
        this.batchSize = batchSize;
        return this;
    }

    /**
     * Executes the query for the given collection and returns a single document.
     *
     * @param type the type of entities to search
     * @return the founbd document wrapped as Optional or an empty one, if no document was found.
     */
    public Optional singleIn(Class type) {
        return singleIn(getRelationName(type));
    }

    /**
     * Executes the query for the given collection and returns a single document.
     *
     * @param collection the collection to search in
     * @return the founbd document wrapped as Optional or an empty one, if no document was found.
     */
    public Optional singleIn(String collection) {
        Watch w = Watch.start();
        try {
            FindIterable cur = mongo.db().getCollection(collection).find(filterObject);
            if (fields != null) {
                cur.projection(fields);
            }
            if (orderBy != null) {
                cur.sort(orderBy);
            }
            cur.skip(skip);

            Document obj = cur.first();

            if (obj == null) {
                return Optional.empty();
            } else {
                return Optional.of(new Doc(obj));
            }
        } finally {
            mongo.callDuration.addValue(w.elapsedMillis());
            if (Microtiming.isEnabled()) {
                w.submitMicroTiming(KEY_MONGO, "FIND ONE - " + collection + ": " + filterObject);
            }
            traceIfRequired(collection, w);
        }
    }

    /**
     * Executes the query for the given collection and calls the given processor for each document as long as it
     * returns true.
     *
     * @param type      the type of entities to search
     * @param processor the processor to handle matches, which also controls if further results should be processed
     */
    public void eachIn(Class type, Function processor) {
        eachIn(getRelationName(type), processor);
    }

    /**
     * Executes the query for the given collection and calls the given processor for each document as long as it
     * returns true.
     *
     * @param collection the collection to search in
     * @param processor  the processor to handle matches, which also controls if further results should be processed
     */
    public void eachIn(String collection, Function processor) {
        Watch w = Watch.start();

        if (Mongo.LOG.isFINE()) {
            Mongo.LOG.FINE("FIND: %s\nFilter: %s", collection, filterObject);
        }

        FindIterable cur = mongo.db().getCollection(collection).find(filterObject);
        if (fields != null) {
            cur.projection(fields);
        }
        if (orderBy != null) {
            cur.sort(orderBy);
        }
        cur.skip(skip);
        if (limit > 0) {
            cur.limit(limit);
        }
        if (batchSize > 0) {
            cur.batchSize(batchSize);
        }

        TaskContext ctx = TaskContext.get();
        Monoflop mf = Monoflop.create();
        for (Document doc : cur) {
            if (mf.firstCall()) {
                handleTracingAndReporting(collection, w);
            }

            boolean keepGoing = processor.apply(new Doc(doc));
            if (!keepGoing || !ctx.isActive()) {
                return;
            }
        }
    }

    @Override
    public Doc explain(String collection) {
        FindIterable cur = mongo.db().getCollection(collection).find(filterObject);
        if (fields != null) {
            cur.projection(fields);
        }
        if (orderBy != null) {
            cur.sort(orderBy);
        }
        cur.skip(skip);
        if (limit > 0) {
            cur.limit(limit);
        }

        return new Doc(cur.modifiers(new Document("$explain", true)).first());
    }

    private void handleTracingAndReporting(String collection, Watch w) {
        mongo.callDuration.addValue(w.elapsedMillis());
        if (Microtiming.isEnabled()) {
            w.submitMicroTiming(KEY_MONGO, "FIND ALL - " + collection + ": " + filterObject);
        }
        traceIfRequired(collection, w);
    }

    /**
     * Executes the query for the given collection and calls the given processor for each document.
     *
     * @param type      the type of entities to search
     * @param processor the processor to handle matches
     */
    public void allIn(Class type, Consumer processor) {
        allIn(getRelationName(type), processor);
    }

    /**
     * Executes the query for the given collection and calls the given processor for each document.
     *
     * @param collection the collection to search in
     * @param processor  the processor to handle matches
     */
    public void allIn(String collection, Consumer processor) {
        eachIn(collection, d -> {
            processor.accept(d);
            return true;
        });
    }

    /**
     * Counts the number of documents in the result of the given query.
     * 

* Note that limits are ignored for this query. * * @param type the type of entities to search * @return the number of documents found */ public long countIn(Class type) { return countIn(getRelationName(type)); } /** * Counts the number of documents in the result of the given query. *

* Note that limits are ignored for this query. * * @param collection the collection to search in * @return the number of documents found */ public long countIn(String collection) { Watch w = Watch.start(); try { return mongo.db().getCollection(collection).count(filterObject); } finally { mongo.callDuration.addValue(w.elapsedMillis()); if (Microtiming.isEnabled()) { w.submitMicroTiming(KEY_MONGO, "COUNT - " + collection + ": " + filterObject); } traceIfRequired(collection, w); } } /** * Aggregates the documents in the result of the given query with an accumulator operator. *

* Note that limits are ignored for this query. * * @param type the type of entities to aggregate * @param field the field to aggregate * @param operator the accumulation operator to aggregate with * @return the result of the accumulation (usually Integer, Double or List) * @see MongoDB Reference */ public Value aggregateIn(@Nonnull Class type, @Nonnull Mapping field, @Nonnull String operator) { return aggregateIn(getRelationName(type), field, operator); } /** * Aggregates the documents in the result of the given query with an accumulator operator. *

* Note that limits are ignored for this query. * * @param collection the collection to search in * @param field the field to aggregate * @param operator the accumulation operator to aggregate with * @return the result of the accumulation (usually Integer, Double or List) * @see MongoDB Reference */ public Value aggregateIn(@Nonnull String collection, @Nonnull Mapping field, @Nonnull String operator) { Watch w = Watch.start(); try { BasicDBObject groupStage = new BasicDBObject().append(Mango.ID_FIELD, null) .append("result", new BasicDBObject(operator, "$" + field)); MongoCursor queryResult = mongo.db() .getCollection(collection) .aggregate(ImmutableList.of(new BasicDBObject("$match", filterObject), new BasicDBObject("$group", groupStage))) .iterator(); if (queryResult.hasNext()) { return Value.of(queryResult.next().get("result")); } else { return Value.EMPTY; } } finally { mongo.callDuration.addValue(w.elapsedMillis()); if (Microtiming.isEnabled()) { w.submitMicroTiming(KEY_MONGO, "AGGREGATE - " + collection + "." + field + " (" + operator + "): " + filterObject); } traceIfRequired(collection, w); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy