Skip to content

DATAMONGO-1902 - Add support for embedding objects. #896

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.2.0-SNAPSHOT</version>
<version>3.2.0-DATAMONGO-1902-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Spring Data MongoDB</name>
Expand Down
2 changes: 1 addition & 1 deletion spring-data-mongodb-benchmarks/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.2.0-SNAPSHOT</version>
<version>3.2.0-DATAMONGO-1902-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion spring-data-mongodb-distribution/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.2.0-SNAPSHOT</version>
<version>3.2.0-DATAMONGO-1902-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion spring-data-mongodb/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.2.0-SNAPSHOT</version>
<version>3.2.0-DATAMONGO-1902-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class DefaultIndexOperationsProvider implements IndexOperationsProvider {
* @see org.springframework.data.mongodb.core.index.IndexOperationsProvider#reactiveIndexOps(java.lang.String)
*/
@Override
public IndexOperations indexOps(String collectionName) {
return new DefaultIndexOperations(mongoDbFactory, collectionName, mapper);
public IndexOperations indexOps(String collectionName, Class<?> type) {
return new DefaultIndexOperations(mongoDbFactory, collectionName, mapper, type);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -715,20 +715,26 @@ public void dropCollection(String collectionName) {
});
}


@Override
public IndexOperations indexOps(String collectionName) {
return indexOps(collectionName, null);
}

/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ExecutableInsertOperation#indexOps(java.lang.String)
*/
public IndexOperations indexOps(String collectionName) {
return new DefaultIndexOperations(this, collectionName, null);
public IndexOperations indexOps(String collectionName, @Nullable Class<?> type) {
return new DefaultIndexOperations(this, collectionName, type);
}

/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.ExecutableInsertOperation#indexOps(java.lang.Class)
*/
public IndexOperations indexOps(Class<?> entityClass) {
return new DefaultIndexOperations(this, getCollectionName(entityClass), entityClass);
return indexOps(getCollectionName(entityClass), entityClass);
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,19 @@ Bson getDocument() {
return this.document;
}

/**
* Copies all of the mappings from the given {@link Document} to the underlying target {@link Document}. These
* mappings will replace any mappings that the target document had for any of the keys currently in the specified map.
*
* @param source
*/
public void putAll(Document source) {

Map<String, Object> target = BsonUtils.asMap(document);

target.putAll(source);
}

/**
* Puts the given value into the backing {@link Document} based on the coordinates defined through the given
* {@link MongoPersistentProperty}. By default this will be the plain field name. But field names might also consist
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@
import org.springframework.data.mapping.model.SpELExpressionParameterValueProvider;
import org.springframework.data.mongodb.CodecRegistryProvider;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.mapping.Embedded;
import org.springframework.data.mongodb.core.mapping.Embedded.OnEmpty;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.event.AfterConvertCallback;
Expand Down Expand Up @@ -427,6 +429,13 @@ private void readProperties(MongoPersistentEntity<?> entity, PersistentPropertyA
continue;
}

if (prop.isEmbedded()) {

accessor.setProperty(prop,
readEmbedded(documentAccessor, currentPath, prop, mappingContext.getPersistentEntity(prop)));
continue;
}

// We skip the id property since it was already set

if (entity.isIdProperty(prop)) {
Expand Down Expand Up @@ -472,6 +481,22 @@ private void readAssociation(Association<MongoPersistentProperty> association, P
accessor.setProperty(property, dbRefResolver.resolveDbRef(property, dbref, callback, handler));
}

@Nullable
private Object readEmbedded(DocumentAccessor documentAccessor, ObjectPath currentPath, MongoPersistentProperty prop,
MongoPersistentEntity<?> embeddedEntity) {

if (prop.findAnnotation(Embedded.class).onEmpty().equals(OnEmpty.USE_EMPTY)) {
return read(embeddedEntity, (Document) documentAccessor.getDocument(), currentPath);
}

for (MongoPersistentProperty persistentProperty : embeddedEntity) {
if (documentAccessor.hasValue(persistentProperty)) {
return read(embeddedEntity, (Document) documentAccessor.getDocument(), currentPath);
}
}
return null;
}

/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.convert.MongoWriter#toDBRef(java.lang.Object, org.springframework.data.mongodb.core.mapping.MongoPersistentProperty)
Expand Down Expand Up @@ -642,6 +667,15 @@ protected void writePropertyInternal(@Nullable Object obj, DocumentAccessor acce
TypeInformation<?> valueType = ClassTypeInformation.from(obj.getClass());
TypeInformation<?> type = prop.getTypeInformation();

if (prop.isEmbedded()) {

Document target = new Document();
writeInternal(obj, target, mappingContext.getPersistentEntity(prop));

accessor.putAll(target);
return;
}

if (valueType.isCollectionLike()) {
List<Object> collectionInternal = createCollection(asCollection(obj), prop);
accessor.put(prop, collectionInternal);
Expand Down Expand Up @@ -1352,6 +1386,14 @@ public Object convertToMongoType(@Nullable Object obj, TypeInformation<?> typeIn
return !obj.getClass().equals(typeInformation.getType()) ? newDocument : removeTypeInfo(newDocument, true);
}

@Nullable
@Override
public Object convertToMongoType(@Nullable Object obj, MongoPersistentEntity entity) {
Document newDocument = new Document();
writeInternal(obj, newDocument, entity);
return newDocument;
}

public List<Object> maybeConvertList(Iterable<?> source, TypeInformation<?> typeInformation) {

List<Object> newDbl = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.util.BsonUtils;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
Expand Down Expand Up @@ -144,9 +145,9 @@ default Object convertId(@Nullable Object id, Class<?> targetType) {
try {
return getConversionService().canConvert(id.getClass(), targetType)
? getConversionService().convert(id, targetType)
: convertToMongoType(id, null);
: convertToMongoType(id, (TypeInformation<?>) null);
} catch (ConversionException o_O) {
return convertToMongoType(id, null);
return convertToMongoType(id,(TypeInformation<?>) null);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.springframework.data.mongodb.core.query.MongoRegexCreator.MatchMode;
import org.springframework.data.mongodb.core.query.SerializationUtils;
import org.springframework.data.mongodb.core.query.UntypedExampleMatcher;
import org.springframework.data.mongodb.util.DotPath;
import org.springframework.data.support.ExampleMatcherAccessor;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
Expand Down Expand Up @@ -134,7 +135,7 @@ private void applyPropertySpecs(String path, Document source, Class<?> probeType
while (iter.hasNext()) {

Map.Entry<String, Object> entry = iter.next();
String propertyPath = StringUtils.hasText(path) ? path + "." + entry.getKey() : entry.getKey();
String propertyPath = DotPath.from(path).append(entry.getKey()).toString();
String mappedPropertyPath = getMappedPropertyPath(propertyPath, probeType);

if (isEmptyIdProperty(entry)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import org.bson.conversions.Bson;
import org.springframework.data.convert.EntityWriter;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
Expand All @@ -43,7 +44,7 @@ public interface MongoWriter<T> extends EntityWriter<T, Bson> {
*/
@Nullable
default Object convertToMongoType(@Nullable Object obj) {
return convertToMongoType(obj, null);
return convertToMongoType(obj, (TypeInformation<?>) null);
}

/**
Expand All @@ -57,6 +58,9 @@ default Object convertToMongoType(@Nullable Object obj) {
@Nullable
Object convertToMongoType(@Nullable Object obj, @Nullable TypeInformation<?> typeInformation);

default Object convertToMongoType(@Nullable Object obj, MongoPersistentEntity<?> entity) {
return convertToMongoType(obj, entity.getTypeInformation());
}
/**
* Creates a {@link DBRef} to refer to the given object.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;

import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.domain.Example;
Expand All @@ -42,6 +43,7 @@
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty.PropertyToFieldNameConverter;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.util.BsonUtils;
import org.springframework.data.mongodb.util.DotPath;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
Expand Down Expand Up @@ -140,9 +142,23 @@ public Document getMappedObject(Bson query, @Nullable MongoPersistentEntity<?> e
try {

Field field = createPropertyField(entity, key, mappingContext);
Entry<String, Object> entry = getMappedObjectForField(field, BsonUtils.get(query, key));

result.put(entry.getKey(), entry.getValue());
// TODO: move to dedicated method
if (field.getProperty() != null && field.getProperty().isEmbedded()) {

Object theNestedObject = BsonUtils.get(query, key);
Document mappedValue = (Document) getMappedValue(field, theNestedObject);
if (!StringUtils.hasText(field.getMappedKey())) {
result.putAll(mappedValue);
} else {
result.put(field.getMappedKey(), mappedValue);
}
} else {

Entry<String, Object> entry = getMappedObjectForField(field, BsonUtils.get(query, key));

result.put(entry.getKey(), entry.getValue());
}
} catch (InvalidPersistentPropertyPath invalidPathException) {

// in case the object has not already been mapped
Expand Down Expand Up @@ -173,10 +189,16 @@ public Document getMappedSort(Document sortObject, @Nullable MongoPersistentEnti
return new Document();
}

sortObject = filterEmbeddedObjects(sortObject, entity);

Document mappedSort = new Document();
for (Map.Entry<String, Object> entry : BsonUtils.asMap(sortObject).entrySet()) {

Field field = createPropertyField(entity, entry.getKey(), mappingContext);
if (field.getProperty() != null && field.getProperty().isEmbedded()) {
continue;
}

mappedSort.put(field.getMappedKey(), entry.getValue());
}

Expand All @@ -197,7 +219,9 @@ public Document getMappedFields(Document fieldsObject, @Nullable MongoPersistent

Assert.notNull(fieldsObject, "FieldsObject must not be null!");

Document mappedFields = fieldsObject.isEmpty() ? new Document() : getMappedObject(fieldsObject, entity);
fieldsObject = filterEmbeddedObjects(fieldsObject, entity);

Document mappedFields = getMappedObject(fieldsObject, entity);
mapMetaAttributes(mappedFields, entity, MetaMapping.FORCE);
return mappedFields;
}
Expand All @@ -217,6 +241,44 @@ private void mapMetaAttributes(Document source, @Nullable MongoPersistentEntity<
}
}

private Document filterEmbeddedObjects(Document fieldsObject, @Nullable MongoPersistentEntity<?> entity) {

if (fieldsObject.isEmpty() || entity == null) {
return fieldsObject;
}

Document target = new Document();

for (Entry<String, Object> field : fieldsObject.entrySet()) {

try {

PropertyPath path = PropertyPath.from(field.getKey(), entity.getTypeInformation());
PersistentPropertyPath<MongoPersistentProperty> persistentPropertyPath = mappingContext
.getPersistentPropertyPath(path);
MongoPersistentProperty property = mappingContext.getPersistentPropertyPath(path).getRequiredLeafProperty();

if (property.isEmbedded() && property.isEntity()) {

MongoPersistentEntity<?> embeddedEntity = mappingContext.getRequiredPersistentEntity(property);

for (MongoPersistentProperty embedded : embeddedEntity) {

DotPath dotPath = DotPath.from(persistentPropertyPath.toDotPath()).append(embedded.getName());
target.put(dotPath.toString(), field.getValue());
}

} else {
target.put(field.getKey(), field.getValue());
}
} catch (RuntimeException e) {
target.put(field.getKey(), field.getValue());
}

}
return target;
}

private Document getMappedTextScoreField(MongoPersistentProperty property) {
return new Document(property.getFieldName(), META_TEXT_SCORE);
}
Expand Down Expand Up @@ -497,6 +559,11 @@ protected Object convertSimpleOrDocument(Object source, @Nullable MongoPersisten
*/
@Nullable
protected Object delegateConvertToMongoType(Object source, @Nullable MongoPersistentEntity<?> entity) {

if (entity != null && entity.isEmbedded()) {
return converter.convertToMongoType(source, entity);
}

return converter.convertToMongoType(source, entity == null ? null : entity.getTypeInformation());
}

Expand Down Expand Up @@ -912,6 +979,7 @@ public boolean isMap() {
public TypeInformation<?> getTypeHint() {
return ClassTypeInformation.OBJECT;
}

}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ public static boolean isUpdateObject(@Nullable Document updateObj) {
*/
@Override
protected Object delegateConvertToMongoType(Object source, @Nullable MongoPersistentEntity<?> entity) {

if(entity != null && entity.isEmbedded()) {
return converter.convertToMongoType(source, entity);
}

return converter.convertToMongoType(source,
entity == null ? ClassTypeInformation.OBJECT : getTypeHintForEntity(source, entity));
}
Expand Down
Loading