org.envirocar.server.mongo.dao.MongoMeasurementDao.java Source code

Java tutorial

Introduction

Here is the source code for org.envirocar.server.mongo.dao.MongoMeasurementDao.java

Source

/*
 * Copyright (C) 2013 The enviroCar project
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.envirocar.server.mongo.dao;

import java.util.ArrayList;
import java.util.List;

import org.bson.BSONObject;
import org.bson.types.ObjectId;
import org.envirocar.server.core.TemporalFilter;
import org.envirocar.server.core.dao.MeasurementDao;
import org.envirocar.server.core.entities.Measurement;
import org.envirocar.server.core.entities.Measurements;
import org.envirocar.server.core.entities.Track;
import org.envirocar.server.core.entities.User;
import org.envirocar.server.core.exception.GeometryConverterException;
import org.envirocar.server.core.filter.MeasurementFilter;
import org.envirocar.server.core.util.GeometryConverter;
import org.envirocar.server.core.util.Pagination;
import org.envirocar.server.mongo.MongoDB;
import org.envirocar.server.mongo.entity.MongoMeasurement;
import org.envirocar.server.mongo.entity.MongoTrack;
import org.envirocar.server.mongo.entity.MongoUser;
import org.envirocar.server.mongo.util.MongoUtils;
import org.envirocar.server.mongo.util.MorphiaUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.jmkgreen.morphia.Datastore;
import com.github.jmkgreen.morphia.Key;
import com.github.jmkgreen.morphia.mapping.Mapper;
import com.github.jmkgreen.morphia.query.MorphiaIterator;
import com.github.jmkgreen.morphia.query.Query;
import com.github.jmkgreen.morphia.query.QueryImpl;
import com.github.jmkgreen.morphia.query.UpdateResults;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.mongodb.AggregationOutput;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.DBRef;
import com.vividsolutions.jts.geom.Geometry;

/**
 * TODO JavaDoc
 *
 * @author Arne de Wall
 */
public class MongoMeasurementDao extends AbstractMongoDao<ObjectId, MongoMeasurement, Measurements>
        implements MeasurementDao {
    public static final String ID = Mapper.ID_KEY;
    private static final Logger log = LoggerFactory.getLogger(MongoMeasurementDao.class);
    private static final String TRACKS = "tracks";
    private static final String TRACK_NAME_PATH = MongoUtils.path(TRACKS, MongoMeasurement.TRACK,
            MongoMeasurement.IDENTIFIER);
    private static final String TRACK_NAME_VALUE = MongoUtils.valueOf(TRACK_NAME_PATH);
    public static final String TRACK_VALUE = MongoUtils.valueOf(MongoMeasurement.TRACK);
    private final MongoDB mongoDB;
    private final GeometryConverter<BSONObject> geometryConverter;
    @Inject
    private MongoTrackDao trackDao;

    @Inject
    protected MongoMeasurementDao(MongoDB mongoDB, GeometryConverter<BSONObject> geometryConverter) {
        super(MongoMeasurement.class, mongoDB);
        this.mongoDB = mongoDB;
        this.geometryConverter = geometryConverter;
    }

    @Override
    public MongoMeasurement create(Measurement measurement) {
        return save(measurement);
    }

    @Override
    public MongoMeasurement save(Measurement measurement) {
        MongoMeasurement mongoMeasurement = (MongoMeasurement) measurement;
        save(mongoMeasurement);
        return mongoMeasurement;
    }

    @Override
    public void delete(Measurement m) {
        delete(((MongoMeasurement) m).getId());
        if (m.hasTrack()) {
            updateTrackTimeForDeletedMeasurement(m);
        }
    }

    public void updateTrackTimeForDeletedMeasurement(Measurement m) {
        boolean update = false;
        Track track = m.getTrack();
        if (track.hasBegin() && m.getTime().equals(track.getBegin())) {
            MongoMeasurement newBegin = q().field(MongoMeasurement.TRACK).equal(key(track))
                    .order(MongoMeasurement.TIME).limit(1).get();
            track.setBegin(newBegin == null ? null : newBegin.getTime());
            update = true;
        }
        if (track.hasEnd() && m.getTime().equals(track.getEnd())) {
            MongoMeasurement newEnd = q().field(MongoMeasurement.TRACK).equal(key(track))
                    .order(MongoUtils.reverse(MongoMeasurement.TIME)).limit(1).get();
            track.setEnd(newEnd == null ? null : newEnd.getTime());
            update = true;
        }
        if (update) {
            trackDao.save(track);
        }
    }

    @Override
    public Measurements get(MeasurementFilter request) {
        if (request.hasGeometry()) {
            //needed because of lacking geo2d support in morphia
            return getMongo(request);
        } else {
            return getMorphia(request);
        }
    }

    private Measurements getMorphia(MeasurementFilter request) {
        Query<MongoMeasurement> q = q().order(MongoMeasurement.TIME);
        if (request.hasTrack()) {
            q.field(MongoMeasurement.TRACK).equal(key(request.getTrack()));
        }
        if (request.hasUser()) {
            q.field(MongoMeasurement.USER).equal(key(request.getUser()));
        }
        if (request.hasTemporalFilter()) {
            MorphiaUtils.temporalFilter(q.field(MongoMeasurement.TIME), request.getTemporalFilter());
        }
        return fetch(q, request.getPagination());
    }

    private Measurements getMongo(MeasurementFilter request) {
        BasicDBObjectBuilder q = new BasicDBObjectBuilder();
        if (request.hasGeometry()) {
            q.add(MongoMeasurement.GEOMETRY, withinGeometry(request.getGeometry()));
        }
        if (request.hasTrack()) {
            q.add(MongoMeasurement.TRACK, ref(request.getTrack()));
        }
        if (request.hasUser()) {
            q.add(MongoMeasurement.USER, ref(request.getUser()));
        }
        if (request.hasTemporalFilter()) {
            q.add(MongoMeasurement.TIME, MongoUtils.temporalFilter(request.getTemporalFilter()));
        }
        return query(q.get(), request.getPagination());
    }

    @Override
    public Measurement getById(String id) {
        ObjectId oid;
        try {
            oid = new ObjectId(id);
        } catch (IllegalArgumentException e) {
            return null;
        }
        return get(oid);
    }

    void removeUser(MongoUser user) {
        UpdateResults<MongoMeasurement> result = update(q().field(MongoMeasurement.USER).equal(key(user)),
                up().unset(MongoMeasurement.USER));
        if (result.getHadError()) {
            log.error("Error removing user {} from measurement: {}", user, result.getError());
        } else {
            log.debug("Removed user {} from {} measurements", user, result.getUpdatedCount());
        }
    }

    @Override
    protected Measurements createPaginatedIterable(Iterable<MongoMeasurement> i, Pagination p, long count) {
        return Measurements.from(i).withPagination(p).withElements(count).build();
    }

    void removeTrack(MongoTrack track) {
        UpdateResults<MongoMeasurement> result = update(q().field(MongoMeasurement.TRACK).equal(key(track)),
                up().unset(MongoMeasurement.TRACK));
        if (result.getHadError()) {
            log.error("Error removing track {} from measurements: {}", track, result.getError());
        } else {
            log.debug("Removed track {} from {} measurements", track, result.getUpdatedCount());
        }
    }

    List<Key<MongoTrack>> getTrackKeysByBbox(MeasurementFilter filter) {
        ArrayList<DBObject> filters = new ArrayList<DBObject>(4);
        if (filter.hasGeometry()) {
            filters.add(matchGeometry(filter.getGeometry()));
        }
        if (filter.hasUser()) {
            filters.add(matchUser(filter.getUser()));
        }
        if (filter.hasTrack()) {
            filters.add(matchTrack(filter.getTrack()));
        }
        if (filter.hasTemporalFilter()) {
            filters.add(matchTime(filter.getTemporalFilter()));
        }

        final AggregationOutput out;
        if (filters.isEmpty()) {
            out = aggregate(project(), group());
        } else {
            int size = filters.size();
            if (size == 1) {
                out = aggregate(filters.get(0), project(), group());
            } else {
                DBObject first = filters.get(0);
                DBObject[] other = new DBObject[size + 1];
                for (int i = 1; i < size; ++i) {
                    other[i - 1] = filters.get(i);
                }
                other[other.length - 2] = project();
                other[other.length - 1] = group();
                out = aggregate(first, other);
            }
        }
        return toKeyList(out.results());
    }

    private AggregationOutput aggregate(DBObject firstOp, DBObject... additionalOps) {
        AggregationOutput result = mongoDB.getDatastore().getCollection(MongoMeasurement.class).aggregate(firstOp,
                additionalOps);
        result.getCommandResult().throwOnError();
        return result;
    }

    private DBObject matchGeometry(Geometry polygon) {
        return MongoUtils.match(MongoMeasurement.GEOMETRY, withinGeometry(polygon));
    }

    private DBObject matchUser(User user) {
        return MongoUtils.match(MongoMeasurement.USER, ref(user));
    }

    private DBObject matchTrack(Track track) {
        return MongoUtils.match(MongoMeasurement.TRACK, ref(track));
    }

    private DBObject matchTime(TemporalFilter tf) {
        return MongoUtils.match(MongoMeasurement.TIME, MongoUtils.temporalFilter(tf));
    }

    private DBObject withinGeometry(Geometry polygon) {
        try {
            return MongoUtils.geoWithin(geometryConverter.encode(polygon));
        } catch (GeometryConverterException e) {
            throw new RuntimeException(e);
        }
    }

    private DBObject project() {
        return MongoUtils.project(new BasicDBObject(MongoMeasurement.TRACK, 1));
    }

    private DBObject group() {
        BasicDBObject fields = new BasicDBObject();
        fields.put(ID, TRACK_NAME_VALUE);
        fields.put(TRACKS, MongoUtils.addToSet(TRACK_VALUE));
        return MongoUtils.group(fields);
    }

    protected List<Key<MongoTrack>> toKeyList(Iterable<DBObject> res) {
        List<Key<MongoTrack>> keys = Lists.newLinkedList();
        for (DBObject obj : res) {
            BasicDBList list = (BasicDBList) obj.get(TRACKS);
            for (int i = 0; i < list.size(); i++) {
                DBRef ref = (DBRef) list.get(i);
                Key<MongoTrack> key = getMapper().refToKey(ref);
                keys.add(key);
            }
        }
        return keys;
    }

    private Measurements query(DBObject query, Pagination p) {
        final Mapper mapper = this.mongoDB.getMapper();
        final Datastore ds = this.mongoDB.getDatastore();
        final DBCollection coll = ds.getCollection(MongoMeasurement.class);

        DBCursor cursor = coll.find(query, null);
        long count = 0;

        cursor.setDecoderFactory(ds.getDecoderFact());
        if (p != null) {
            count = coll.count(query);
            if (p.getOffset() > 0) {
                cursor.skip(p.getOffset());
            }
            if (p.getLimit() > 0) {
                cursor.limit(p.getLimit());
            }
        }
        cursor.sort(QueryImpl.parseFieldsString(MongoMeasurement.TIME, MongoMeasurement.class, mapper, true));
        Iterable<MongoMeasurement> i = new MorphiaIterator<MongoMeasurement, MongoMeasurement>(cursor, mapper,
                MongoMeasurement.class, coll.getName(), mapper.createEntityCache());
        return createPaginatedIterable(i, p, count);
    }
}