Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
ec74185
init version implementation
Jul 4, 2012
116eda7
DATAMONGO-477 - Change upper bound of Guava dependency to 14.
rocketraman Jul 6, 2012
69493b6
DATAMONGO-475 - Fixed debug output in map-reduce operations.
odrotbohm Jul 16, 2012
83d0396
DATAMONGO-474 - Fixed criteria mapping for MongoTemplate.group(…).
odrotbohm Jul 16, 2012
5e81400
DATAMONGO-482 - Fixed typo in reference documentation.
odrotbohm Jul 16, 2012
3a3fefb
DATAMONGO-483 - Indexes now use the field name even if index name is …
odrotbohm Jul 16, 2012
0dcec2d
DATAMONGO-480 - Consider WriteResult for insert(…) and save(…) methods.
amolnayak311 Jul 10, 2012
fa7c1ea
DATAMONGO-476 - JavaConfig support for repositories.
odrotbohm Jul 6, 2012
c3b58c2
DATAMONGO-485 - Added test case to show complex id's are working.
odrotbohm Jul 17, 2012
73423c3
DATAMONGO-486 - Polished namespace implementation.
odrotbohm Jul 18, 2012
cdc1efe
DATAMONGO-489 - Ensure read collections get converted to appropriate …
odrotbohm Jul 23, 2012
281f8b3
DATAMONGO-490 - Fixed typos.
odrotbohm Jul 23, 2012
190422f
DATAMONGO-474 - Populating id's after save now inspects field only.
odrotbohm Jul 24, 2012
fc0fd14
DATAMONGO-491 - Upgrade to Spring Data Commons 1.4.0.M1.
odrotbohm Jul 24, 2012
7ae6d82
DATAMONGO-491 - Prepare 1.1.0.M2 release.
odrotbohm Jul 24, 2012
c91fb8c
DATAMONGO-491 - Release 1.1.0.M2.
spring-builds Jul 24, 2012
24e2f3b
DATAMONGO-491 - Prepare next development iteration.
spring-builds Jul 24, 2012
0ebfb90
DATAMONGO-493 - Added test case to show the described scenario is wor…
odrotbohm Jul 24, 2012
fff8998
DATAMONGO-493 - Fixed broken $ne handling in QueryMapper.
odrotbohm Jul 24, 2012
55a16a3
DATAMONGO-495 - Fixed debug output in MongoTemplate.doFind(…).
odrotbohm Jul 26, 2012
5e0bca9
DATAMONGO-494 - QueryMapper now forwards entity metadata into nested …
odrotbohm Jul 27, 2012
dd7222d
DATAMONGO-497 - Fixed reading of empty collections.
odrotbohm Jul 30, 2012
bf436cc
DATAMONGO-496 - AbstractMongoConfiguration now defaults mapping base …
odrotbohm Jul 30, 2012
5860a9d
DATAMONGO-499 - Fixed namespace reference to repository XSD.
odrotbohm Jul 31, 2012
697a96b
DATAMONGO-500 - Index creation is only done for the correct MappingCo…
odrotbohm Jul 31, 2012
4213be4
DATAMONGO-502 - QueryMapper now translates property names into field …
odrotbohm Aug 8, 2012
81d5bc8
DATAMONGO-472 - MongoQueryCreator now correctly translates Not keyword.
odrotbohm Aug 10, 2012
538bccd
DATAMONGO-476 - @EnableMongoRepositories is now inherited into sub-cl…
odrotbohm Aug 13, 2012
3cf7951
DATACMNS-212 - Apply refactorings in Spring Data Commons.
odrotbohm Aug 13, 2012
5e5df90
DATAMONGO-505 - Fixed handling of parameter binding of associations a…
odrotbohm Aug 15, 2012
9257f84
DATAMONGO-508 - Eagerly return DBRef creation if the given value alre…
odrotbohm Aug 15, 2012
b440b4e
DATAMONGO-509 - SimpleMongoRepository.exists(…) now avoids loading un…
odrotbohm Aug 15, 2012
c63dc00
DATAMONGO-510 - Criteria now only uses BasicDBList internally.
odrotbohm Aug 15, 2012
60769ef
DATAMONGO-511 - QueryMapper now maps associations correctly.
odrotbohm Aug 15, 2012
5a9aad9
DATAMONGO-506 - Added test case to show BasicQuery is working for nes…
odrotbohm Aug 15, 2012
ff7e897
DATACMNS-214 - Adapted API change in Spring Data Commons.
odrotbohm Aug 16, 2012
f00962a
DATAMONGO-519 - Make Spring 3.1.2.RELEASE default Spring dependency v…
odrotbohm Aug 20, 2012
cbdedd1
DATAMONGO-517 - Fixed complex keyword handling.
odrotbohm Aug 21, 2012
b412cf4
DATAMONGO-513 - Update to Spring Data Commons 1.4.0.RC1.
odrotbohm Aug 23, 2012
649572a
DATAMONGO-513 - Prepare changelog for 1.1.0.RC1.
odrotbohm Aug 24, 2012
0b91218
DATAMONGO-513 - Release 1.1.0.RC1.
spring-builds Aug 24, 2012
1c3b5c2
DATAMONGO-513 - Prepare next development iteration.
spring-builds Aug 24, 2012
95e44e2
DATAMONGO-526 - Polished README.md.
markpollack Aug 25, 2012
a5d7ce5
DATAMONGO-513 - Update to Spring Data Commons 1.4.0.BUILD-SNAPSHOT.
odrotbohm Aug 30, 2012
9bb3311
DATAMONGO-523 - Added test case to verify type alias detection.
odrotbohm Sep 3, 2012
3e4016c
DATAMONGO-279
noter Sep 4, 2012
58854af
DATAMONGO-521 - Added test case to show that repository AND query works.
odrotbohm Sep 3, 2012
1cf70f6
DATAMONGO-527 - Fixed Criteria.equals(…).
odrotbohm Sep 3, 2012
c5f5f64
DATAMONGO-279 - fix broken repository tests
noter Sep 4, 2012
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
DATAMONGO-511 - QueryMapper now maps associations correctly.
Complete overhaul of the QueryMapper to better handle complex scenarios like property paths and association references.
  • Loading branch information
odrotbohm authored and noter committed Sep 3, 2012
commit 60769efd43ec4fb3f50ff3dd2eb32b9a9edafe91
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.bson.types.BasicBSONList;
Expand Down Expand Up @@ -76,47 +75,121 @@ public QueryMapper(MongoConverter converter) {
*/
public DBObject getMappedObject(DBObject query, MongoPersistentEntity<?> entity) {

DBObject newDbo = new BasicDBObject();
if (isKeyWord(query)) {
return getMappedKeyword(query, entity);
}

DBObject result = new BasicDBObject();

for (String key : query.keySet()) {

MongoPersistentProperty targetProperty = getTargetProperty(key, entity);
String newKey = determineKey(key, entity);
Object value = query.get(key);

if (isIdKey(key, entity)) {
if (value instanceof DBObject) {
DBObject valueDbo = (DBObject) value;
if (valueDbo.containsField("$in") || valueDbo.containsField("$nin")) {
String inKey = valueDbo.containsField("$in") ? "$in" : "$nin";
List<Object> ids = new ArrayList<Object>();
for (Object id : (Iterable<?>) valueDbo.get(inKey)) {
ids.add(convertId(id));
}
valueDbo.put(inKey, ids.toArray(new Object[ids.size()]));
} else if (valueDbo.containsField("$ne")) {
valueDbo.put("$ne", convertId(valueDbo.get("$ne")));
} else {
value = getMappedObject((DBObject) value, null);
result.put(newKey, getMappedValue(value, targetProperty, newKey));
}

return result;
}

/**
* Returns the given {@link DBObject} representing a keyword by mapping the keyword's value.
*
* @param query the {@link DBObject} representing a keyword (e.g. {@code $ne : … } )
* @param entity
* @return
*/
private DBObject getMappedKeyword(DBObject query, MongoPersistentEntity<?> entity) {

String newKey = query.keySet().iterator().next();
Object value = query.get(newKey);

// $or/$nor
if (newKey.matches(N_OR_PATTERN)) {

Iterable<?> conditions = (Iterable<?>) value;
BasicDBList newConditions = new BasicDBList();

for (Object condition : conditions) {
newConditions.add(getMappedObject((DBObject) condition, entity));
}

return new BasicDBObject(newKey, newConditions);
}

return new BasicDBObject(newKey, convertSimpleOrDBObject(value, entity));
}

/**
* Returns the mapped value for the given source object assuming it's a value for the given
* {@link MongoPersistentProperty}.
*
* @param source the source object to be mapped
* @param property the property the value is a value for
* @param newKey the key the value will be bound to eventually
* @return
*/
private Object getMappedValue(Object source, MongoPersistentProperty property, String newKey) {

if (property == null) {
return convertSimpleOrDBObject(source, null);
}

if (property.isIdProperty() || "_id".equals(newKey)) {

if (source instanceof DBObject) {
DBObject valueDbo = (DBObject) source;
if (valueDbo.containsField("$in") || valueDbo.containsField("$nin")) {
String inKey = valueDbo.containsField("$in") ? "$in" : "$nin";
List<Object> ids = new ArrayList<Object>();
for (Object id : (Iterable<?>) valueDbo.get(inKey)) {
ids.add(convertId(id));
}
valueDbo.put(inKey, ids.toArray(new Object[ids.size()]));
} else if (valueDbo.containsField("$ne")) {
valueDbo.put("$ne", convertId(valueDbo.get("$ne")));
} else {
value = convertId(value);
}
newKey = "_id";
} else if (key.matches(N_OR_PATTERN)) {
// $or/$nor
Iterable<?> conditions = (Iterable<?>) value;
BasicBSONList newConditions = new BasicBSONList();
Iterator<?> iter = conditions.iterator();
while (iter.hasNext()) {
newConditions.add(getMappedObject((DBObject) iter.next(), entity));
return getMappedObject((DBObject) source, null);
}
value = newConditions;

return valueDbo;

} else {
return convertId(source);
}
}

if (property.isAssociation()) {
return isKeyWord(source) ? getMappedValue(getKeywordValue(source), property, newKey) : convertAssociation(source,
property);
}

return convertSimpleOrDBObject(source, mappingContext.getPersistentEntity(property));
}

private MongoPersistentProperty getTargetProperty(String key, MongoPersistentEntity<?> entity) {

if (isIdKey(key, entity)) {
return entity.getIdProperty();
}

PersistentPropertyPath<MongoPersistentProperty> path = getPath(key, entity);
return path == null ? null : path.getLeafProperty();
}

newDbo.put(newKey, convertSimpleOrDBObject(value, null));
private PersistentPropertyPath<MongoPersistentProperty> getPath(String key, MongoPersistentEntity<?> entity) {

if (entity == null) {
return null;
}

return newDbo;
try {
PropertyPath path = PropertyPath.from(key, entity.getTypeInformation());
return mappingContext.getPersistentPropertyPath(path);
} catch (PropertyReferenceException e) {
return null;
}
}

/**
Expand All @@ -128,17 +201,12 @@ public DBObject getMappedObject(DBObject query, MongoPersistentEntity<?> entity)
*/
private String determineKey(String key, MongoPersistentEntity<?> entity) {

if (entity == null) {
return key;
if (entity == null && DEFAULT_ID_NAMES.contains(key)) {
return "_id";
}

try {
PropertyPath path = PropertyPath.from(key, entity.getTypeInformation());
PersistentPropertyPath<MongoPersistentProperty> propertyPath = mappingContext.getPersistentPropertyPath(path);
return propertyPath.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE);
} catch (PropertyReferenceException e) {
return key;
}
PersistentPropertyPath<MongoPersistentProperty> path = getPath(key, entity);
return path == null ? key : path.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE);
}

/**
Expand All @@ -161,6 +229,30 @@ private Object convertSimpleOrDBObject(Object source, MongoPersistentEntity<?> e
return converter.convertToMongoType(source);
}

/**
* Converts the given source assuming it's actually an association to anoter object.
*
* @param source
* @param property
* @return
*/
private Object convertAssociation(Object source, MongoPersistentProperty property) {

if (property == null || !property.isAssociation()) {
return source;
}

if (source instanceof Iterable) {
BasicBSONList result = new BasicBSONList();
for (Object element : (Iterable<?>) source) {
result.add(converter.toDBRef(element, property));
}
return result;
}

return converter.toDBRef(source, property);
}

/**
* Returns whether the given key will be considered an id key.
*
Expand All @@ -183,6 +275,34 @@ private boolean isIdKey(String key, MongoPersistentEntity<?> entity) {
return DEFAULT_ID_NAMES.contains(key);
}

/**
* Returns whether the given value is representing a query keyword.
*
* @param value
* @return
*/
private static boolean isKeyWord(Object value) {

if (!(value instanceof DBObject) || value instanceof BasicDBList) {
return false;
}

DBObject dbObject = (DBObject) value;
return dbObject.keySet().size() == 1 && dbObject.keySet().iterator().next().startsWith("$");
}

/**
* Returns the value of the given source assuming it's a query keyword.
*
* @param source
* @return
*/
private static Object getKeywordValue(Object source) {

DBObject dbObject = (DBObject) source;
return dbObject.get(dbObject.keySet().iterator().next());
}

/**
* Converts the given raw id value into either {@link ObjectId} or {@link String}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.Person;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
Expand All @@ -57,6 +58,7 @@ public class QueryMapperUnitTests {

QueryMapper mapper;
MongoMappingContext context;
MappingMongoConverter converter;

@Mock
MongoDbFactory factory;
Expand All @@ -66,7 +68,7 @@ public void setUp() {

context = new MongoMappingContext();

MappingMongoConverter converter = new MappingMongoConverter(factory, context);
converter = new MappingMongoConverter(factory, context);
converter.afterPropertiesSet();

mapper = new QueryMapper(converter);
Expand Down Expand Up @@ -203,7 +205,7 @@ public void transformsArraysCorrectly() {
}

@Test
public void doesNotHandleNestedFieldsWithDefaultIdNames() {
public void doesHandleNestedFieldsWithDefaultIdNames() {

BasicDBObject dbObject = new BasicDBObject("id", new ObjectId().toString());
dbObject.put("nested", new BasicDBObject("id", new ObjectId().toString()));
Expand All @@ -212,7 +214,7 @@ public void doesNotHandleNestedFieldsWithDefaultIdNames() {

DBObject result = mapper.getMappedObject(dbObject, entity);
assertThat(result.get("_id"), is(instanceOf(ObjectId.class)));
assertThat(((DBObject) result.get("nested")).get("id"), is(instanceOf(String.class)));
assertThat(((DBObject) result.get("nested")).get("_id"), is(instanceOf(ObjectId.class)));
}

/**
Expand Down Expand Up @@ -287,6 +289,35 @@ public void returnsOriginalKeyIfNoPropertyReference() {
assertThat(result.keySet().size(), is(1));
}

@Test
public void convertsAssociationCorrectly() {

Reference reference = new Reference();
reference.id = 5L;

Query query = query(where("reference").is(reference));
DBObject object = mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(WithDBRef.class));

Object referenceObject = object.get("reference");

assertThat(referenceObject, is(instanceOf(com.mongodb.DBRef.class)));
}

@Test
public void convertsNestedAssociationCorrectly() {

Reference reference = new Reference();
reference.id = 5L;

Query query = query(where("withDbRef.reference").is(reference));
DBObject object = mapper.getMappedObject(query.getQueryObject(),
context.getPersistentEntity(WithDBRefWrapper.class));

Object referenceObject = object.get("withDbRef.reference");

assertThat(referenceObject, is(instanceOf(com.mongodb.DBRef.class)));
}

class IdWrapper {
Object id;
}
Expand Down Expand Up @@ -323,4 +354,20 @@ class CustomizedField {
@Field("foo")
CustomizedField field;
}

class WithDBRef {

@DBRef
Reference reference;
}

class Reference {

Long id;
}

class WithDBRefWrapper {

WithDBRef withDbRef;
}
}