Skip to content

Commit 26bd879

Browse files
committed
DATAMONGO-1141 - Add support for $push $sort in Update.
Sorting update modifier added. Supports sorting arrays by document fields and element values.
1 parent d0674e4 commit 26bd879

File tree

2 files changed

+153
-0
lines changed

2 files changed

+153
-0
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
import java.util.Set;
2828

2929
import org.springframework.dao.InvalidDataAccessApiUsageException;
30+
import org.springframework.data.domain.Sort;
31+
import org.springframework.data.domain.Sort.Direction;
32+
import org.springframework.data.domain.Sort.Order;
3033
import org.springframework.util.Assert;
3134
import org.springframework.util.StringUtils;
3235

@@ -44,6 +47,7 @@
4447
* @author Thomas Darimont
4548
* @author Alexey Plotnik
4649
* @author Mark Paluch
50+
* @author Pavel Vodrazka
4751
*/
4852
public class Update {
4953

@@ -660,6 +664,58 @@ public Object getValue() {
660664
return this.count;
661665
}
662666
}
667+
668+
/**
669+
* Implementation of {@link Modifier} representing {@code $sort}.
670+
*
671+
* @author Pavel Vodrazka
672+
* @since 1.10
673+
*/
674+
private static class SortModifier implements Modifier {
675+
676+
private final Object sort;
677+
678+
public SortModifier(Direction direction) {
679+
this.sort = direction.isAscending() ? 1 : -1;
680+
}
681+
682+
public SortModifier(Sort sort) {
683+
this.sort = createDBObject(sort);
684+
}
685+
686+
private DBObject createDBObject(Sort sort) {
687+
688+
DBObject obj = new BasicDBObject();
689+
690+
for (Order order : sort) {
691+
if (order.isIgnoreCase()) {
692+
throw new IllegalArgumentException(String.format("Given sort contained an Order for %s with ignore case! "
693+
+ "MongoDB does not support sorting ignoring case currently!", order.getProperty()));
694+
}
695+
obj.put(order.getProperty(), order.isAscending() ? 1 : -1);
696+
}
697+
698+
return obj;
699+
}
700+
701+
/*
702+
* (non-Javadoc)
703+
* @see org.springframework.data.mongodb.core.query.Update.Modifier#getKey()
704+
*/
705+
@Override
706+
public String getKey() {
707+
return "$sort";
708+
}
709+
710+
/*
711+
* (non-Javadoc)
712+
* @see org.springframework.data.mongodb.core.query.Update.Modifier#getValue()
713+
*/
714+
@Override
715+
public Object getValue() {
716+
return this.sort;
717+
}
718+
}
663719

664720
/**
665721
* Builder for creating {@code $push} modifiers
@@ -707,6 +763,36 @@ public PushOperatorBuilder slice(int count) {
707763
return this;
708764
}
709765

766+
/**
767+
* Propagates {@code $sort} to {@code $push}. {@code $sort} requires the {@code $each} operator.
768+
* Forces elements to be sorted by values in given {@literal direction}.
769+
*
770+
* @param direction must not be {@literal null}.
771+
* @return never {@literal null}.
772+
* @since 1.10
773+
*/
774+
public PushOperatorBuilder sort(Direction direction) {
775+
776+
Assert.notNull(direction, "Direction must not be 'null'.");
777+
this.modifiers.addModifier(new SortModifier(direction));
778+
return this;
779+
}
780+
781+
/**
782+
* Propagates {@code $sort} to {@code $push}. {@code $sort} requires the {@code $each} operator.
783+
* Forces document elements to be sorted in given {@literal order}.
784+
*
785+
* @param order must not be {@literal null}.
786+
* @return never {@literal null}.
787+
* @since 1.10
788+
*/
789+
public PushOperatorBuilder sort(Sort order) {
790+
791+
Assert.notNull(order, "Order must not be 'null'.");
792+
this.modifiers.addModifier(new SortModifier(order));
793+
return this;
794+
}
795+
710796
/**
711797
* Forces values to be added at the given {@literal position}.
712798
*

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.data.mongodb.core.convert;
1717

1818
import static org.hamcrest.CoreMatchers.*;
19+
import static org.hamcrest.Matchers.equalTo;
1920
import static org.hamcrest.collection.IsMapContaining.*;
2021
import static org.junit.Assert.*;
2122
import static org.mockito.Mockito.*;
@@ -42,6 +43,9 @@
4243
import org.springframework.core.convert.converter.Converter;
4344
import org.springframework.data.annotation.Id;
4445
import org.springframework.data.convert.WritingConverter;
46+
import org.springframework.data.domain.Sort;
47+
import org.springframework.data.domain.Sort.Direction;
48+
import org.springframework.data.domain.Sort.Order;
4549
import org.springframework.data.mapping.model.MappingException;
4650
import org.springframework.data.mongodb.MongoDbFactory;
4751
import org.springframework.data.mongodb.core.DBObjectTestUtils;
@@ -68,6 +72,7 @@
6872
* @author Christoph Strobl
6973
* @author Thomas Darimont
7074
* @author Mark Paluch
75+
* @author Pavel Vodrazka
7176
*/
7277
@RunWith(MockitoJUnitRunner.class)
7378
public class UpdateMapperUnitTests {
@@ -416,6 +421,68 @@ public void updatePushEachWithSliceShouldRenderWhenUsingMultiplePushCorrectly()
416421
assertThat(key2.containsField("$each"), is(true));
417422
}
418423

424+
/**
425+
* @see DATAMONGO-1141
426+
*/
427+
@Test
428+
public void updatePushEachWithValueSortShouldRenderCorrectly() {
429+
430+
Update update = new Update().push("scores").sort(Direction.DESC).each(42, 23, 68);
431+
432+
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class));
433+
434+
DBObject push = getAsDBObject(mappedObject, "$push");
435+
DBObject key = getAsDBObject(push, "scores");
436+
437+
assertThat(key.containsField("$sort"), is(true));
438+
assertThat((Integer) key.get("$sort"), is(-1));
439+
assertThat(key.containsField("$each"), is(true));
440+
}
441+
442+
/**
443+
* @see DATAMONGO-1141
444+
*/
445+
@Test
446+
public void updatePushEachWithDocumentSortShouldRenderCorrectly() {
447+
448+
Update update = new Update().push("names").sort(new Sort(new Order(Direction.ASC, "last"), new Order(Direction.ASC, "first")))
449+
.each(Collections.emptyList());
450+
451+
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class));
452+
453+
DBObject push = getAsDBObject(mappedObject, "$push");
454+
DBObject key = getAsDBObject(push, "names");
455+
456+
assertThat(key.containsField("$sort"), is(true));
457+
assertThat((DBObject) key.get("$sort"), equalTo(new BasicDBObjectBuilder().add("last", 1).add("first", 1).get()));
458+
assertThat(key.containsField("$each"), is(true));
459+
}
460+
461+
/**
462+
* @see DATAMONGO-1141
463+
*/
464+
@Test
465+
public void updatePushEachWithSortShouldRenderCorrectlyWhenUsingMultiplePush() {
466+
467+
Update update = new Update().push("authors").sort(Direction.ASC).each("Harry")
468+
.push("chapters").sort(new Sort(Direction.ASC, "order")).each(Collections.emptyList());
469+
470+
DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class));
471+
472+
DBObject push = getAsDBObject(mappedObject, "$push");
473+
DBObject key1 = getAsDBObject(push, "authors");
474+
475+
assertThat(key1.containsField("$sort"), is(true));
476+
assertThat((Integer) key1.get("$sort"), is(1));
477+
assertThat(key1.containsField("$each"), is(true));
478+
479+
DBObject key2 = getAsDBObject(push, "chapters");
480+
481+
assertThat(key2.containsField("$sort"), is(true));
482+
assertThat((DBObject) key2.get("$sort"), equalTo(new BasicDBObjectBuilder().add("order", 1).get()));
483+
assertThat(key2.containsField("$each"), is(true));
484+
}
485+
419486
/**
420487
* @see DATAMONGO-410
421488
*/

0 commit comments

Comments
 (0)