/*
* Copyright (C) 2010 Olafur Gauti Gudmundsson
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may
* obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/
package org.mongodb.morphia;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.WriteConcern;
import org.bson.types.ObjectId;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.mongodb.morphia.annotations.Embedded;
import org.mongodb.morphia.annotations.Entity;
import org.mongodb.morphia.annotations.Id;
import org.mongodb.morphia.annotations.Indexed;
import org.mongodb.morphia.annotations.PreLoad;
import org.mongodb.morphia.logging.Logger;
import org.mongodb.morphia.query.Query;
import org.mongodb.morphia.query.TestQuery.ContainsPic;
import org.mongodb.morphia.query.TestQuery.Pic;
import org.mongodb.morphia.query.UpdateOperations;
import org.mongodb.morphia.query.UpdateOpsImpl;
import org.mongodb.morphia.query.UpdateResults;
import org.mongodb.morphia.query.ValidationException;
import org.mongodb.morphia.testmodel.Article;
import org.mongodb.morphia.testmodel.Circle;
import org.mongodb.morphia.testmodel.Rectangle;
import org.mongodb.morphia.testmodel.Translation;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.hasItem;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mongodb.morphia.logging.MorphiaLoggerFactory.get;
import static org.mongodb.morphia.query.PushOptions.options;
/**
* @author Scott Hernandez
*/
@SuppressWarnings("UnusedDeclaration")
public class TestUpdateOps extends TestBase {
private static final Logger LOG = get(TestUpdateOps.class);
@Test
public void shouldUpdateAnArrayElement() {
// given
ObjectId parentId = new ObjectId();
String childName = "Bob";
String updatedLastName = "updatedLastName";
Parent parent = new Parent();
parent.id = parentId;
parent.children.add(new Child("Anthony", "Child"));
parent.children.add(new Child(childName, "originalLastName"));
getDs().save(parent);
// when
Query<Parent> query = getDs().find(Parent.class)
.field("_id").equal(parentId)
.field("children.first")
.equal(childName);
UpdateOperations<Parent> updateOps = getDs().createUpdateOperations(Parent.class)
.set("children.$.last", updatedLastName);
UpdateResults updateResults = getDs().update(query, updateOps);
// then
assertThat(updateResults.getUpdatedCount(), is(1));
assertThat(getDs().find(Parent.class).filter("id", parentId).get().children, hasItem(new Child(childName, updatedLastName)));
}
@Test
public void testDisableValidation() {
Child child1 = new Child("James", "Rigney");
validateClassName("children", getDs().createUpdateOperations(Parent.class)
.removeAll("children", child1), false);
validateClassName("children", getDs().createUpdateOperations(Parent.class)
.disableValidation()
.removeAll("children", child1), false);
validateClassName("c", getDs().createUpdateOperations(Parent.class)
.disableValidation()
.removeAll("c", child1), true);
}
private void validateClassName(final String path, final UpdateOperations<Parent> ops, final boolean expected) {
DBObject ops1 = ((UpdateOpsImpl) ops).getOps();
Map pull = (Map) ops1.get("$pull");
Map children = (Map) pull.get(path);
Assert.assertEquals(expected, children.containsKey("className"));
}
@Test
@SuppressWarnings("deprecation")
public void testAdd() throws Exception {
checkMinServerVersion(2.6);
ContainsIntArray cIntArray = new ContainsIntArray();
Datastore ds = getDs();
ds.save(cIntArray);
assertThat(ds.get(cIntArray).values, is((new ContainsIntArray()).values));
//add 4 to array
assertUpdated(ds.update(ds.createQuery(ContainsIntArray.class),
ds.createUpdateOperations(ContainsIntArray.class)
.add("values", 4, false)),
1);
assertThat(ds.get(cIntArray).values, is(new Integer[]{1, 2, 3, 4}));
//add unique (4) -- noop
assertUpdated(ds.update(ds.createQuery(ContainsIntArray.class),
ds.createUpdateOperations(ContainsIntArray.class)
.add("values", 4, false)),
1);
assertThat(ds.get(cIntArray).values, is(new Integer[]{1, 2, 3, 4}));
//add dup 4
assertUpdated(ds.update(ds.createQuery(ContainsIntArray.class),
ds.createUpdateOperations(ContainsIntArray.class)
.add("values", 4, true)),
1);
assertThat(ds.get(cIntArray).values, is(new Integer[]{1, 2, 3, 4, 4}));
//cleanup for next tests
ds.delete(ds.find(ContainsIntArray.class));
cIntArray = ds.getByKey(ContainsIntArray.class, ds.save(new ContainsIntArray()));
//add [4,5]
final List<Integer> newValues = new ArrayList<Integer>();
newValues.add(4);
newValues.add(5);
assertUpdated(ds.update(ds.createQuery(ContainsIntArray.class),
ds.createUpdateOperations(ContainsIntArray.class)
.addAll("values", newValues, false)),
1);
assertThat(ds.get(cIntArray).values, is(new Integer[]{1, 2, 3, 4, 5}));
//add them again... noop
assertUpdated(ds.update(ds.createQuery(ContainsIntArray.class),
ds.createUpdateOperations(ContainsIntArray.class)
.addAll("values", newValues, false)),
1);
assertThat(ds.get(cIntArray).values, is(new Integer[]{1, 2, 3, 4, 5}));
//add dups [4,5]
assertUpdated(ds.update(ds.createQuery(ContainsIntArray.class),
ds.createUpdateOperations(ContainsIntArray.class)
.addAll("values", newValues, true)),
1);
assertThat(ds.get(cIntArray).values, is(new Integer[]{1, 2, 3, 4, 5, 4, 5}));
}
@Test
@SuppressWarnings("deprecation")
public void testAddAll() {
getMorphia().map(EntityLogs.class, EntityLog.class);
String uuid = "4ec6ada9-081a-424f-bee0-934c0bc4fab7";
EntityLogs logs = new EntityLogs();
logs.uuid = uuid;
getDs().save(logs);
Query<EntityLogs> finder = getDs().find(EntityLogs.class).field("uuid").equal(uuid);
// both of these entries will have a className attribute
List<EntityLog> latestLogs = asList(new EntityLog("whatever1", new Date()), new EntityLog("whatever2", new Date()));
UpdateOperations<EntityLogs> updateOperationsAll = getDs().createUpdateOperations(EntityLogs.class)
.addAll("logs", latestLogs, false);
getDs().update(finder, updateOperationsAll, true);
validateNoClassName(finder.get());
// this entry will NOT have a className attribute
UpdateOperations<EntityLogs> updateOperations3 = getDs().createUpdateOperations(EntityLogs.class)
.add("logs", new EntityLog("whatever3", new Date()), false);
getDs().update(finder, updateOperations3, true);
validateNoClassName(finder.get());
// this entry will NOT have a className attribute
UpdateOperations<EntityLogs> updateOperations4 = getDs().createUpdateOperations(EntityLogs.class)
.add("logs", new EntityLog("whatever4", new Date()), false);
getDs().update(finder, updateOperations4, true);
validateNoClassName(finder.get());
}
@Test
public void testAddToSet() throws Exception {
ContainsIntArray cIntArray = new ContainsIntArray();
getDs().save(cIntArray);
assertThat(getDs().get(cIntArray).values, is((new ContainsIntArray()).values));
assertUpdated(getDs().update(getDs().find(ContainsIntArray.class),
getDs().createUpdateOperations(ContainsIntArray.class)
.addToSet("values", 5)),
1);
assertThat(getDs().get(cIntArray).values, is(new Integer[]{1, 2, 3, 5}));
assertUpdated(getDs().update(getDs().find(ContainsIntArray.class),
getDs().createUpdateOperations(ContainsIntArray.class)
.addToSet("values", 4)),
1);
assertThat(getDs().get(cIntArray).values, is(new Integer[]{1, 2, 3, 5, 4}));
assertUpdated(getDs().update(getDs().find(ContainsIntArray.class),
getDs().createUpdateOperations(ContainsIntArray.class)
.addToSet("values", asList(8, 9))),
1);
assertThat(getDs().get(cIntArray).values, is(new Integer[]{1, 2, 3, 5, 4, 8, 9}));
assertUpdated(getDs().update(getDs().find(ContainsIntArray.class),
getDs().createUpdateOperations(ContainsIntArray.class)
.addToSet("values", asList(4, 5))),
1);
assertThat(getDs().get(cIntArray).values, is(new Integer[]{1, 2, 3, 5, 4, 8, 9}));
}
@Test
@SuppressWarnings("deprecation")
public void testUpdateFirst() throws Exception {
ContainsIntArray cIntArray = new ContainsIntArray();
ContainsIntArray control = new ContainsIntArray();
Datastore ds = getDs();
ds.save(cIntArray, control);
assertThat(ds.get(cIntArray).values, is((new ContainsIntArray()).values));
Query<ContainsIntArray> query = ds.find(ContainsIntArray.class);
doUpdates(cIntArray, control, query, ds.createUpdateOperations(ContainsIntArray.class)
.addToSet("values", 4),
new Integer[]{1, 2, 3, 4});
doUpdates(cIntArray, control, query, ds.createUpdateOperations(ContainsIntArray.class)
.addToSet("values", asList(4, 5)),
new Integer[]{1, 2, 3, 4, 5});
assertInserted(ds.updateFirst(ds.find(ContainsIntArray.class)
.filter("values", new Integer[]{4, 5, 7}),
ds.createUpdateOperations(ContainsIntArray.class)
.addToSet("values", 6), true));
assertNotNull(ds.find(ContainsIntArray.class)
.filter("values", new Integer[]{4, 5, 7, 6}));
}
@SuppressWarnings("deprecation")
private void doUpdates(final ContainsIntArray updated, final ContainsIntArray control,
final Query<ContainsIntArray> query, final UpdateOperations<ContainsIntArray> operations,
final Integer[] target) {
assertUpdated(getDs().updateFirst(query, operations), 1);
assertThat(getDs().get(updated).values, is(target));
assertThat(getDs().get(control).values, is(new Integer[]{1, 2, 3}));
assertUpdated(getDs().update(query, operations, new UpdateOptions()), 1);
assertThat(getDs().get(updated).values, is(target));
assertThat(getDs().get(control).values, is(new Integer[]{1, 2, 3}));
}
@Test
public void testExistingUpdates() throws Exception {
Circle c = new Circle(100D);
getDs().save(c);
c = new Circle(12D);
getDs().save(c);
assertUpdated(getDs().update(getDs().find(Circle.class),
getDs().createUpdateOperations(Circle.class)
.inc("radius", 1D),
new UpdateOptions()),
1);
assertUpdated(getDs().update(getDs().find(Circle.class),
getDs().createUpdateOperations(Circle.class)
.inc("radius")),
2);
//test possible data type change.
final Circle updatedCircle = getDs().find(Circle.class).filter("radius", 13).get();
assertThat(updatedCircle, is(notNullValue()));
assertThat(updatedCircle.getRadius(), is(13D));
}
@Test
public void testIncDec() throws Exception {
final Rectangle[] array = {new Rectangle(1, 10), new Rectangle(1, 10), new Rectangle(1, 10), new Rectangle(10, 10),
new Rectangle(10, 10)};
for (final Rectangle rect : array) {
getDs().save(rect);
}
final Query<Rectangle> heightOf1 = getDs().find(Rectangle.class).filter("height", 1D);
final Query<Rectangle> heightOf2 = getDs().find(Rectangle.class).filter("height", 2D);
final Query<Rectangle> heightOf35 = getDs().find(Rectangle.class).filter("height", 3.5D);
assertThat(getDs().getCount(heightOf1), is(3L));
assertThat(getDs().getCount(heightOf2), is(0L));
final UpdateResults results = getDs().update(heightOf1, getDs().createUpdateOperations(Rectangle.class)
.inc("height"));
assertUpdated(results, 3);
assertThat(getDs().getCount(heightOf1), is(0L));
assertThat(getDs().getCount(heightOf2), is(3L));
getDs().update(heightOf2, getDs().createUpdateOperations(Rectangle.class).dec("height"));
assertThat(getDs().getCount(heightOf1), is(3L));
assertThat(getDs().getCount(heightOf2), is(0L));
getDs().update(heightOf1, getDs().createUpdateOperations(Rectangle.class).inc("height", 2.5D));
assertThat(getDs().getCount(heightOf1), is(0L));
assertThat(getDs().getCount(heightOf35), is(3L));
getDs().update(heightOf35, getDs().createUpdateOperations(Rectangle.class).dec("height", 2.5D));
assertThat(getDs().getCount(heightOf1), is(3L));
assertThat(getDs().getCount(heightOf35), is(0L));
getDs().update(getDs().find(Rectangle.class).filter("height", 1D),
getDs().createUpdateOperations(Rectangle.class)
.set("height", 1D)
.inc("width", 20D));
assertThat(getDs().getCount(Rectangle.class), is(5L));
assertThat(getDs().find(Rectangle.class).filter("height", 1D).get(), is(notNullValue()));
assertThat(getDs().find(Rectangle.class).filter("width", 30D).get(), is(notNullValue()));
getDs().update(getDs().find(Rectangle.class).filter("width", 30D),
getDs().createUpdateOperations(Rectangle.class).set("height", 2D).set("width", 2D));
assertThat(getDs().find(Rectangle.class).filter("width", 1D).get(), is(nullValue()));
assertThat(getDs().find(Rectangle.class).filter("width", 2D).get(), is(notNullValue()));
getDs().update(heightOf35, getDs().createUpdateOperations(Rectangle.class).dec("height", 1));
getDs().update(heightOf35, getDs().createUpdateOperations(Rectangle.class).dec("height", Long.MAX_VALUE));
getDs().update(heightOf35, getDs().createUpdateOperations(Rectangle.class).dec("height", 1.5f));
getDs().update(heightOf35, getDs().createUpdateOperations(Rectangle.class).dec("height", Double.MAX_VALUE));
try {
getDs().update(heightOf35, getDs().createUpdateOperations(Rectangle.class)
.dec("height", new AtomicInteger(1)));
fail("Wrong data type not recognized.");
} catch (IllegalArgumentException ignore) {}
}
@Test
@SuppressWarnings("deprecation")
public void testInsertUpdate() throws Exception {
assertInserted(getDs().update(getDs().find(Circle.class).field("radius").equal(0),
getDs().createUpdateOperations(Circle.class).inc("radius", 1D), true));
assertInserted(getDs().update(getDs().find(Circle.class).field("radius").equal(0),
getDs().createUpdateOperations(Circle.class).inc("radius", 1D),
new UpdateOptions()
.upsert(true)));
}
@Test
public void testInsertWithRef() throws Exception {
final Pic pic = new Pic();
pic.setName("fist");
final Key<Pic> picKey = getDs().save(pic);
assertInserted(getDs().update(getDs().find(ContainsPic.class).filter("name", "first").filter("pic", picKey),
getDs().createUpdateOperations(ContainsPic.class)
.set("name", "A"),
new UpdateOptions().upsert(true)));
assertThat(getDs().find(ContainsPic.class).count(), is(1L));
getDs().delete(getDs().find(ContainsPic.class));
assertInserted(getDs().update(getDs().find(ContainsPic.class).filter("name", "first").filter("pic", pic),
getDs().createUpdateOperations(ContainsPic.class).set("name", "second"),
new UpdateOptions()
.upsert(true)));
assertThat(getDs().find(ContainsPic.class).count(), is(1L));
//test reading the object.
final ContainsPic cp = getDs().find(ContainsPic.class).get();
assertThat(cp, is(notNullValue()));
assertThat(cp.getName(), is("second"));
assertThat(cp.getPic(), is(notNullValue()));
assertThat(cp.getPic().getName(), is(notNullValue()));
assertThat(cp.getPic().getName(), is("fist"));
}
@Test
public void testMaxKeepsCurrentDocumentValueWhenThisIsLargerThanSuppliedValue() throws Exception {
checkMinServerVersion(2.6);
final ObjectId id = new ObjectId();
final double originalValue = 2D;
Datastore ds = getDs();
assertInserted(ds.update(ds.find(Circle.class)
.field("id").equal(id),
ds.createUpdateOperations(Circle.class)
.setOnInsert("radius", originalValue),
new UpdateOptions()
.upsert(true)));
assertUpdated(ds.update(ds.find(Circle.class)
.field("id").equal(id),
ds.createUpdateOperations(Circle.class)
.max("radius", 1D),
new UpdateOptions()
.upsert(true)),
1);
assertThat(ds.get(Circle.class, id).getRadius(), is(originalValue));
}
@Test
public void testMinKeepsCurrentDocumentValueWhenThisIsSmallerThanSuppliedValue() throws Exception {
checkMinServerVersion(2.6);
final ObjectId id = new ObjectId();
final double originalValue = 3D;
assertInserted(getDs().update(getDs().find(Circle.class).field("id").equal(id),
getDs().createUpdateOperations(Circle.class).setOnInsert("radius", originalValue),
new UpdateOptions().upsert(true)));
assertUpdated(getDs().update(getDs().find(Circle.class).field("id").equal(id),
getDs().createUpdateOperations(Circle.class).min("radius", 5D),
new UpdateOptions().upsert(true)), 1);
final Circle updatedCircle = getDs().get(Circle.class, id);
assertThat(updatedCircle, is(notNullValue()));
assertThat(updatedCircle.getRadius(), is(originalValue));
}
@Test
public void testMinUsesSuppliedValueWhenThisIsSmallerThanCurrentDocumentValue() throws Exception {
checkMinServerVersion(2.6);
final ObjectId id = new ObjectId();
final double newLowerValue = 2D;
assertInserted(getDs().update(getDs().find(Circle.class).field("id").equal(id),
getDs().createUpdateOperations(Circle.class).setOnInsert("radius", 3D),
new UpdateOptions().upsert(true)));
assertUpdated(getDs().update(getDs().find(Circle.class).field("id").equal(id),
getDs().createUpdateOperations(Circle.class).min("radius", newLowerValue),
new UpdateOptions().upsert(true)), 1);
final Circle updatedCircle = getDs().get(Circle.class, id);
assertThat(updatedCircle, is(notNullValue()));
assertThat(updatedCircle.getRadius(), is(newLowerValue));
}
@Test
public void testPush() throws Exception {
checkMinServerVersion(2.6);
ContainsIntArray cIntArray = new ContainsIntArray();
getDs().save(cIntArray);
assertThat(getDs().get(cIntArray).values, is((new ContainsIntArray()).values));
getDs().update(getDs().find(ContainsIntArray.class),
getDs().createUpdateOperations(ContainsIntArray.class)
.push("values", 4),
new UpdateOptions()
.multi(false));
assertThat(getDs().get(cIntArray).values, is(new Integer[]{1, 2, 3, 4}));
getDs().update(getDs().find(ContainsIntArray.class),
getDs().createUpdateOperations(ContainsIntArray.class)
.push("values", 4),
new UpdateOptions()
.multi(false));
assertThat(getDs().get(cIntArray).values, is(new Integer[]{1, 2, 3, 4, 4}));
getDs().update(getDs().find(ContainsIntArray.class),
getDs().createUpdateOperations(ContainsIntArray.class)
.push("values", asList(5, 6)),
new UpdateOptions()
.multi(false));
assertThat(getDs().get(cIntArray).values, is(new Integer[]{1, 2, 3, 4, 4, 5, 6}));
getDs().update(getDs().find(ContainsIntArray.class),
getDs().createUpdateOperations(ContainsIntArray.class)
.push("values", 12, options().position(2)),
new UpdateOptions()
.multi(false));
assertThat(getDs().get(cIntArray).values, is(new Integer[]{1, 2, 12, 3, 4, 4, 5, 6}));
getDs().update(getDs().find(ContainsIntArray.class),
getDs().createUpdateOperations(ContainsIntArray.class)
.push("values", asList(99, 98, 97), options().position(4)),
new UpdateOptions()
.multi(false));
assertThat(getDs().get(cIntArray).values, is(new Integer[]{1, 2, 12, 3, 99, 98, 97, 4, 4, 5, 6}));
}
@Test
public void testRemoveAllSingleValue() {
EntityLogs logs = new EntityLogs();
Date date = new Date();
logs.logs.addAll(asList(
new EntityLog("log1", date),
new EntityLog("log2", date),
new EntityLog("log3", date),
new EntityLog("log1", date),
new EntityLog("log2", date),
new EntityLog("log3", date)));
Datastore ds = getDs();
ds.save(logs);
UpdateOperations<EntityLogs> operations =
ds.createUpdateOperations(EntityLogs.class).removeAll("logs", new EntityLog("log3", date));
UpdateResults results = ds.update(ds.find(EntityLogs.class), operations);
Assert.assertEquals(1, results.getUpdatedCount());
EntityLogs updated = ds.find(EntityLogs.class).get();
Assert.assertEquals(4, updated.logs.size());
for (int i = 0; i < 4; i++) {
Assert.assertEquals(new EntityLog("log" + ((i % 2) + 1), date), updated.logs.get(i));
}
}
@Test
public void testRemoveAllList() {
EntityLogs logs = new EntityLogs();
Date date = new Date();
logs.logs.addAll(asList(
new EntityLog("log1", date),
new EntityLog("log2", date),
new EntityLog("log3", date),
new EntityLog("log1", date),
new EntityLog("log2", date),
new EntityLog("log3", date)));
Datastore ds = getDs();
ds.save(logs);
UpdateOperations<EntityLogs> operations =
ds.createUpdateOperations(EntityLogs.class).removeAll("logs", singletonList(new EntityLog("log3", date)));
UpdateResults results = ds.update(ds.find(EntityLogs.class), operations);
Assert.assertEquals(1, results.getUpdatedCount());
EntityLogs updated = ds.find(EntityLogs.class).get();
Assert.assertEquals(4, updated.logs.size());
for (int i = 0; i < 4; i++) {
Assert.assertEquals(new EntityLog("log" + ((i % 2) + 1), date), updated.logs.get(i));
}
}
@Test
@Ignore("mapping in WriteResult needs to be resolved")
public void testRemoveWithNoData() {
DumbColl dumbColl = new DumbColl("ID");
dumbColl.fromArray = singletonList(new DumbArrayElement("something"));
DumbColl dumbColl2 = new DumbColl("ID2");
dumbColl2.fromArray = singletonList(new DumbArrayElement("something"));
getDs().save(asList(dumbColl, dumbColl2));
UpdateResults deleteResults = getDs().update(
getDs().find(DumbColl.class).field("opaqueId").equalIgnoreCase("ID"),
getAds().createUpdateOperations(DumbColl.class,
new BasicDBObject("$pull", new BasicDBObject("fromArray", new BasicDBObject("whereId", "not there")))));
getDs().update(
getDs().find(DumbColl.class).field("opaqueId").equalIgnoreCase("ID"),
getAds().createUpdateOperations(DumbColl.class)
.removeAll("fromArray", new DumbArrayElement("something")));
}
@Test
public void testElemMatchUpdate() {
// setUp
Object id = getDs().save(new ContainsIntArray()).getId();
assertThat(getDs().get(ContainsIntArray.class, id).values, arrayContaining(1, 2, 3));
// do patch
Query<ContainsIntArray> q = getDs().createQuery(ContainsIntArray.class)
.filter("id", id)
.filter("values", 2);
UpdateOperations<ContainsIntArray> ops = getDs().createUpdateOperations(ContainsIntArray.class)
.set("values.$", 5);
getDs().update(q, ops);
// expected
assertThat(getDs().get(ContainsIntArray.class, id).values, arrayContaining(1, 5, 3));
}
@Test
public void testRemoveFirst() throws Exception {
final ContainsIntArray cIntArray = new ContainsIntArray();
getDs().save(cIntArray);
ContainsIntArray cIALoaded = getDs().get(cIntArray);
assertThat(cIALoaded.values.length, is(3));
assertThat(cIALoaded.values, is((new ContainsIntArray()).values));
assertUpdated(getDs().update(getDs().find(ContainsIntArray.class),
getDs().createUpdateOperations(ContainsIntArray.class)
.removeFirst("values"),
new UpdateOptions()
.multi(false)),
1);
assertThat(getDs().get(cIntArray).values, is(new Integer[]{2, 3}));
assertUpdated(getDs().update(getDs().find(ContainsIntArray.class),
getDs().createUpdateOperations(ContainsIntArray.class)
.removeLast("values"),
new UpdateOptions()
.multi(false)),
1);
assertThat(getDs().get(cIntArray).values, is(new Integer[]{2}));
}
@Test
public void testSetOnInsertWhenInserting() throws Exception {
checkMinServerVersion(2.4);
ObjectId id = new ObjectId();
assertInserted(getDs().update(getDs().find(Circle.class).field("id").equal(id),
getDs().createUpdateOperations(Circle.class).setOnInsert("radius", 2D),
new UpdateOptions()
.upsert(true)));
final Circle updatedCircle = getDs().get(Circle.class, id);
assertThat(updatedCircle, is(notNullValue()));
assertThat(updatedCircle.getRadius(), is(2D));
}
@Test
public void testSetOnInsertWhenUpdating() throws Exception {
checkMinServerVersion(2.4);
ObjectId id = new ObjectId();
assertInserted(getDs().update(getDs().find(Circle.class).field("id").equal(id),
getDs().createUpdateOperations(Circle.class).setOnInsert("radius", 1D),
new UpdateOptions()
.upsert(true)));
assertUpdated(getDs().update(getDs().find(Circle.class).field("id").equal(id),
getDs().createUpdateOperations(Circle.class).setOnInsert("radius", 2D),
new UpdateOptions()
.upsert(true)),
1);
final Circle updatedCircle = getDs().get(Circle.class, id);
assertThat(updatedCircle, is(notNullValue()));
assertThat(updatedCircle.getRadius(), is(1D));
}
@Test
public void testSetUnset() throws Exception {
Datastore ds = getDs();
final Key<Circle> key = ds.save(new Circle(1));
assertUpdated(ds.update(ds.find(Circle.class).filter("radius", 1D),
ds.createUpdateOperations(Circle.class).set("radius", 2D),
new UpdateOptions()
.multi(false)),
1);
assertThat(ds.getByKey(Circle.class, key).getRadius(), is(2D));
assertUpdated(ds.update(ds.find(Circle.class).filter("radius", 2D),
ds.createUpdateOperations(Circle.class).unset("radius"),
new UpdateOptions()
.multi(false)),
1);
assertThat(ds.getByKey(Circle.class, key).getRadius(), is(0D));
Article article = new Article();
ds.save(article);
ds.update(ds.find(Article.class),
ds.createUpdateOperations(Article.class)
.set("translations", new HashMap<String, Translation>()));
ds.update(ds.find(Article.class),
ds.createUpdateOperations(Article.class)
.unset("translations"));
}
@Test
public void testUpdateFirstNoCreate() {
getDs().delete(getDs().find(EntityLogs.class));
List<EntityLogs> logs = new ArrayList<EntityLogs>();
for (int i = 0; i < 100; i++) {
logs.add(createEntryLogs("name", "logs" + i));
}
EntityLogs logs1 = logs.get(0);
Query<EntityLogs> query = getDs().find(EntityLogs.class);
UpdateOperations<EntityLogs> updateOperations = getDs().createUpdateOperations(EntityLogs.class);
BasicDBObject object = new BasicDBObject("new", "value");
updateOperations.set("raw", object);
getDs().update(query, updateOperations, new UpdateOptions());
List<EntityLogs> list = getDs().find(EntityLogs.class).asList();
for (int i = 0; i < list.size(); i++) {
final EntityLogs entityLogs = list.get(i);
assertEquals(entityLogs.id.equals(logs1.id) ? object : logs.get(i).raw, entityLogs.raw);
}
}
@Test
@SuppressWarnings("deprecation")
public void testUpdateFirstNoCreateWithEntity() {
List<EntityLogs> logs = new ArrayList<EntityLogs>();
for (int i = 0; i < 100; i++) {
logs.add(createEntryLogs("name", "logs" + i));
}
EntityLogs logs1 = logs.get(0);
Query<EntityLogs> query = getDs().find(EntityLogs.class);
BasicDBObject object = new BasicDBObject("new", "value");
EntityLogs newLogs = new EntityLogs();
newLogs.raw = object;
getDs().updateFirst(query, newLogs, false);
List<EntityLogs> list = getDs().find(EntityLogs.class).asList();
for (int i = 0; i < list.size(); i++) {
final EntityLogs entityLogs = list.get(i);
assertEquals(entityLogs.id.equals(logs1.id) ? object : logs.get(i).raw, entityLogs.raw);
}
}
@Test
public void testUpdateFirstNoCreateWithWriteConcern() {
List<EntityLogs> logs = new ArrayList<EntityLogs>();
for (int i = 0; i < 100; i++) {
logs.add(createEntryLogs("name", "logs" + i));
}
EntityLogs logs1 = logs.get(0);
getDs().update(getDs().find(EntityLogs.class),
getDs().createUpdateOperations(EntityLogs.class)
.set("raw", new BasicDBObject("new", "value")),
new UpdateOptions());
List<EntityLogs> list = getDs().find(EntityLogs.class).asList();
for (int i = 0; i < list.size(); i++) {
final EntityLogs entityLogs = list.get(i);
assertEquals(entityLogs.id.equals(logs1.id) ? new BasicDBObject("new", "value") : logs.get(i).raw, entityLogs.raw);
}
}
@Test
public void testUpdateKeyRef() throws Exception {
final ContainsPicKey cpk = new ContainsPicKey();
cpk.name = "cpk one";
Datastore ds = getDs();
ds.save(cpk);
final Pic pic = new Pic();
pic.setName("fist again");
final Key<Pic> picKey = ds.save(pic);
// picKey = getDs().getKey(pic);
//test with Key<Pic>
assertThat(ds.update(ds.find(ContainsPicKey.class).filter("name", cpk.name),
ds.createUpdateOperations(ContainsPicKey.class).set("pic", pic),
new UpdateOptions()).getUpdatedCount(),
is(1));
//test reading the object.
final ContainsPicKey cpk2 = ds.find(ContainsPicKey.class).get();
assertThat(cpk2, is(notNullValue()));
assertThat(cpk.name, is(cpk2.name));
assertThat(cpk2.pic, is(notNullValue()));
assertThat(picKey, is(cpk2.pic));
ds.update(ds.find(ContainsPicKey.class).filter("name", cpk.name),
ds.createUpdateOperations(ContainsPicKey.class).set("pic", picKey),
new UpdateOptions());
//test reading the object.
final ContainsPicKey cpk3 = ds.find(ContainsPicKey.class).get();
assertThat(cpk3, is(notNullValue()));
assertThat(cpk.name, is(cpk3.name));
assertThat(cpk3.pic, is(notNullValue()));
assertThat(picKey, is(cpk3.pic));
}
@Test
public void testUpdateKeyList() throws Exception {
final ContainsPicKey cpk = new ContainsPicKey();
cpk.name = "cpk one";
Datastore ds = getDs();
ds.save(cpk);
final Pic pic = new Pic();
pic.setName("fist again");
final Key<Pic> picKey = ds.save(pic);
cpk.keys = singletonList(picKey);
//test with Key<Pic>
final UpdateResults res = ds.update(ds.find(ContainsPicKey.class).filter("name", cpk.name),
ds.createUpdateOperations(ContainsPicKey.class).set("keys", cpk.keys),
new UpdateOptions());
assertThat(res.getUpdatedCount(), is(1));
//test reading the object.
final ContainsPicKey cpk2 = ds.find(ContainsPicKey.class).get();
assertThat(cpk2, is(notNullValue()));
assertThat(cpk.name, is(cpk2.name));
assertThat(cpk2.keys, hasItem(picKey));
}
@Test
public void testUpdateRef() throws Exception {
final ContainsPic cp = new ContainsPic();
cp.setName("cp one");
getDs().save(cp);
final Pic pic = new Pic();
pic.setName("fist");
final Key<Pic> picKey = getDs().save(pic);
//test with Key<Pic>
assertThat(getDs().update(getDs().find(ContainsPic.class).filter("name", cp.getName()),
getDs().createUpdateOperations(ContainsPic.class)
.set("pic", pic),
new UpdateOptions())
.getUpdatedCount(),
is(1));
//test reading the object.
final ContainsPic cp2 = getDs().find(ContainsPic.class).get();
assertThat(cp2, is(notNullValue()));
assertThat(cp.getName(), is(cp2.getName()));
assertThat(cp2.getPic(), is(notNullValue()));
assertThat(cp2.getPic().getName(), is(notNullValue()));
assertThat(pic.getName(), is(cp2.getPic().getName()));
getDs().update(getDs().find(ContainsPic.class).filter("name", cp.getName()),
getDs().createUpdateOperations(ContainsPic.class)
.set("pic", picKey),
new UpdateOptions());
//test reading the object.
final ContainsPic cp3 = getDs().find(ContainsPic.class).get();
assertThat(cp3, is(notNullValue()));
assertThat(cp.getName(), is(cp3.getName()));
assertThat(cp3.getPic(), is(notNullValue()));
assertThat(cp3.getPic().getName(), is(notNullValue()));
assertThat(pic.getName(), is(cp3.getPic().getName()));
}
@Test
public void testUpdateWithDifferentType() throws Exception {
final ContainsInt cInt = new ContainsInt();
cInt.val = 21;
getDs().save(cInt);
final UpdateResults res = getDs().update(getDs().find(ContainsInt.class),
getDs().createUpdateOperations(ContainsInt.class).inc("val", 1.1D),
new UpdateOptions());
assertUpdated(res, 1);
assertThat(getDs().find(ContainsInt.class).get().val, is(22));
}
@Test(expected = ValidationException.class)
public void testValidationBadFieldName() throws Exception {
getDs().update(getDs().find(Circle.class).field("radius").equal(0),
getDs().createUpdateOperations(Circle.class).inc("r", 1D));
}
@Test
public void isolated() {
UpdateOperations<Circle> updates = getDs().createUpdateOperations(Circle.class)
.inc("radius", 1D);
assertFalse(updates.isIsolated());
updates.isolated();
assertTrue(updates.isIsolated());
getDs().update(getDs().find(Circle.class)
.field("radius").equal(0),
updates,
new UpdateOptions()
.upsert(true)
.writeConcern(WriteConcern.ACKNOWLEDGED));
}
private void assertInserted(final UpdateResults res) {
assertThat(res.getInsertedCount(), is(1));
assertThat(res.getUpdatedCount(), is(0));
assertThat(res.getUpdatedExisting(), is(false));
}
private void assertUpdated(final UpdateResults res, final int count) {
assertThat(res.getInsertedCount(), is(0));
assertThat(res.getUpdatedCount(), is(count));
assertThat(res.getUpdatedExisting(), is(true));
}
private EntityLogs createEntryLogs(final String key, final String value) {
EntityLogs logs = new EntityLogs();
logs.raw = new BasicDBObject(key, value);
getDs().save(logs);
return logs;
}
@SuppressWarnings({"rawtypes", "unchecked"})
private void validateNoClassName(final EntityLogs loaded) {
List<DBObject> logs = (List<DBObject>) loaded.raw.get("logs");
for (DBObject o : logs) {
Assert.assertNull(o.get("className"));
}
}
private static class ContainsIntArray {
private final Integer[] values = {1, 2, 3};
@Id
private ObjectId id;
}
private static class ContainsInt {
@Id
private ObjectId id;
private int val;
}
@Entity
private static class ContainsPicKey {
@Id
private ObjectId id;
private String name = "test";
private Key<Pic> pic;
private List<Key<Pic>> keys;
}
@Entity(noClassnameStored = true)
public static class EntityLogs {
@Id
private ObjectId id;
@Indexed
private String uuid;
@Embedded
private List<EntityLog> logs = new ArrayList<EntityLog>();
private DBObject raw;
@PreLoad
public void preload(final DBObject raw) {
this.raw = raw;
}
}
@Embedded
@Entity(noClassnameStored = true)
public static class EntityLog {
private Date receivedTs;
private String value;
public EntityLog() {
}
EntityLog(final String value, final Date date) {
this.value = value;
receivedTs = date;
}
@Override
public int hashCode() {
int result = receivedTs != null ? receivedTs.hashCode() : 0;
result = 31 * result + (value != null ? value.hashCode() : 0);
return result;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof EntityLog)) {
return false;
}
final EntityLog entityLog = (EntityLog) o;
return receivedTs != null ? receivedTs.equals(entityLog.receivedTs)
: entityLog.receivedTs == null && (value != null ? value.equals(entityLog.value)
: entityLog.value == null);
}
@Override
public String toString() {
return String.format("EntityLog{receivedTs=%s, value='%s'}", receivedTs, value);
}
}
private static final class Parent {
@Embedded
private final Set<Child> children = new HashSet<Child>();
@Id
private ObjectId id;
}
private static final class Child {
private String first;
private String last;
private Child(final String first, final String last) {
this.first = first;
this.last = last;
}
private Child() {
}
@Override
public int hashCode() {
int result = first != null ? first.hashCode() : 0;
result = 31 * result + (last != null ? last.hashCode() : 0);
return result;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Child child = (Child) o;
return first != null ? first.equals(child.first)
: child.first == null && (last != null ? last.equals(child.last) : child.last == null);
}
}
private static final class DumbColl {
private String opaqueId;
private List<DumbArrayElement> fromArray;
private DumbColl() {
}
private DumbColl(final String opaqueId) {
this.opaqueId = opaqueId;
}
}
private static final class DumbArrayElement {
private String whereId;
private DumbArrayElement(final String whereId) {
this.whereId = whereId;
}
}
}