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

com.elepy.mongo.MongoDao Maven / Gradle / Ivy

There is a newer version: 3.0.0-alpha-29
Show newest version
package com.elepy.mongo;

import com.elepy.dao.Crud;
import com.elepy.dao.Expression;
import com.elepy.dao.Query;
import com.elepy.dao.SortOption;
import com.elepy.exceptions.ElepyConfigException;
import com.elepy.exceptions.ElepyException;
import com.elepy.models.Property;
import com.elepy.models.Schema;
import com.elepy.mongo.annotations.MongoIndex;
import com.elepy.utils.ReflectionUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mongodb.BasicDBObject;
import com.mongodb.MongoSocketException;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.Indexes;
import com.mongodb.client.model.Sorts;
import org.bson.conversions.Bson;
import org.mongojack.internal.MongoJackModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public class MongoDao implements Crud {

    private final Schema schema;
    private final ObjectMapper objectMapper;
    private final MongoCollection mongoCollection;

    private static final Logger logger = LoggerFactory.getLogger("mongo");

    private final QueryBuilder queryBuilder;
    private final MongoDatabase database;


    public MongoDao(MongoDatabase database, final String collectionName, final Schema schema) {
        this.database = database;
        this.schema = schema;
        this.objectMapper = MongoJackModule.configure(CustomJacksonModule.configure(new ObjectMapper()));

        ElepyCodecRegistry jacksonCodecRegistry = new ElepyCodecRegistry(objectMapper, null);
        jacksonCodecRegistry.addCodecForClass(schema.getJavaClass());
        this.mongoCollection =
                database.getCollection(collectionName.replaceAll("/", ""))
                        .withDocumentClass(schema.getJavaClass())
                        .withCodecRegistry(jacksonCodecRegistry);
        this.queryBuilder = new QueryBuilder<>(schema);
        createIndexes();

    }


    public MongoCollection getMongoCollection() {
        return mongoCollection;
    }

    private void createIndexes() {
        try {
            Arrays.stream(schema.getJavaClass().getAnnotationsByType(MongoIndex.class))
                    .forEach(this::createIndex);


            schema.getProperties().stream().filter(Property::isUnique)
                    .forEach(property -> mongoCollection.createIndex(new BasicDBObject(property.getName(), 1)));
        } catch (MongoSocketException e) {
            logger.error("Failed at creating index", e);
        }

    }

    @Override
    public List find(Query query) {

        query.purge();

        final List sortSpec = query.getSortingSpecification().getMap().entrySet().stream().map(entry -> {
            if (entry.getValue().equals(SortOption.ASCENDING)) {
                return Sorts.ascending(entry.getKey());
            } else {
                return Sorts.descending(entry.getKey());
            }
        }).collect(Collectors.toList());
        final var expression = new QueryBuilder<>(schema).expression(query.getExpression());
        return mongoCollection.find(expression).limit(query.getLimit()).skip(query.getSkip()).sort(Sorts.orderBy(sortSpec)).into(new ArrayList<>());
    }

    @Override
    public Optional getById(Serializable id) {
        return Optional.ofNullable(mongoCollection.find(Filters.eq("_id", id)).first());
    }


    @Override
    public void update(T item) {
        mongoCollection.replaceOne(Filters.eq("_id", getId(item)), item);
    }

    @Override
    public void create(T item) {

        idQuery(item);
        mongoCollection.insertOne(item);
    }

    @Override
    public void create(T... items) {
        for (T item : items) {
            idQuery(item);
        }
        mongoCollection.insertMany(List.of(items));
    }

    private Bson idQuery(T item) {

        Optional idMaybe = ReflectionUtils.getId(item);
        if (idMaybe.isEmpty()) {

            try {
                throw new ElepyException(String.format("No Identifier provided to the object: %s", objectMapper.writeValueAsString(item)), 400);
            } catch (JsonProcessingException e) {
                throw new ElepyException("No Identifier provided to the object", 500, e);
            }
        }

        var id = idMaybe.get();
        return idQuery(id);
    }

    private Bson idQuery(Serializable id) {
        return Filters.eq("_id", id);
    }


    @Override
    public List getAll() {
        return mongoCollection.find().into(new ArrayList<>());
    }

    @Override
    public void deleteById(Serializable id) {
        mongoCollection.deleteOne(Filters.eq("_id", id));
    }

    @Override
    public void delete(Expression expression) {
        mongoCollection.deleteMany(queryBuilder.expression(expression));
    }

    @Override
    public long count(Query query) {
        query.purge();
        return mongoCollection.countDocuments(queryBuilder.expression(query.getExpression()));
    }

    @Override
    public Schema getSchema() {
        return schema;
    }

    @Override
    public ObjectMapper getObjectMapper() {
        return objectMapper;
    }


    private void createIndex(MongoIndex annotation) {

        if (annotation.properties().length == 0) {
            throw new ElepyConfigException("No properties specified in MongoIndex");
        }


        final var indexOptions = new IndexOptions();

        if (!isDefault(annotation.text())) {
            indexOptions.textVersion(annotation.text());
        }
        if (!isDefault(annotation.expireAfterSeconds())) {
            indexOptions.expireAfter(annotation.expireAfterSeconds(), TimeUnit.SECONDS);
        }
        if (!isDefault(annotation.name())) {
            indexOptions.name(annotation.name());
        }
        if (!isDefault(annotation.unique())) {
            indexOptions.unique(annotation.unique());
        }
        final var compoundIndex = Indexes.compoundIndex(Arrays
                .stream(annotation.properties())
                .map(this::createIndexField)
                .collect(Collectors.toList()));

        mongoCollection.createIndex(compoundIndex, indexOptions);
    }

    private Bson createIndexField(String property) {
        final var split = property.split(":");

        //Ensures property exists
        schema.getProperty(split[0]);

        if (split.length == 1) {
            return Indexes.ascending(property);
        }

        try {
            final var i = Integer.parseInt(split[1]);

            return new BasicDBObject(property, i);
        } catch (NumberFormatException e) {
            throw new ElepyConfigException(String.format("%s is not a valid integer", split[1]), e);
        }
    }

    private boolean isDefault(Object o) {
        if (o instanceof String) {
            return "".equals(o);
        }
        if (o instanceof Boolean) {
            return !((Boolean) o);
        }
        if (o instanceof Long || o instanceof Integer) {
            return ((Number) o).longValue() == -1;
        }
        return false;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy