Package de.bwaldvogel.mongo.backend.memory

Source Code of de.bwaldvogel.mongo.backend.memory.MemoryCollection

package de.bwaldvogel.mongo.backend.memory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;

import org.bson.BSONObject;
import org.bson.BasicBSONObject;
import org.bson.types.ObjectId;

import de.bwaldvogel.mongo.backend.DefaultQueryMatcher;
import de.bwaldvogel.mongo.backend.DocumentComparator;
import de.bwaldvogel.mongo.backend.MongoCollection;
import de.bwaldvogel.mongo.backend.QueryMatcher;
import de.bwaldvogel.mongo.backend.Utils;
import de.bwaldvogel.mongo.backend.ValueComparator;
import de.bwaldvogel.mongo.backend.memory.index.Index;
import de.bwaldvogel.mongo.exception.MongoServerError;
import de.bwaldvogel.mongo.exception.MongoServerException;

public class MemoryCollection extends MongoCollection {

    private List<Index> indexes = new ArrayList<Index>();

    private QueryMatcher matcher = new DefaultQueryMatcher();

    private AtomicLong dataSize = new AtomicLong();

    private List<BSONObject> documents = new ArrayList<BSONObject>();
    private Queue<Integer> emptyPositions = new LinkedList<Integer>();

    private String idField;

    public MemoryCollection(String databaseName, String collectionName, String idField) {
        super(databaseName, collectionName);
        this.idField = idField;
    }

    public void addIndex(Index index) {
        indexes.add(index);
    }

    private Iterable<Integer> matchDocuments(BSONObject query, Iterable<Integer> positions) throws MongoServerException {
        List<Integer> answer = new ArrayList<Integer>();
        for (Integer pos : positions) {
            BSONObject document = documents.get(pos.intValue());
            if (matcher.matches(document, query)) {
                answer.add(pos);
            }
        }
        return answer;
    }

    private Iterable<Integer> matchDocuments(BSONObject query) throws MongoServerException {
        List<Integer> answer = new ArrayList<Integer>();
        for (int i = 0; i < documents.size(); i++) {
            BSONObject document = documents.get(i);
            if (document != null && matcher.matches(document, query)) {
                answer.add(Integer.valueOf(i));
            }
        }
        return answer;
    }

    private Iterable<Integer> queryDocuments(BSONObject query) throws MongoServerException {
        synchronized (indexes) {
            for (Index index : indexes) {
                if (index.canHandle(query)) {
                    return matchDocuments(query, index.getPositions(query));
                }
            }
        }

        return matchDocuments(query);
    }

    private void changeSubdocumentValue(Object document, String key, Object newValue, Integer matchPos)
            throws MongoServerException {
        changeSubdocumentValue(document, key, newValue, new AtomicReference<Integer>(matchPos));
    }

    private void changeSubdocumentValue(Object document, String key, Object newValue, AtomicReference<Integer> matchPos)
            throws MongoServerException {
        int dotPos = key.indexOf('.');
        if (dotPos > 0) {
            String mainKey = key.substring(0, dotPos);
            String subKey = getSubkey(key, dotPos, matchPos);

            Object subObject = Utils.getListSafe(document, mainKey);
            if (subObject instanceof BSONObject || subObject instanceof List<?>) {
                changeSubdocumentValue(subObject, subKey, newValue, matchPos);
            } else {
                BSONObject obj = new BasicBSONObject();
                changeSubdocumentValue(obj, subKey, newValue, matchPos);
                Utils.setListSafe(document, mainKey, obj);
            }
        } else {
            Utils.setListSafe(document, key, newValue);
        }
    }

    protected String getSubkey(String key, int dotPos, AtomicReference<Integer> matchPos) throws MongoServerError {
        String subKey = key.substring(dotPos + 1);

        if (subKey.matches("\\$(\\..+)?")) {
            if (matchPos == null || matchPos.get() == null) {
                throw new MongoServerError(16650, //
                        "Cannot apply the positional operator without a corresponding query " //
                                + "field containing an array.");
            }
            Integer pos = matchPos.getAndSet(null);
            return subKey.replaceFirst("\\$", String.valueOf(pos));
        }
        return subKey;
    }

    private void removeSubdocumentValue(Object document, String key, Integer matchPos) throws MongoServerException {
        removeSubdocumentValue(document, key, new AtomicReference<Integer>(matchPos));
    }

    private void removeSubdocumentValue(Object document, String key, AtomicReference<Integer> matchPos)
            throws MongoServerException {
        int dotPos = key.indexOf('.');
        if (dotPos > 0) {
            String mainKey = key.substring(0, dotPos);
            String subKey = getSubkey(key, dotPos, matchPos);
            Object subObject = Utils.getListSafe(document, mainKey);
            if (subObject instanceof BSONObject || subObject instanceof List<?>) {
                removeSubdocumentValue(subObject, subKey, matchPos);
            } else {
                throw new MongoServerException("failed to remove subdocument");
            }
        } else {
            Utils.removeListSafe(document, key);
        }
    }

    private Object getSubdocumentValue(Object document, String key, Integer matchPos) throws MongoServerException {
        return getSubdocumentValue(document, key, new AtomicReference<Integer>(matchPos));
    }

    private Object getSubdocumentValue(Object document, String key, AtomicReference<Integer> matchPos)
            throws MongoServerException {
        int dotPos = key.indexOf('.');
        if (dotPos > 0) {
            String mainKey = key.substring(0, dotPos);
            String subKey = getSubkey(key, dotPos, matchPos);
            Object subObject = Utils.getListSafe(document, mainKey);
            if (subObject instanceof BSONObject || subObject instanceof List<?>) {
                return getSubdocumentValue(subObject, subKey, matchPos);
            } else {
                return null;
            }
        } else {
            return Utils.getListSafe(document, key);
        }
    }

    private void modifyField(BSONObject document, String modifier, BSONObject change, Integer matchPos, boolean isUpsert)
            throws MongoServerException {

        if (!modifier.equals("$unset")) {
            for (String key : change.keySet()) {
                if (key.startsWith("$")) {
                    throw new MongoServerError(15896, "Modified field name may not start with $");
                }
            }
        }

        if (modifier.equals("$set") || (modifier.equals("$setOnInsert") && isUpsert)) {
            for (String key : change.keySet()) {
                Object newValue = change.get(key);
                Object oldValue = getSubdocumentValue(document, key, matchPos);

                if (Utils.nullAwareEquals(newValue, oldValue)) {
                    // no change
                    continue;
                }

                assertNotKeyField(key);

                changeSubdocumentValue(document, key, newValue, matchPos);
            }
        } else if (modifier.equals("$setOnInsert")) {
            // no upsert → ignore
        } else if (modifier.equals("$unset")) {
            for (String key : change.keySet()) {
                assertNotKeyField(key);
                removeSubdocumentValue(document, key, matchPos);
            }
        } else if (modifier.equals("$push") || modifier.equals("$pushAll") || modifier.equals("$addToSet")) {
            updatePushAllAddToSet(document, modifier, change, matchPos);
        } else if (modifier.equals("$pull") || modifier.equals("$pullAll")) {
            // http://docs.mongodb.org/manual/reference/operator/pull/
            for (String key : change.keySet()) {
                Object value = getSubdocumentValue(document, key, matchPos);
                List<Object> list;
                if (value == null) {
                    return;
                } else if (value instanceof List<?>) {
                    list = Utils.asList(value);
                } else {
                    throw new MongoServerError(10142, "Cannot apply " + modifier + " modifier to non-array");
                }

                Object pushValue = change.get(key);
                if (modifier.equals("$pullAll")) {
                    if (!(pushValue instanceof Collection<?>)) {
                        throw new MongoServerError(10153, "Modifier " + modifier + " allowed for arrays only");
                    }
                    @SuppressWarnings("unchecked")
                    Collection<Object> valueList = (Collection<Object>) pushValue;
                    do {
                    } while (list.removeAll(valueList));
                } else {
                    do {
                    } while (list.remove(pushValue));
                }
                // no need to put something back
            }
        } else if (modifier.equals("$pop")) {
            for (String key : change.keySet()) {
                Object value = getSubdocumentValue(document, key, matchPos);
                List<Object> list;
                if (value == null) {
                    return;
                } else if (value instanceof List<?>) {
                    list = Utils.asList(value);
                } else {
                    throw new MongoServerError(10143, "Cannot apply " + modifier + " modifier to non-array");
                }

                Object pushValue = change.get(key);
                if (!list.isEmpty()) {
                    if (pushValue != null && Utils.normalizeValue(pushValue).equals(Double.valueOf(-1.0))) {
                        list.remove(0);
                    } else {
                        list.remove(list.size() - 1);
                    }
                }
                // no need to put something back
            }
        } else if (modifier.equals("$inc")) {
            // http://docs.mongodb.org/manual/reference/operator/inc/
            for (String key : change.keySet()) {
                assertNotKeyField(key);

                Object value = getSubdocumentValue(document, key, matchPos);
                Number number;
                if (value == null) {
                    number = Integer.valueOf(0);
                } else if (value instanceof Number) {
                    number = (Number) value;
                } else {
                    throw new MongoServerException("can not increment '" + value + "'");
                }

                changeSubdocumentValue(document, key, Utils.addNumbers(number, (Number) change.get(key)), matchPos);
            }
        } else {
            throw new MongoServerError(10147, "Invalid modifier specified: " + modifier);
        }

    }

    private void updatePushAllAddToSet(BSONObject document, String modifier, BSONObject change, Integer matchPos)
            throws MongoServerException {
        // http://docs.mongodb.org/manual/reference/operator/push/
        for (String key : change.keySet()) {
            Object value = getSubdocumentValue(document, key, matchPos);
            List<Object> list;
            if (value == null) {
                list = new ArrayList<Object>();
            } else if (value instanceof List<?>) {
                list = Utils.asList(value);
            } else {
                throw new MongoServerError(10141, "Cannot apply " + modifier + " modifier to non-array");
            }

            Object changeValue = change.get(key);
            if (modifier.equals("$pushAll")) {
                if (!(changeValue instanceof Collection<?>)) {
                    throw new MongoServerError(10153, "Modifier " + modifier + " allowed for arrays only");
                }
                @SuppressWarnings("unchecked")
                Collection<Object> valueList = (Collection<Object>) changeValue;
                list.addAll(valueList);
            } else {
                Collection<Object> pushValues = new ArrayList<Object>();
                if (changeValue instanceof BSONObject
                        && ((BSONObject) changeValue).keySet().equals(Collections.singleton("$each"))) {
                    @SuppressWarnings("unchecked")
                    Collection<Object> values = (Collection<Object>) ((BSONObject) changeValue).get("$each");
                    pushValues.addAll(values);
                } else {
                    pushValues.add(changeValue);
                }

                for (Object val : pushValues) {
                    if (modifier.equals("$push")) {
                        list.add(val);
                    } else if (modifier.equals("$addToSet")) {
                        if (!list.contains(val)) {
                            list.add(val);
                        }
                    } else {
                        throw new MongoServerException("internal server error. illegal modifier here: " + modifier);
                    }
                }
            }
            changeSubdocumentValue(document, key, list, matchPos);
        }
    }

    private void assertNotKeyField(String key) throws MongoServerError {
        if (key.equals(idField)) {
            throw new MongoServerError(10148, "Mod on " + idField + " not allowed");
        }
    }

    private void applyUpdate(BSONObject oldDocument, BSONObject newDocument) throws MongoServerException {

        if (newDocument.equals(oldDocument)) {
            return;
        }

        Object oldId = oldDocument.get(idField);
        Object newId = newDocument.get(idField);

        if (newId != null && !Utils.nullAwareEquals(oldId, newId)) {
            oldId = new BasicBSONObject(idField, oldId);
            newId = new BasicBSONObject(idField, newId);
            throw new MongoServerError(13596, "cannot change _id of a document old:" + oldId + " new:" + newId);
        }

        if (newId == null && oldId != null) {
            newDocument.put(idField, oldId);
        }

        cloneInto(oldDocument, newDocument);
    }

    Object deriveDocumentId(BSONObject selector) {
        Object value = selector.get(idField);
        if (value != null) {
            if (!Utils.containsQueryExpression(value)) {
                return value;
            } else {
                return deriveIdFromExpression(value);
            }
        }
        return new ObjectId();
    }

    private Object deriveIdFromExpression(Object value) {
        BSONObject expression = (BSONObject) value;
        for (String key : expression.keySet()) {
            Object expressionValue = expression.get(key);
            if (key.equals("$in")) {
                Collection<?> list = (Collection<?>) expressionValue;
                if (!list.isEmpty()) {
                    return list.iterator().next();
                }
            }
        }
        // fallback to random object id
        return new ObjectId();
    }

    private BSONObject calculateUpdateDocument(BSONObject oldDocument, BSONObject update, Integer matchPos,
            boolean isUpsert) throws MongoServerException {

        int numStartsWithDollar = 0;
        for (String key : update.keySet()) {
            if (key.startsWith("$")) {
                numStartsWithDollar++;
            }
        }

        BSONObject newDocument = new BasicBSONObject(idField, oldDocument.get(idField));

        if (numStartsWithDollar == update.keySet().size()) {
            cloneInto(newDocument, oldDocument);
            for (String key : update.keySet()) {
                modifyField(newDocument, key, (BSONObject) update.get(key), matchPos, isUpsert);
            }
        } else if (numStartsWithDollar == 0) {
            applyUpdate(newDocument, update);
        } else {
            throw new MongoServerException("illegal update: " + update);
        }

        return newDocument;
    }

    public synchronized void addDocument(BSONObject document) throws MongoServerException {

        Integer pos = emptyPositions.poll();
        if (pos == null)
            pos = Integer.valueOf(documents.size());

        for (Index index : indexes) {
            index.checkAdd(document);
        }
        for (Index index : indexes) {
            index.add(document, pos);
        }
        dataSize.addAndGet(Utils.calculateSize(document));
        if (pos == documents.size()) {
            documents.add(document);
        } else {
            documents.set(pos.intValue(), document);
        }
    }

    public synchronized void removeDocument(BSONObject document) throws MongoServerException {
        Integer pos = null;

        if (!indexes.isEmpty()) {
            for (Index index : indexes) {
                pos = index.remove(document);
            }
        } else {
            int idx = documents.indexOf(document);
            if (idx >= 0) {
                pos = Integer.valueOf(idx);
            }
        }
        if (pos == null) {
            // not found
            return;
        }
        dataSize.addAndGet(-Utils.calculateSize(document));
        documents.set(pos.intValue(), null);
        emptyPositions.add(pos);
    }

    public synchronized int count() {
        return documents.size() - emptyPositions.size();
    }

    public synchronized BSONObject findAndModify(BSONObject query) throws MongoServerException {

        boolean returnNew = Utils.isTrue(query.get("new"));

        if (!query.containsField("remove") && !query.containsField("update")) {
            throw new MongoServerException("need remove or update");
        }

        BSONObject queryObject = new BasicBSONObject();

        if (query.containsField("query")) {
            queryObject.put("query", query.get("query"));
        } else {
            queryObject.put("query", new BasicBSONObject());
        }

        if (query.containsField("sort")) {
            queryObject.put("orderby", query.get("sort"));
        }

        BSONObject lastErrorObject = null;
        BSONObject returnDocument = null;
        int num = 0;
        for (BSONObject document : handleQuery(queryObject, 0, 1)) {
            num++;
            if (Utils.isTrue(query.get("remove"))) {
                removeDocument(document);
                returnDocument = document;
            } else if (query.get("update") != null) {
                BSONObject updateQuery = (BSONObject) query.get("update");

                Integer matchPos = matcher.matchPosition(document, (BSONObject) queryObject.get("query"));

                BSONObject oldDocument = updateDocument(document, updateQuery, matchPos);
                if (returnNew) {
                    returnDocument = document;
                } else {
                    returnDocument = oldDocument;
                }
                lastErrorObject = new BasicBSONObject("updatedExisting", Boolean.TRUE);
                lastErrorObject.put("n", Integer.valueOf(1));
            }
        }
        if (num == 0 && Utils.isTrue(query.get("upsert"))) {
            BSONObject selector = (BSONObject) query.get("query");
            BSONObject updateQuery = (BSONObject) query.get("update");
            BSONObject newDocument = handleUpsert(updateQuery, selector);
            if (returnNew) {
                returnDocument = newDocument;
            } else {
                returnDocument = new BasicBSONObject();
            }
            num++;
        }

        if (query.get("fields") != null) {
            BSONObject fields = (BSONObject) query.get("fields");
            returnDocument = projectDocument(returnDocument, fields);
        }

        BSONObject result = new BasicBSONObject();
        if (lastErrorObject != null) {
            result.put("lastErrorObject", lastErrorObject);
        }
        result.put("value", returnDocument);
        Utils.markOkay(result);
        return result;
    }

    private BSONObject projectDocument(BSONObject document, BSONObject fields) {

        if (document == null)
            return null;

        BSONObject newDocument = new BasicBSONObject();
        for (String key : fields.keySet()) {
            if (Utils.isTrue(fields.get(key))) {
                projectField(document, newDocument, key);
            }
        }

        // implicitly add _id if not mentioned
        // http://docs.mongodb.org/manual/core/read-operations/#result-projections
        if (!fields.containsField(idField)) {
            newDocument.put(idField, document.get(idField));
        }

        return newDocument;
    }

    private void projectField(BSONObject document, BSONObject newDocument, String key) {

        if (document == null)
            return;

        int dotPos = key.indexOf('.');
        if (dotPos > 0) {
            String mainKey = key.substring(0, dotPos);
            String subKey = key.substring(dotPos + 1);

            Object object = document.get(mainKey);
            // do not project the subdocument if it is not of type BSONObject
            if (object instanceof BSONObject) {
                if (!newDocument.containsField(mainKey)) {
                    newDocument.put(mainKey, new BasicBSONObject());
                }
                projectField((BSONObject) object, (BSONObject) newDocument.get(mainKey), subKey);
            }
        } else {
            newDocument.put(key, document.get(key));
        }
    }

    public synchronized Iterable<BSONObject> handleQuery(BSONObject queryObject, int numberToSkip, int numberToReturn)
            throws MongoServerException {
        return handleQuery(queryObject, numberToSkip, numberToReturn, null);
    }

    public synchronized Iterable<BSONObject> handleQuery(BSONObject queryObject, int numberToSkip, int numberToReturn,
            BSONObject fieldSelector) throws MongoServerException {

        BSONObject query;
        BSONObject orderBy = null;

        if (numberToReturn < 0) {
            // actually: request to close cursor automatically
            numberToReturn = -numberToReturn;
        }

        if (queryObject.containsField("query")) {
            query = (BSONObject) queryObject.get("query");
            orderBy = (BSONObject) queryObject.get("orderby");
        } else if (queryObject.containsField("$query")) {
            query = (BSONObject) queryObject.get("$query");
            orderBy = (BSONObject) queryObject.get("$orderby");
        } else {
            query = queryObject;
        }

        if (documents.isEmpty()) {
            return Collections.emptyList();
        }

        Iterable<Integer> keys = queryDocuments(query);

        List<BSONObject> objs = new ArrayList<BSONObject>();
        for (Integer pos : keys) {
            if (numberToSkip > 0) {
                numberToSkip--;
                continue;
            }
            objs.add(documents.get(pos.intValue()));
        }

        if (orderBy != null && !orderBy.keySet().isEmpty()) {
            if (orderBy.keySet().iterator().next().equals("$natural")) {
                if (orderBy.get("$natural").equals(Integer.valueOf(1))) {
                    // keep it as is
                } else if (orderBy.get("$natural").equals(Integer.valueOf(-1))) {
                    Collections.reverse(objs);
                }
            } else {
                Collections.sort(objs, new DocumentComparator(orderBy));
            }
        }

        if (numberToReturn > 0 && objs.size() > numberToReturn)
            objs = objs.subList(0, numberToReturn);

        if (fieldSelector != null && !fieldSelector.keySet().isEmpty()) {
            for (int i = 0; i < objs.size(); i++) {
                objs.set(i, projectDocument(objs.get(i), fieldSelector));
            }
        }

        return objs;
    }

    public synchronized BSONObject handleDistinct(BSONObject query) throws MongoServerException {
        String key = query.get("key").toString();
        BSONObject q = (BSONObject) query.get("query");
        TreeSet<Object> values = new TreeSet<Object>(new ValueComparator());

        for (Integer pos : queryDocuments(q)) {
            BSONObject document = documents.get(pos.intValue());
            if (document.containsField(key))
                values.add(document.get(key));
        }

        BSONObject response = new BasicBSONObject("values", new ArrayList<Object>(values));
        Utils.markOkay(response);
        return response;
    }

    public synchronized int insertDocuments(List<BSONObject> documents) throws MongoServerException {
        for (BSONObject document : documents) {
            addDocument(document);
        }
        return documents.size();
    }

    public synchronized int deleteDocuments(BSONObject selector, int limit) throws MongoServerException {
        int n = 0;
        for (BSONObject document : handleQuery(selector, 0, limit)) {
            if (limit > 0 && n >= limit) {
                throw new MongoServerException("internal error: too many elements (" + n + " >= " + limit + ")");
            }
            removeDocument(document);
            n++;
        }
        return n;
    }

    public synchronized BSONObject updateDocuments(BSONObject selector, BSONObject updateQuery, boolean isMulti,
            boolean isUpsert) throws MongoServerException {
        int n = 0;
        boolean updatedExisting = false;

        if (isMulti) {
            for (String key : updateQuery.keySet()) {
                if (!key.startsWith("$")) {
                    throw new MongoServerError(10158, "multi update only works with $ operators");
                }
            }
        }

        for (Integer position : queryDocuments(selector)) {
            BSONObject document = documents.get(position.intValue());
            Integer matchPos = matcher.matchPosition(document, selector);
            updateDocument(document, updateQuery, matchPos);
            updatedExisting = true;
            n++;

            if (!isMulti) {
                break;
            }
        }

        BSONObject result = new BasicBSONObject();

        // insert?
        if (n == 0 && isUpsert) {
            BSONObject newDocument = handleUpsert(updateQuery, selector);
            if (!selector.containsField(idField)) {
                result.put("upserted", newDocument.get(idField));
            }
            n++;
        }

        result.put("n", Integer.valueOf(n));
        result.put("updatedExisting", Boolean.valueOf(updatedExisting));
        return result;
    }

    private BSONObject updateDocument(BSONObject document, BSONObject updateQuery, Integer matchPos)
            throws MongoServerException {
        synchronized (document) {
            // copy document
            BSONObject oldDocument = new BasicBSONObject();
            cloneInto(oldDocument, document);

            BSONObject newDocument = calculateUpdateDocument(document, updateQuery, matchPos, false);

            if (!newDocument.equals(oldDocument)) {
                for (Index index : indexes) {
                    index.checkUpdate(oldDocument, newDocument);
                }
                for (Index index : indexes) {
                    index.updateInPlace(oldDocument, newDocument);
                }

                long oldSize = Utils.calculateSize(oldDocument);
                long newSize = Utils.calculateSize(newDocument);
                dataSize.addAndGet(newSize - oldSize);

                // only keep fields that are also in the updated document
                Set<String> fields = new HashSet<String>(document.keySet());
                fields.removeAll(newDocument.keySet());
                for (String key : fields) {
                    document.removeField(key);
                }

                // update the fields
                for (String key : newDocument.keySet()) {
                    if (key.contains(".")) {
                        throw new MongoServerException(
                                "illegal field name. must not happen as it must be catched by the driver");
                    }
                    document.put(key, newDocument.get(key));
                }
            }
            return oldDocument;
        }
    }

    private void cloneInto(BSONObject targetDocument, BSONObject sourceDocument) {
        for (String key : sourceDocument.keySet()) {
            targetDocument.put(key, cloneValue(sourceDocument.get(key)));
        }
    }

    protected Object cloneValue(Object value) {
        if (value instanceof BSONObject) {
            BSONObject newValue = new BasicBSONObject();
            cloneInto(newValue, (BSONObject) value);
            return newValue;
        } else if (value instanceof List<?>) {
            @SuppressWarnings("unchecked")
            List<Object> list = (List<Object>) value;
            List<Object> newValue = new ArrayList<Object>();
            for (Object v : list) {
                newValue.add(cloneValue(v));
            }
            return newValue;
        } else {
            return value;
        }
    }

    private BSONObject handleUpsert(BSONObject updateQuery, BSONObject selector) throws MongoServerException {
        BSONObject document = convertSelectorToDocument(selector);

        BSONObject newDocument = calculateUpdateDocument(document, updateQuery, null, true);
        if (newDocument.get(idField) == null) {
            newDocument.put(idField, deriveDocumentId(selector));
        }
        addDocument(newDocument);
        return newDocument;
    }

    /**
     * convert selector used in an upsert statement into a document
     */
    BSONObject convertSelectorToDocument(BSONObject selector) throws MongoServerException {
        BSONObject document = new BasicBSONObject();
        for (String key : selector.keySet()) {
            if (key.startsWith("$"))
                continue;

            Object value = selector.get(key);
            if (!Utils.containsQueryExpression(value)) {
                changeSubdocumentValue(document, key, value, (AtomicReference<Integer>) null);
            }
        }
        return document;
    }

    public int getNumIndexes() {
        return indexes.size();
    }

    public int count(BSONObject query) throws MongoServerException {
        if (query.keySet().isEmpty()) {
            return count();
        }

        int count = 0;
        Iterator<Integer> it = queryDocuments(query).iterator();
        while (it.hasNext()) {
            it.next();
            count++;
        }
        return count;
    }

    public BSONObject getStats() {
        BSONObject response = new BasicBSONObject("ns", getFullName());
        response.put("count", Integer.valueOf(documents.size()));
        response.put("size", Long.valueOf(dataSize.get()));

        double averageSize = 0;
        if (!documents.isEmpty()) {
            averageSize = dataSize.get() / (double) documents.size();
        }
        response.put("avgObjSize", Double.valueOf(averageSize));
        response.put("storageSize", Integer.valueOf(0));
        response.put("numExtents", Integer.valueOf(0));
        response.put("nindexes", Integer.valueOf(indexes.size()));
        BSONObject indexSizes = new BasicBSONObject();
        for (Index index : indexes) {
            indexSizes.put(index.getName(), Long.valueOf(index.getDataSize()));
        }

        response.put("indexSize", indexSizes);
        Utils.markOkay(response);
        return response;
    }

    public BSONObject validate() {
        BSONObject response = new BasicBSONObject("ns", getFullName());
        response.put("extentCount", Integer.valueOf(0));
        response.put("datasize", Long.valueOf(dataSize.get()));
        response.put("nrecords", Integer.valueOf(documents.size()));
        response.put("padding", Integer.valueOf(1));
        response.put("deletedCount", Integer.valueOf(emptyPositions.size()));
        response.put("deletedSize", Integer.valueOf(0));

        response.put("nIndexes", Integer.valueOf(indexes.size()));
        BSONObject keysPerIndex = new BasicBSONObject();
        for (Index index : indexes) {
            keysPerIndex.put(index.getName(), Long.valueOf(index.getCount()));
        }

        response.put("keysPerIndex", keysPerIndex);
        response.put("valid", Boolean.TRUE);
        response.put("errors", Arrays.asList());
        Utils.markOkay(response);
        return response;
    }
}
TOP

Related Classes of de.bwaldvogel.mongo.backend.memory.MemoryCollection

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.