DATADOC-102 modified Update to allow multiple field updates for most operations

This commit is contained in:
Thomas Risberg
2011-04-30 17:39:55 -04:00
parent 6e4e487eb4
commit caa8faf769
2 changed files with 220 additions and 155 deletions

View File

@@ -15,175 +15,197 @@
*/ */
package org.springframework.data.document.mongodb.query; package org.springframework.data.document.mongodb.query;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import com.mongodb.BasicDBObject; import com.mongodb.BasicDBObject;
import com.mongodb.DBObject; import com.mongodb.DBObject;
public class Update { public class Update {
public enum Position { public enum Position {
LAST, FIRST LAST, FIRST
} }
private HashMap<String, Object> criteria = new LinkedHashMap<String, Object>(); private HashMap<String, Object> modifierOps = new LinkedHashMap<String, Object>();
/** /**
* Static factory method to create an Update using the provided key * Static factory method to create an Update using the provided key
* *
* @param key * @param key
* @return * @return
*/ */
public static Update update(String key, Object value) { public static Update update(String key, Object value) {
return new Update().set(key, value); return new Update().set(key, value);
} }
/**
* Update using the $set update modifier
*
* @param key
* @param value
* @return
*/
public Update set(String key, Object value) {
criteria.put("$set", Collections.singletonMap(key, convertValueIfNecessary(value)));
return this;
}
/**
* Update using the $unset update modifier
*
* @param key
* @return
*/
public Update unset(String key) {
criteria.put("$unset", Collections.singletonMap(key, 1));
return this;
}
/** /**
* Update using the $inc update modifier * Update using the $set update modifier
* *
* @param key * @param key
* @param inc * @param value
* @return * @return
*/ */
public Update inc(String key, Number inc) { public Update set(String key, Object value) {
criteria.put("$inc", Collections.singletonMap(key, inc)); addMultiFieldOperation("$set", key, convertValueIfNecessary(value));
return this; return this;
} }
/** /**
* Update using the $push update modifier * Update using the $unset update modifier
* *
* @param key * @param key
* @param value * @return
* @return */
*/ public Update unset(String key) {
public Update push(String key, Object value) { addMultiFieldOperation("$unset", key, 1);
criteria.put("$push", Collections.singletonMap(key, convertValueIfNecessary(value))); return this;
return this; }
}
/** /**
* Update using the $pushAll update modifier * Update using the $inc update modifier
* *
* @param key * @param key
* @param values * @param inc
* @return * @return
*/ */
public Update pushAll(String key, Object[] values) { public Update inc(String key, Number inc) {
Object[] convertedValues = new Object[values.length]; addMultiFieldOperation("$inc", key, inc);
for (int i = 0; i < values.length; i++) { return this;
convertedValues[i] = convertValueIfNecessary(values[i]); }
}
DBObject keyValue = new BasicDBObject();
keyValue.put(key, convertedValues);
criteria.put("$pushAll", keyValue);
return this;
}
/** /**
* Update using the $addToSet update modifier * Update using the $push update modifier
* *
* @param key * @param key
* @param value * @param value
* @return * @return
*/ */
public Update addToSet(String key, Object value) { public Update push(String key, Object value) {
criteria.put("$addToSet", Collections.singletonMap(key, convertValueIfNecessary(value))); addMultiFieldOperation("$push", key, convertValueIfNecessary(value));
return this; return this;
} }
/** /**
* Update using the $pop update modifier * Update using the $pushAll update modifier
* *
* @param key * @param key
* @param pos * @param values
* @return * @return
*/ */
public Update pop(String key, Position pos) { public Update pushAll(String key, Object[] values) {
criteria.put("$pop", Collections.singletonMap(key, (pos == Position.FIRST ? -1 : 1))); Object[] convertedValues = new Object[values.length];
return this; for (int i = 0; i < values.length; i++) {
} convertedValues[i] = convertValueIfNecessary(values[i]);
}
DBObject keyValue = new BasicDBObject();
keyValue.put(key, convertedValues);
modifierOps.put("$pushAll", keyValue);
return this;
}
/** /**
* Update using the $pull update modifier * Update using the $addToSet update modifier
* *
* @param key * @param key
* @param value * @param value
* @return * @return
*/ */
public Update pull(String key, Object value) { public Update addToSet(String key, Object value) {
criteria.put("$pull", Collections.singletonMap(key, convertValueIfNecessary(value))); addMultiFieldOperation("$addToSet", key, convertValueIfNecessary(value));
return this; return this;
} }
/** /**
* Update using the $pullAll update modifier * Update using the $pop update modifier
* *
* @param key * @param key
* @param values * @param pos
* @return * @return
*/ */
public Update pullAll(String key, Object[] values) { public Update pop(String key, Position pos) {
Object[] convertedValues = new Object[values.length]; addMultiFieldOperation("$pop", key,
for (int i = 0; i < values.length; i++) { (pos == Position.FIRST ? -1 : 1));
convertedValues[i] = convertValueIfNecessary(values[i]); return this;
} }
DBObject keyValue = new BasicDBObject();
keyValue.put(key, convertedValues);
criteria.put("$pullAll", keyValue);
return this;
}
/** /**
* Update using the $rename update modifier * Update using the $pull update modifier
* *
* @param oldName * @param key
* @param newName * @param value
* @return * @return
*/ */
public Update rename(String oldName, String newName) { public Update pull(String key, Object value) {
criteria.put("$rename", Collections.singletonMap(oldName, newName)); addMultiFieldOperation("$pull", key, convertValueIfNecessary(value));
return this; return this;
} }
public DBObject getUpdateObject() { /**
DBObject dbo = new BasicDBObject(); * Update using the $pullAll update modifier
for (String k : criteria.keySet()) { *
dbo.put(k, criteria.get(k)); * @param key
} * @param values
return dbo; * @return
} */
public Update pullAll(String key, Object[] values) {
Object[] convertedValues = new Object[values.length];
for (int i = 0; i < values.length; i++) {
convertedValues[i] = convertValueIfNecessary(values[i]);
}
DBObject keyValue = new BasicDBObject();
keyValue.put(key, convertedValues);
modifierOps.put("$pullAll", keyValue);
return this;
}
protected Object convertValueIfNecessary(Object value) { /**
if (value instanceof Enum) { * Update using the $rename update modifier
return ((Enum<?>) value).name(); *
} * @param oldName
return value; * @param newName
} * @return
*/
public Update rename(String oldName, String newName) {
addMultiFieldOperation("$rename", oldName, newName);
return this;
}
public DBObject getUpdateObject() {
DBObject dbo = new BasicDBObject();
for (String k : modifierOps.keySet()) {
dbo.put(k, modifierOps.get(k));
}
return dbo;
}
@SuppressWarnings("unchecked")
protected void addMultiFieldOperation(String operator, String key,
Object value) {
Object existingValue = this.modifierOps.get(operator);
LinkedHashMap<String, Object> keyValueMap;
if (existingValue == null) {
keyValueMap = new LinkedHashMap<String, Object>();
this.modifierOps.put(operator, keyValueMap);
} else {
if (existingValue instanceof LinkedHashMap) {
keyValueMap = (LinkedHashMap<String, Object>) existingValue;
}
else {
throw new InvalidDataAccessApiUsageException("Modifier Operations should be a LinkedHashMap but was " +
existingValue.getClass());
}
}
keyValueMap.put(key, value);
}
protected Object convertValueIfNecessary(Object value) {
if (value instanceof Enum) {
return ((Enum<?>) value).name();
}
return value;
}
} }

View File

@@ -15,7 +15,12 @@
*/ */
package org.springframework.data.document.mongodb; package org.springframework.data.document.mongodb;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.isOneOf;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.springframework.data.document.mongodb.query.Criteria.where; import static org.springframework.data.document.mongodb.query.Criteria.where;
@@ -23,9 +28,6 @@ import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
@@ -37,11 +39,20 @@ import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.document.mongodb.convert.MappingMongoConverter; import org.springframework.data.document.mongodb.convert.MappingMongoConverter;
import org.springframework.data.document.mongodb.convert.MongoConverter; import org.springframework.data.document.mongodb.convert.MongoConverter;
import org.springframework.data.document.mongodb.mapping.MongoMappingContext; import org.springframework.data.document.mongodb.mapping.MongoMappingContext;
import org.springframework.data.document.mongodb.query.*; import org.springframework.data.document.mongodb.query.Criteria;
import org.springframework.data.document.mongodb.query.Index;
import org.springframework.data.document.mongodb.query.Index.Duplicates; import org.springframework.data.document.mongodb.query.Index.Duplicates;
import org.springframework.data.document.mongodb.query.Order;
import org.springframework.data.document.mongodb.query.Query;
import org.springframework.data.document.mongodb.query.Update;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.WriteResult;
/** /**
* Integration test for {@link MongoTemplate}. * Integration test for {@link MongoTemplate}.
* *
@@ -339,6 +350,38 @@ public class MongoTemplateTests {
} }
} }
@Test
public void testUsingUpdateWithMultipleSet() throws Exception {
template.remove(new Query(), PersonWithIdPropertyOfTypeObjectId.class);
PersonWithIdPropertyOfTypeObjectId p1 = new PersonWithIdPropertyOfTypeObjectId();
p1.setFirstName("Sven");
p1.setAge(11);
template.insert("springdata", p1);
PersonWithIdPropertyOfTypeObjectId p2 = new PersonWithIdPropertyOfTypeObjectId();
p2.setFirstName("Mary");
p2.setAge(21);
template.insert("springdata", p2);
Update u = new Update().set("firstName", "Bob").set("age", 10);
WriteResult wr = template.updateMulti("springdata", new Query(), u);
assertThat(wr.getN(), is(2));
Query q1 = new Query(Criteria.where("age").in(11, 21));
List<PersonWithIdPropertyOfTypeObjectId> r1 = template.find("springdata", q1, PersonWithIdPropertyOfTypeObjectId.class);
assertThat(r1.size(), is(0));
Query q2 = new Query(Criteria.where("age").is(10));
List<PersonWithIdPropertyOfTypeObjectId> r2 = template.find("springdata", q2, PersonWithIdPropertyOfTypeObjectId.class);
assertThat(r2.size(), is(2));
for (PersonWithIdPropertyOfTypeObjectId p : r2) {
assertThat(p.getAge(), is(10));
assertThat(p.getFirstName(), is("Bob"));
}
}
@Test @Test
public void testRemovingDocument() throws Exception { public void testRemovingDocument() throws Exception {