Skip to content

DATAMONGO-2215 - Add support for array filters to Update. #656

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 4 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>2.2.0.BUILD-SNAPSHOT</version>
<version>2.2.0.DATAMONGO-2215-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>2.2.0.BUILD-SNAPSHOT</version>
<version>2.2.0.DATAMONGO-2215-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
4 changes: 2 additions & 2 deletions spring-data-mongodb-cross-store/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>2.2.0.BUILD-SNAPSHOT</version>
<version>2.2.0.DATAMONGO-2215-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down Expand Up @@ -50,7 +50,7 @@
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>2.2.0.BUILD-SNAPSHOT</version>
<version>2.2.0.DATAMONGO-2215-SNAPSHOT</version>
</dependency>

<!-- reactive -->
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>2.2.0.BUILD-SNAPSHOT</version>
<version>2.2.0.DATAMONGO-2215-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>2.2.0.BUILD-SNAPSHOT</version>
<version>2.2.0.DATAMONGO-2215-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,5 +137,14 @@ public void inc(String version) {
public Boolean isIsolated() {
return delegate.isIsolated();
}

/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.query.UpdateDefinition#getArrayFilters()
*/
@Override
public List<ArrayFilter> getArrayFilters() {
return delegate.getArrayFilters();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.mongodb.core.query.UpdateDefinition.ArrayFilter;
import org.springframework.data.mongodb.core.validation.Validator;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.util.CloseableIterator;
Expand Down Expand Up @@ -1587,6 +1588,11 @@ public UpdateResult doInCollection(MongoCollection<Document> collection)
UpdateOptions opts = new UpdateOptions();
opts.upsert(upsert);

if (update.hasArrayFilters()) {
opts.arrayFilters(
update.getArrayFilters().stream().map(ArrayFilter::asDocument).collect(Collectors.toList()));
}

Document queryObj = new Document();

if (query != null) {
Expand Down Expand Up @@ -2551,7 +2557,9 @@ protected <T> T doFindAndModify(String collectionName, Document query, Document
collectionName);
}

return executeFindOneInternal(new FindAndModifyCallback(mappedQuery, fields, sort, mappedUpdate, options),
return executeFindOneInternal(
new FindAndModifyCallback(mappedQuery, fields, sort, mappedUpdate,
update.getArrayFilters().stream().map(ArrayFilter::asDocument).collect(Collectors.toList()), options),
new ReadDocumentCallback<>(readerToUse, entityClass, collectionName), collectionName);
}

Expand Down Expand Up @@ -2908,14 +2916,16 @@ private static class FindAndModifyCallback implements CollectionCallback<Documen
private final Document fields;
private final Document sort;
private final Document update;
private final List<Document> arrayFilters;
private final FindAndModifyOptions options;

public FindAndModifyCallback(Document query, Document fields, Document sort, Document update,
FindAndModifyOptions options) {
List<Document> arrayFilters, FindAndModifyOptions options) {
this.query = query;
this.fields = fields;
this.sort = sort;
this.update = update;
this.arrayFilters = arrayFilters;
this.options = options;
}

Expand All @@ -2933,6 +2943,10 @@ public Document doInCollection(MongoCollection<Document> collection) throws Mong

options.getCollation().map(Collation::toMongoCollation).ifPresent(opts::collation);

if (!arrayFilters.isEmpty()) {
opts.arrayFilters(arrayFilters);
}

return collection.findOneAndUpdate(query, update, opts);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.mongodb.core.query.UpdateDefinition.ArrayFilter;
import org.springframework.data.mongodb.core.validation.Validator;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.util.Optionals;
Expand Down Expand Up @@ -1640,6 +1641,11 @@ protected Mono<UpdateResult> doUpdate(String collectionName, Query query, @Nulla
UpdateOptions updateOptions = new UpdateOptions().upsert(upsert);
query.getCollation().map(Collation::toMongoCollation).ifPresent(updateOptions::collation);

if (update.hasArrayFilters()) {
updateOptions.arrayFilters(update.getArrayFilters().stream().map(ArrayFilter::asDocument)
.map(it -> queryMapper.getMappedObject(it, entity)).collect(Collectors.toList()));
}

if (!UpdateMapper.isUpdateObject(updateObj)) {

ReplaceOptions replaceOptions = new ReplaceOptions();
Expand Down Expand Up @@ -2367,7 +2373,7 @@ protected <T> Mono<T> doFindAndModify(String collectionName, Document query, Doc
collectionName));
}

return executeFindOneInternal(new FindAndModifyCallback(mappedQuery, fields, sort, mappedUpdate, options),
return executeFindOneInternal(new FindAndModifyCallback(mappedQuery, fields, sort, mappedUpdate, update.getArrayFilters().stream().map(ArrayFilter::asDocument).collect(Collectors.toList()), options),
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName), collectionName);
});
}
Expand Down Expand Up @@ -2751,6 +2757,7 @@ private static class FindAndModifyCallback implements ReactiveCollectionCallback
private final Document fields;
private final Document sort;
private final Document update;
private final List<Document> arrayFilters;
private final FindAndModifyOptions options;

@Override
Expand All @@ -2766,12 +2773,12 @@ public Publisher<Document> doInCollection(MongoCollection<Document> collection)
return collection.findOneAndDelete(query, findOneAndDeleteOptions);
}

FindOneAndUpdateOptions findOneAndUpdateOptions = convertToFindOneAndUpdateOptions(options, fields, sort);
FindOneAndUpdateOptions findOneAndUpdateOptions = convertToFindOneAndUpdateOptions(options, fields, sort, arrayFilters);
return collection.findOneAndUpdate(query, update, findOneAndUpdateOptions);
}

private FindOneAndUpdateOptions convertToFindOneAndUpdateOptions(FindAndModifyOptions options, Document fields,
Document sort) {
private static FindOneAndUpdateOptions convertToFindOneAndUpdateOptions(FindAndModifyOptions options, Document fields,
Document sort, List<Document> arrayFilters) {

FindOneAndUpdateOptions result = new FindOneAndUpdateOptions();

Expand All @@ -2784,6 +2791,7 @@ private FindOneAndUpdateOptions convertToFindOneAndUpdateOptions(FindAndModifyOp
}

result = options.getCollation().map(Collation::toMongoCollation).map(result::collation).orElse(result);
result.arrayFilters(arrayFilters);

return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.bson.BsonValue;
import org.bson.Document;
Expand Down Expand Up @@ -758,6 +760,8 @@ public boolean isJsonSchema() {
*/
protected static class Field {

protected static final Pattern POSITIONAL_PARM = Pattern.compile("\\$\\[.*\\]");

private static final String ID_KEY = "_id";

protected final String name;
Expand Down Expand Up @@ -1030,7 +1034,10 @@ private PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpre

try {

PropertyPath path = PropertyPath.from(pathExpression.replaceAll("\\.\\d+", ""), entity.getTypeInformation());
String rawPath = pathExpression.replaceAll("\\.\\d+", "") //
.replaceAll(POSITIONAL_PARM.pattern(), "");

PropertyPath path = PropertyPath.from(rawPath, entity.getTypeInformation());

if (isPathToJavaLangClassProperty(path)) {
return null;
Expand Down Expand Up @@ -1179,6 +1186,11 @@ private static boolean isPositionalParameter(String partial) {
return true;
}

Matcher matcher = POSITIONAL_PARM.matcher(partial);
if (matcher.find()) {
return true;
}

try {
Long.valueOf(partial);
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ private static class MetadataBackedUpdateField extends MetadataBackedField {
public MetadataBackedUpdateField(MongoPersistentEntity<?> entity, String key,
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {

super(key.replaceAll("\\.\\$", ""), entity, mappingContext);
super(key.replaceAll("\\.\\$(\\[.*\\])?", ""), entity, mappingContext);
this.key = key;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package org.springframework.data.mongodb.core.query;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
Expand Down Expand Up @@ -58,6 +59,7 @@ public enum Position {
private Set<String> keysToUpdate = new HashSet<>();
private Map<String, Object> modifierOps = new LinkedHashMap<>();
private Map<String, PushOperatorBuilder> pushCommandBuilders = new LinkedHashMap<>(1);
private List<ArrayFilter> arrayFilters = new ArrayList<>();

/**
* Static factory method to create an Update using the provided key
Expand Down Expand Up @@ -399,6 +401,33 @@ public Update isolated() {
return this;
}

/**
* Filter elements in an array that match the given criteria for update.
*
* @param criteria must not be {@literal null}.
* @return this.
* @since 2.2
*/
public Update filterArray(CriteriaDefinition criteria) {

this.arrayFilters.add(() -> criteria.getCriteriaObject());
return this;
}

/**
* Filter elements in an array that match the given criteria for update.
*
* @param identifier the positional operator identifier filter criteria name.
* @param expression the positional operator filter expression.
* @return this.
* @since 2.2
*/
public Update filterArray(String identifier, Object expression) {

this.arrayFilters.add(() -> new Document(identifier, expression));
return this;
}

/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.query.UpdateDefinition#isIsolated()
Expand All @@ -415,6 +444,14 @@ public Document getUpdateObject() {
return new Document(modifierOps);
}

/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.query.UpdateDefinition#getArrayFilters()
*/
public List<ArrayFilter> getArrayFilters() {
return Collections.unmodifiableList(this.arrayFilters);
}

/**
* This method is not called anymore rather override {@link #addMultiFieldOperation(String, String, Object)}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package org.springframework.data.mongodb.core.query;

import java.util.List;

import org.bson.Document;

/**
Expand Down Expand Up @@ -53,4 +55,36 @@ public interface UpdateDefinition {
* @param key must not be {@literal null}.
*/
void inc(String key);

/**
* Get the specification which elements to modify in an array field.
*
* @return never {@literal null}.
* @since 2.2
*/
List<ArrayFilter> getArrayFilters();

/**
* @return {@literal true} if {@link UpdateDefinition} contains {@link #getArrayFilters() array filters}.
* @since 2.2
*/
default boolean hasArrayFilters() {
return !getArrayFilters().isEmpty();
}

/**
* A filter to specify which elements to modify in an array field.
*
* @since 2.2
*/
interface ArrayFilter {

/**
* Get the {@link Document} representation of the filter to apply. The returned Document is subject to mapping
* domain type filed names.
*
* @return never {@literal null}.
*/
Document asDocument();
}
}
Loading