in grails-data-mongodb/core/src/main/groovy/org/grails/datastore/mapping/mongo/engine/codecs/PersistentEntityCodec.groovy [202:359]
Document encodeUpdate(Object value, EntityAccess access = createEntityAccess(value), EncoderContext encoderContext = DEFAULT_ENCODER_CONTEXT, boolean embedded = false) {
Document update = new Document()
def entity = access.persistentEntity
def proxyFactory = mappingContext.proxyFactory
if( proxyFactory.isProxy(value) ) {
value = proxyFactory.unwrap(value)
}
if(value instanceof DirtyCheckable) {
def sets = new BsonDocument()
def unsets = new Document()
BsonWriter writer = new BsonDocumentWriter(sets)
writer.writeStartDocument()
DirtyCheckable dirty = (DirtyCheckable)value
Set<String> processed = []
def dirtyProperties = new ArrayList<String>(dirty.listDirtyPropertyNames())
boolean isNew = dirtyProperties.isEmpty() && dirty.hasChanged()
def isVersioned = entity.isVersioned()
if(isNew) {
// if it is new it can only be an embedded entity that has now been updated
// so we get all properties
dirtyProperties = entity.persistentPropertyNames
if(!entity.isRoot()) {
sets.put(MongoConstants.MONGO_CLASS_FIELD, new BsonString(entity.discriminator))
}
if(isVersioned) {
EntityPersister.incrementEntityVersion(access)
}
}
else {
// schedule lastUpdated if necessary
if( entity.getPropertyByName(GormProperties.LAST_UPDATED) != null) {
dirtyProperties.add(GormProperties.LAST_UPDATED)
}
}
for(propertyName in dirtyProperties) {
def prop = entity.getPropertyByName(propertyName)
if(prop != null) {
processed << propertyName
Object v = access.getProperty(prop.name)
if (v != null) {
if(prop instanceof Embedded) {
encodeEmbeddedUpdate(sets,unsets, (Association)prop, v)
}
else if(prop instanceof EmbeddedCollection) {
encodeEmbeddedCollectionUpdate(access, sets, unsets, (Association)prop, v)
}
else {
def propKind = prop.getClass().superclass
PropertyEncoder<? extends PersistentProperty> propertyEncoder = getPropertyEncoder((Class<? extends PersistentProperty>)propKind)
propertyEncoder?.encode(writer, prop, v, access, encoderContext, codecRegistry)
}
}
else if(embedded || !isNew) {
unsets[prop.name] = BLANK_STRING
}
}
}
if(value instanceof DynamicAttributes) {
Map<String, Object> attributes = ((DynamicAttributes) value).attributes()
for(attr in attributes.keySet()) {
Object v = attributes.get(attr)
if(v == null) {
unsets.put(attr,BLANK_STRING)
}
else {
writer.writeName(attr)
Codec<Object> codec = (Codec<Object>)codecRegistry.get(v.getClass())
codec.encode(writer, v, encoderContext)
}
}
}
else {
GormEnhancer.findStaticApi(entity.javaClass).withSession { Session mongoSession ->
if(mongoSession != null) {
Document schemaless = (Document)mongoSession.getAttribute(value, SCHEMALESS_ATTRIBUTES)
if(schemaless != null) {
for(name in schemaless.keySet()) {
def v = schemaless.get(name)
if(v == null) {
unsets.put(name,BLANK_STRING)
}
else {
writer.writeName(name)
Codec<Object> codec = (Codec<Object>)codecRegistry.get(v.getClass())
codec.encode(writer, v, encoderContext)
}
}
}
}
}
}
for(association in entity.associations) {
if(processed.contains( association.name )) continue
if(association instanceof OneToMany) {
def v = access.getProperty(association.name)
if (v != null) {
// TODO: handle unprocessed association
}
}
else if(association instanceof ToOne) {
def v = access.getProperty(association.name)
if( v instanceof DirtyCheckable ) {
if(((DirtyCheckable)v).hasChanged()) {
if(association instanceof Embedded) {
encodeEmbeddedUpdate(sets, unsets, association, v)
}
}
}
}
else if(association instanceof EmbeddedCollection) {
def v = access.getProperty(association.name)
if( v instanceof DirtyCheckableCollection ) {
if(((DirtyCheckableCollection)v).hasChanged()) {
encodeEmbeddedCollectionUpdate(access, sets, unsets, association, v)
}
}
}
}
boolean hasSets = !sets.isEmpty()
boolean hasUnsets = !unsets.isEmpty()
if(hasSets && isVersioned) {
def version = entity.version
def propKind = version.getClass().superclass
MongoCodecEntityPersister.incrementEntityVersion(access)
def v = access.getProperty(version.name)
getPropertyEncoder((Class<? extends PersistentProperty>) propKind)?.encode(writer, version, v, access, encoderContext, codecRegistry)
}
writer.writeEndDocument()
if(hasSets) {
update.put(MONGO_SET_OPERATOR, sets)
}
if(hasUnsets) {
update.put(MONGO_UNSET_OPERATOR,unsets)
}
}
else {
// TODO: Support non-dirty checkable objects?
}
return update
}