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 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); } }
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(); }
/** 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; }
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; } }
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; }
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 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))) { newDocument.put(key, document.get(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; }
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 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; }
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 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; }
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); }
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 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); } }