diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/Collation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/Collation.java
index 3dae36ee8..a0b7d9e51 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/Collation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/Collation.java
@@ -15,12 +15,18 @@
*/
package org.springframework.data.mongodb.core;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
import java.util.Locale;
import java.util.Optional;
import org.bson.Document;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
import com.mongodb.client.model.Collation.Builder;
import com.mongodb.client.model.CollationAlternate;
@@ -37,45 +43,36 @@ import com.mongodb.client.model.CollationStrength;
* query itself specifies the same collation.
*
* @author Christoph Strobl
+ * @author Mark Paluch
* @since 2.0
* @see MongoDB Reference - Collation
*/
public class Collation {
- private static final Collation DEFAULT = of("simple");
+ private static final Collation SIMPLE = of("simple");
- private final ICULocale locale;
+ private final CollationLocale locale;
- private Optional strength = Optional.empty();
+ private Optional strength = Optional.empty();
private Optional numericOrdering = Optional.empty();
private Optional alternate = Optional.empty();
private Optional backwards = Optional.empty();
private Optional normalization = Optional.empty();
private Optional version = Optional.empty();
- private Collation(ICULocale locale) {
+ private Collation(CollationLocale locale) {
Assert.notNull(locale, "ICULocale must not be null!");
this.locale = locale;
}
/**
- * Create new {@link Collation} using simple binary comparison.
+ * Create a {@link Collation} using {@literal simple} binary comparison.
*
- * @return
- * @see #binary()
+ * @return a {@link Collation} for {@literal simple} binary comparison.
*/
public static Collation simple() {
- return binary();
- }
-
- /**
- * Create new {@link Collation} using simple binary comparison.
- *
- * @return
- */
- public static Collation binary() {
- return DEFAULT;
+ return SIMPLE;
}
/**
@@ -88,7 +85,16 @@ public class Collation {
public static Collation of(Locale locale) {
Assert.notNull(locale, "Locale must not be null!");
- return of(ICULocale.of(locale.getLanguage()).variant(locale.getVariant()));
+
+ String format;
+
+ if (StringUtils.hasText(locale.getCountry())) {
+ format = String.format("%s_%s", locale.getLanguage(), locale.getCountry());
+ } else {
+ format = locale.getLanguage();
+ }
+
+ return of(CollationLocale.of(format).variant(locale.getVariant()));
}
/**
@@ -98,16 +104,16 @@ public class Collation {
* @return
*/
public static Collation of(String language) {
- return of(ICULocale.of(language));
+ return of(CollationLocale.of(language));
}
/**
- * Create new {@link Collation} with locale set to the given {@link ICULocale}.
+ * Create new {@link Collation} with locale set to the given {@link CollationLocale}.
*
* @param locale must not be {@literal null}.
* @return
*/
- public static Collation of(ICULocale locale) {
+ public static Collation of(CollationLocale locale) {
return new Collation(locale);
}
@@ -157,13 +163,13 @@ public class Collation {
/**
* Set the level of comparison to perform.
*
- * @param strength must not be {@literal null}.
+ * @param strength
* @return new {@link Collation}.
*/
- public Collation strength(Integer strength) {
+ public Collation strength(int strength) {
- ICUComparisonLevel current = this.strength.orElseGet(() -> new ICUComparisonLevel(strength, null, null));
- return strength(new ICUComparisonLevel(strength, current.caseFirst.orElse(null), current.caseLevel.orElse(null)));
+ ComparisonLevel current = this.strength.orElseGet(() -> new ICUComparisonLevel(strength));
+ return strength(new ICUComparisonLevel(strength, current.getCaseFirst(), current.getCaseLevel()));
}
/**
@@ -172,23 +178,24 @@ public class Collation {
* @param comparisonLevel must not be {@literal null}.
* @return new {@link Collation}
*/
- public Collation strength(ICUComparisonLevel comparisonLevel) {
+ public Collation strength(ComparisonLevel comparisonLevel) {
Collation newInstance = copy();
- newInstance.strength = Optional.ofNullable(comparisonLevel);
+ newInstance.strength = Optional.of(comparisonLevel);
return newInstance;
}
/**
- * Set {@code caseLevel} comarison.
+ * Set whether to include {@code caseLevel} comparison.
*
- * @param caseLevel must not be {@literal null}.
+ * @param caseLevel
* @return new {@link Collation}.
*/
- public Collation caseLevel(Boolean caseLevel) {
+ public Collation caseLevel(boolean caseLevel) {
- ICUComparisonLevel strengthValue = strength.orElseGet(() -> ICUComparisonLevel.primary());
- return strength(new ICUComparisonLevel(strengthValue.level, strengthValue.caseFirst.orElse(null), caseLevel));
+ ComparisonLevel strengthValue = strength.orElseGet(ComparisonLevel::primary);
+ return strength(
+ new ICUComparisonLevel(strengthValue.getLevel(), strengthValue.getCaseFirst(), Optional.of(caseLevel)));
}
/**
@@ -198,7 +205,7 @@ public class Collation {
* @return
*/
public Collation caseFirst(String caseFirst) {
- return caseFirst(new ICUCaseFirst(caseFirst));
+ return caseFirst(new CaseFirst(caseFirst));
}
/**
@@ -207,10 +214,10 @@ public class Collation {
* @param caseFirst must not be {@literal null}.
* @return
*/
- public Collation caseFirst(ICUCaseFirst sort) {
+ public Collation caseFirst(CaseFirst sort) {
- ICUComparisonLevel strengthValue = strength.orElseGet(() -> ICUComparisonLevel.tertiary());
- return strength(new ICUComparisonLevel(strengthValue.level, sort, strengthValue.caseLevel.orElse(null)));
+ ComparisonLevel strengthValue = strength.orElseGet(ComparisonLevel::tertiary);
+ return strength(new ICUComparisonLevel(strengthValue.getLevel(), Optional.of(sort), strengthValue.getCaseLevel()));
}
/**
@@ -236,10 +243,10 @@ public class Collation {
*
* @return new {@link Collation}.
*/
- public Collation numericOrdering(Boolean flag) {
+ public Collation numericOrdering(boolean flag) {
Collation newInstance = copy();
- newInstance.numericOrdering = Optional.ofNullable(flag);
+ newInstance.numericOrdering = Optional.of(flag);
return newInstance;
}
@@ -252,8 +259,8 @@ public class Collation {
*/
public Collation alternate(String alternate) {
- Alternate instance = this.alternate.orElseGet(() -> new Alternate(alternate, null));
- return alternate(new Alternate(alternate, instance.maxVariable.orElse(null)));
+ Alternate instance = this.alternate.orElseGet(() -> new Alternate(alternate, Optional.empty()));
+ return alternate(new Alternate(alternate, instance.maxVariable));
}
/**
@@ -340,7 +347,7 @@ public class Collation {
*/
public Collation maxVariable(String maxVariable) {
- Alternate alternateValue = alternate.orElseGet(() -> Alternate.shifted());
+ Alternate alternateValue = alternate.orElseGet(Alternate::shifted);
return alternate(new AlternateWithMaxVariable(alternateValue.alternate, maxVariable));
}
@@ -362,6 +369,13 @@ public class Collation {
return map(toMongoCollationConverter());
}
+ /**
+ * Transform {@code this} {@link Collation} by applying a {@link Converter}.
+ *
+ * @param mapper
+ * @param
+ * @return
+ */
public R map(Converter super Collation, ? extends R> mapper) {
return mapper.convert(this);
}
@@ -387,39 +401,30 @@ public class Collation {
*
* @since 2.0
*/
- public static class ICUComparisonLevel {
-
- protected final Integer level;
- private final Optional caseFirst;
- private final Optional caseLevel;
-
- private ICUComparisonLevel(Integer level, ICUCaseFirst caseFirst, Boolean caseLevel) {
-
- this.level = level;
- this.caseFirst = Optional.ofNullable(caseFirst);
- this.caseLevel = Optional.ofNullable(caseLevel);
- }
+ public interface ComparisonLevel {
/**
* Primary level of comparison. Collation performs comparisons of the base characters only, ignoring other
* differences such as diacritics and case.
- * The {@code caseLevel} can be set via {@link ComparisonLevelWithCase#caseLevel(Boolean)}.
+ * The {@code caseLevel} can be set via {@link PrimaryICUComparisonLevel#includeCase()} and
+ * {@link PrimaryICUComparisonLevel#excludeCase()}.
*
- * @return new {@link ComparisonLevelWithCase}.
+ * @return new {@link SecondaryICUComparisonLevel}.
*/
- public static PrimaryICUComparisonLevel primary() {
- return new PrimaryICUComparisonLevel(1, null);
+ static PrimaryICUComparisonLevel primary() {
+ return PrimaryICUComparisonLevel.DEFAULT;
}
/**
- * Scondary level of comparison. Collation performs comparisons up to secondary differences, such as
+ * Secondary level of comparison. Collation performs comparisons up to secondary differences, such as
* diacritics.
- * The {@code caseLevel} can be set via {@link ComparisonLevelWithCase#caseLevel(Boolean)}.
+ * The {@code caseLevel} can be set via {@link SecondaryICUComparisonLevel#includeCase()} and
+ * {@link SecondaryICUComparisonLevel#excludeCase()}.
*
- * @return new {@link ComparisonLevelWithCase}.
+ * @return new {@link SecondaryICUComparisonLevel}.
*/
- public static SecondaryICUComparisonLevel secondary() {
- return new SecondaryICUComparisonLevel(2, null);
+ static SecondaryICUComparisonLevel secondary() {
+ return SecondaryICUComparisonLevel.DEFAULT;
}
/**
@@ -429,159 +434,231 @@ public class Collation {
*
* @return new {@link ICUComparisonLevel}.
*/
- public static TertiaryICUComparisonLevel tertiary() {
- return new TertiaryICUComparisonLevel(3, null);
+ static TertiaryICUComparisonLevel tertiary() {
+ return TertiaryICUComparisonLevel.DEFAULT;
}
/**
* Quaternary Level. Limited for specific use case to consider punctuation.
* The {@code caseLevel} cannot be set for {@link ICUComparisonLevel} above {@code secondary}.
*
- * @return new {@link ICUComparisonLevel}.
+ * @return new {@link ComparisonLevel}.
*/
- public static ICUComparisonLevel quaternary() {
- return new ICUComparisonLevel(4, null, null);
+ static ComparisonLevel quaternary() {
+ return ComparisonLevels.QUATERNARY;
}
/**
* Identical Level. Limited for specific use case of tie breaker.
* The {@code caseLevel} cannot be set for {@link ICUComparisonLevel} above {@code secondary}.
*
- * @return new {@link ICUComparisonLevel}.
+ * @return new {@link ComparisonLevel}.
*/
- public static ICUComparisonLevel identical() {
- return new ICUComparisonLevel(5, null, null);
+ static ComparisonLevel identical() {
+ return ComparisonLevels.IDENTICAL;
+ }
+
+ /**
+ * @return collation strength, {@literal 1} for primary, {@literal 2} for secondary and so on.
+ */
+ int getLevel();
+
+ default Optional getCaseFirst() {
+ return Optional.empty();
+ }
+
+ default Optional getCaseLevel() {
+ return Optional.empty();
}
}
+ /**
+ * Abstraction for the ICU Comparison Levels.
+ *
+ * @since 2.0
+ */
+ @AllArgsConstructor(access = AccessLevel.PACKAGE)
+ @Getter
+ static class ICUComparisonLevel implements ComparisonLevel {
+
+ private final int level;
+ private final Optional caseFirst;
+ private final Optional caseLevel;
+
+ ICUComparisonLevel(int level) {
+ this(level, Optional.empty(), Optional.empty());
+ }
+ }
+
+ /**
+ * Simple comparison levels.
+ */
+ enum ComparisonLevels implements ComparisonLevel {
+
+ QUATERNARY(4), IDENTICAL(5);
+
+ private final int level;
+
+ ComparisonLevels(int level) {
+ this.level = level;
+ }
+
+ @Override
+ public int getLevel() {
+ return level;
+ }
+ }
+
+ /**
+ * Primary-strength {@link ICUComparisonLevel}.
+ */
+ public static class PrimaryICUComparisonLevel extends ICUComparisonLevel {
+
+ static final PrimaryICUComparisonLevel DEFAULT = new PrimaryICUComparisonLevel();
+ static final PrimaryICUComparisonLevel WITH_CASE_LEVEL = new PrimaryICUComparisonLevel(true);
+ static final PrimaryICUComparisonLevel WITHOUT_CASE_LEVEL = new PrimaryICUComparisonLevel(false);
+
+ private PrimaryICUComparisonLevel() {
+ super(1);
+ }
+
+ private PrimaryICUComparisonLevel(boolean caseLevel) {
+ super(1, Optional.empty(), Optional.of(caseLevel));
+ }
+
+ /**
+ * Include case comparison.
+ *
+ * @return new {@link ICUComparisonLevel}
+ */
+ public ComparisonLevel includeCase() {
+ return WITH_CASE_LEVEL;
+ }
+
+ /**
+ * Exclude case comparison.
+ *
+ * @return new {@link ICUComparisonLevel}
+ */
+ public ComparisonLevel excludeCase() {
+ return WITHOUT_CASE_LEVEL;
+ }
+ }
+
+ /**
+ * Secondary-strength {@link ICUComparisonLevel}.
+ */
+ public static class SecondaryICUComparisonLevel extends ICUComparisonLevel {
+
+ static final SecondaryICUComparisonLevel DEFAULT = new SecondaryICUComparisonLevel();
+ static final SecondaryICUComparisonLevel WITH_CASE_LEVEL = new SecondaryICUComparisonLevel(true);
+ static final SecondaryICUComparisonLevel WITHOUT_CASE_LEVEL = new SecondaryICUComparisonLevel(false);
+
+ private SecondaryICUComparisonLevel() {
+ super(2);
+ }
+
+ private SecondaryICUComparisonLevel(boolean caseLevel) {
+ super(2, Optional.empty(), Optional.of(caseLevel));
+ }
+
+ /**
+ * Include case comparison.
+ *
+ * @return new {@link SecondaryICUComparisonLevel}
+ */
+ public ComparisonLevel includeCase() {
+ return WITH_CASE_LEVEL;
+ }
+
+ /**
+ * Exclude case comparison.
+ *
+ * @return new {@link SecondaryICUComparisonLevel}
+ */
+ public ComparisonLevel excludeCase() {
+ return WITHOUT_CASE_LEVEL;
+ }
+ }
+
+ /**
+ * Tertiary-strength {@link ICUComparisonLevel}.
+ */
public static class TertiaryICUComparisonLevel extends ICUComparisonLevel {
- private TertiaryICUComparisonLevel(Integer level, ICUCaseFirst caseFirst) {
- super(level, caseFirst, null);
+ static final TertiaryICUComparisonLevel DEFAULT = new TertiaryICUComparisonLevel();
+
+ private TertiaryICUComparisonLevel() {
+ super(3);
+ }
+
+ private TertiaryICUComparisonLevel(CaseFirst caseFirst) {
+ super(3, Optional.of(caseFirst), Optional.empty());
}
/**
* Set the flag that determines sort order of case differences.
*
- * @param caseFirstSort must not be {@literal null}.
- * @return
+ * @param caseFirst must not be {@literal null}.
+ * @return new {@link ICUComparisonLevel}
*/
- public TertiaryICUComparisonLevel caseFirst(ICUCaseFirst caseFirst) {
+ public ComparisonLevel caseFirst(CaseFirst caseFirst) {
Assert.notNull(caseFirst, "CaseFirst must not be null!");
- return new TertiaryICUComparisonLevel(level, caseFirst);
- }
- }
-
- public static class PrimaryICUComparisonLevel extends ICUComparisonLevel {
-
- private PrimaryICUComparisonLevel(Integer level, Boolean caseLevel) {
- super(level, null, caseLevel);
- }
-
- /**
- * Include case comparison.
- *
- * @return new {@link ComparisonLevelWithCase}
- */
- public PrimaryICUComparisonLevel includeCase() {
- return caseLevel(Boolean.TRUE);
- }
-
- /**
- * Exclude case comparison.
- *
- * @return new {@link ComparisonLevelWithCase}
- */
- public PrimaryICUComparisonLevel excludeCase() {
- return caseLevel(Boolean.FALSE);
- }
-
- PrimaryICUComparisonLevel caseLevel(Boolean caseLevel) {
- return new PrimaryICUComparisonLevel(level, caseLevel);
- }
- }
-
- public static class SecondaryICUComparisonLevel extends ICUComparisonLevel {
-
- private SecondaryICUComparisonLevel(Integer level, Boolean caseLevel) {
- super(level, null, caseLevel);
- }
-
- /**
- * Include case comparison.
- *
- * @return new {@link ComparisonLevelWithCase}
- */
- public SecondaryICUComparisonLevel includeCase() {
- return caseLevel(Boolean.TRUE);
- }
-
- /**
- * Exclude case comparison.
- *
- * @return new {@link ComparisonLevelWithCase}
- */
- public SecondaryICUComparisonLevel excludeCase() {
- return caseLevel(Boolean.FALSE);
- }
-
- SecondaryICUComparisonLevel caseLevel(Boolean caseLevel) {
- return new SecondaryICUComparisonLevel(level, caseLevel);
+ return new TertiaryICUComparisonLevel(caseFirst);
}
}
/**
* @since 2.0
*/
- public static class ICUCaseFirst {
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ public static class CaseFirst {
+
+ private static final CaseFirst UPPER = new CaseFirst("upper");
+ private static final CaseFirst LOWER = new CaseFirst("lower");
+ private static final CaseFirst OFF = new CaseFirst("off");
private final String state;
- private ICUCaseFirst(String state) {
- this.state = state;
- }
-
/**
* Sort uppercase before lowercase.
*
- * @return new {@link ICUCaseFirst}.
+ * @return new {@link CaseFirst}.
*/
- public static ICUCaseFirst upper() {
- return new ICUCaseFirst("upper");
+ public static CaseFirst upper() {
+ return UPPER;
}
/**
* Sort lowercase before uppercase.
*
- * @return new {@link ICUCaseFirst}.
+ * @return new {@link CaseFirst}.
*/
- public static ICUCaseFirst lower() {
- return new ICUCaseFirst("lower");
+ public static CaseFirst lower() {
+ return LOWER;
}
/**
* Use the default.
*
- * @return new {@link ICUCaseFirst}.
+ * @return new {@link CaseFirst}.
*/
- public static ICUCaseFirst off() {
- return new ICUCaseFirst("off");
+ public static CaseFirst off() {
+ return OFF;
}
}
/**
* @since 2.0
*/
+ @RequiredArgsConstructor(access = AccessLevel.PACKAGE)
public static class Alternate {
- protected final String alternate;
- protected Optional maxVariable;
+ private static final Alternate NON_IGNORABLE = new Alternate("non-ignorable", Optional.empty());
- private Alternate(String alternate, String maxVariable) {
- this.alternate = alternate;
- this.maxVariable = Optional.ofNullable(maxVariable);
- }
+ final String alternate;
+ final Optional maxVariable;
/**
* Consider Whitespace and punctuation as base characters.
@@ -589,18 +666,18 @@ public class Collation {
* @return new {@link Alternate}.
*/
public static Alternate nonIgnorable() {
- return new Alternate("non-ignorable", null);
+ return NON_IGNORABLE;
}
/**
* Whitespace and punctuation are not considered base characters and are only distinguished at
* strength.
- * NOTE: Only works for {@link ICUComparisonLevel} above {@link ICUComparisonLevel#tertiary()}.
+ * NOTE: Only works for {@link ICUComparisonLevel} above {@link ComparisonLevel#tertiary()}.
*
* @return new {@link AlternateWithMaxVariable}.
*/
public static AlternateWithMaxVariable shifted() {
- return new AlternateWithMaxVariable("shifted", null);
+ return AlternateWithMaxVariable.DEFAULT;
}
}
@@ -609,8 +686,16 @@ public class Collation {
*/
public static class AlternateWithMaxVariable extends Alternate {
+ static final AlternateWithMaxVariable DEFAULT = new AlternateWithMaxVariable("shifted");
+ static final Alternate SHIFTED_PUNCT = new AlternateWithMaxVariable("shifted", "punct");
+ static final Alternate SHIFTED_SPACE = new AlternateWithMaxVariable("shifted", "space");
+
+ private AlternateWithMaxVariable(String alternate) {
+ super(alternate, Optional.empty());
+ }
+
private AlternateWithMaxVariable(String alternate, String maxVariable) {
- super(alternate, maxVariable);
+ super(alternate, Optional.of(maxVariable));
}
/**
@@ -618,8 +703,8 @@ public class Collation {
*
* @return new {@link AlternateWithMaxVariable}.
*/
- public AlternateWithMaxVariable punct() {
- return new AlternateWithMaxVariable(alternate, "punct");
+ public Alternate punct() {
+ return SHIFTED_PUNCT;
}
/**
@@ -627,10 +712,9 @@ public class Collation {
*
* @return new {@link AlternateWithMaxVariable}.
*/
- public AlternateWithMaxVariable space() {
- return new AlternateWithMaxVariable(alternate, "space");
+ public Alternate space() {
+ return SHIFTED_SPACE;
}
-
}
/**
@@ -639,38 +723,34 @@ public class Collation {
* @since 2.0
* @see ICU - International Components for Unicode
*/
- public static class ICULocale {
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ public static class CollationLocale {
private final String language;
private final Optional variant;
- private ICULocale(String language, String variant) {
- this.language = language;
- this.variant = Optional.ofNullable(variant);
- }
-
/**
- * Create new {@link ICULocale} for given language.
+ * Create new {@link CollationLocale} for given language.
*
* @param language must not be {@literal null}.
* @return
*/
- public static ICULocale of(String language) {
+ public static CollationLocale of(String language) {
Assert.notNull(language, "Code must not be null!");
- return new ICULocale(language, null);
+ return new CollationLocale(language, Optional.empty());
}
/**
* Define language variant.
*
* @param variant must not be {@literal null}.
- * @return new {@link ICULocale}.
+ * @return new {@link CollationLocale}.
*/
- public ICULocale variant(String variant) {
+ public CollationLocale variant(String variant) {
Assert.notNull(variant, "Variant must not be null!");
- return new ICULocale(language, variant);
+ return new CollationLocale(language, Optional.of(variant));
}
/**
@@ -681,12 +761,13 @@ public class Collation {
public String asString() {
StringBuilder sb = new StringBuilder(language);
- variant.ifPresent(val -> {
- if (!val.isEmpty()) {
- sb.append("@collation=").append(val);
- }
+ variant.filter(it -> !it.isEmpty()).ifPresent(val -> {
+
+ // Mongo requires variant rendered as ICU keyword (@key=value;key=value…)
+ sb.append("@collation=").append(val);
});
+
return sb.toString();
}
}
@@ -698,24 +779,24 @@ public class Collation {
Document document = new Document();
document.append("locale", source.locale.asString());
- source.strength.ifPresent(val -> {
+ source.strength.ifPresent(strength -> {
- document.append("strength", val.level);
+ document.append("strength", strength.getLevel());
- val.caseLevel.ifPresent(cl -> document.append("caseLevel", cl));
- val.caseFirst.ifPresent(cl -> document.append("caseFirst", cl.state));
+ strength.getCaseLevel().ifPresent(it -> document.append("caseLevel", it));
+ strength.getCaseFirst().ifPresent(it -> document.append("caseFirst", it.state));
});
source.numericOrdering.ifPresent(val -> document.append("numericOrdering", val));
- source.alternate.ifPresent(val -> {
+ source.alternate.ifPresent(it -> {
- document.append("alternate", val.alternate);
- val.maxVariable.ifPresent(maxVariable -> document.append("maxVariable", maxVariable));
+ document.append("alternate", it.alternate);
+ it.maxVariable.ifPresent(maxVariable -> document.append("maxVariable", maxVariable));
});
- source.backwards.ifPresent(val -> document.append("backwards", val));
- source.normalization.ifPresent(val -> document.append("normalization", val));
- source.version.ifPresent(val -> document.append("version", val));
+ source.backwards.ifPresent(it -> document.append("backwards", it));
+ source.normalization.ifPresent(it -> document.append("normalization", it));
+ source.version.ifPresent(it -> document.append("version", it));
return document;
};
@@ -729,24 +810,24 @@ public class Collation {
builder.locale(source.locale.asString());
- source.strength.ifPresent(val -> {
+ source.strength.ifPresent(strength -> {
- builder.collationStrength(CollationStrength.fromInt(val.level));
+ builder.collationStrength(CollationStrength.fromInt(strength.getLevel()));
- val.caseLevel.ifPresent(cl -> builder.caseLevel(cl));
- val.caseFirst.ifPresent(cl -> builder.collationCaseFirst(CollationCaseFirst.fromString(cl.state)));
+ strength.getCaseLevel().ifPresent(builder::caseLevel);
+ strength.getCaseFirst().ifPresent(it -> builder.collationCaseFirst(CollationCaseFirst.fromString(it.state)));
});
- source.numericOrdering.ifPresent(val -> builder.numericOrdering(val));
- source.alternate.ifPresent(val -> {
+ source.numericOrdering.ifPresent(builder::numericOrdering);
+ source.alternate.ifPresent(it -> {
- builder.collationAlternate(CollationAlternate.fromString(val.alternate));
- val.maxVariable
+ builder.collationAlternate(CollationAlternate.fromString(it.alternate));
+ it.maxVariable
.ifPresent(maxVariable -> builder.collationMaxVariable(CollationMaxVariable.fromString(maxVariable)));
});
- source.backwards.ifPresent(val -> builder.backwards(val));
- source.normalization.ifPresent(val -> builder.normalization(val));
+ source.backwards.ifPresent(builder::backwards);
+ source.normalization.ifPresent(builder::normalization);
return builder.build();
};
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java
index e694110f0..469fc1c94 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java
@@ -24,27 +24,33 @@ import org.springframework.util.Assert;
*
* @author Thomas Risberg
* @author Christoph Strobl
+ * @author Mark Paluch
*/
public class CollectionOptions {
private Integer maxDocuments;
private Integer size;
private Boolean capped;
- private Collation collation;
+ private Optional collation;
/**
* Constructs a new CollectionOptions instance.
- *
- * @param size the collection size in bytes, this data space is preallocated
+ *
+ * @param size the collection size in bytes, this data space is preallocated.
* @param maxDocuments the maximum number of documents in the collection.
* @param capped true to created a "capped" collection (fixed size with auto-FIFO behavior based on insertion order),
* false otherwise.
*/
public CollectionOptions(Integer size, Integer maxDocuments, Boolean capped) {
+ this(size, maxDocuments, capped, Optional.empty());
+ }
+
+ private CollectionOptions(Integer size, Integer maxDocuments, Boolean capped, Optional collation) {
this.maxDocuments = maxDocuments;
this.size = size;
this.capped = capped;
+ this.collation = collation;
}
private CollectionOptions() {}
@@ -66,16 +72,24 @@ public class CollectionOptions {
}
/**
- * Create new {@link CollectionOptions} with already given settings and capped set to {@literal true}.
+ * Create new empty {@link CollectionOptions}.
*
* @return new {@link CollectionOptions}.
* @since 2.0
*/
- public CollectionOptions capped() {
+ public static CollectionOptions empty() {
+ return new CollectionOptions();
+ }
- CollectionOptions options = new CollectionOptions(size, maxDocuments, true);
- options.setCollation(collation);
- return options;
+ /**
+ * Create new {@link CollectionOptions} with already given settings and capped set to {@literal true}.
+ *
+ * @param size the collection size in bytes, this data space is preallocated.
+ * @return new {@link CollectionOptions}.
+ * @since 2.0
+ */
+ public CollectionOptions capped(int size) {
+ return new CollectionOptions(size, maxDocuments, true, collation);
}
/**
@@ -86,10 +100,7 @@ public class CollectionOptions {
* @since 2.0
*/
public CollectionOptions maxDocuments(Integer maxDocuments) {
-
- CollectionOptions options = new CollectionOptions(size, maxDocuments, capped);
- options.setCollation(collation);
- return options;
+ return new CollectionOptions(size, maxDocuments, capped, collation);
}
/**
@@ -99,11 +110,8 @@ public class CollectionOptions {
* @return new {@link CollectionOptions}.
* @since 2.0
*/
- public CollectionOptions size(Integer size) {
-
- CollectionOptions options = new CollectionOptions(size, maxDocuments, capped);
- options.setCollation(collation);
- return options;
+ public CollectionOptions size(int size) {
+ return new CollectionOptions(size, maxDocuments, capped, collation);
}
/**
@@ -114,10 +122,7 @@ public class CollectionOptions {
* @since 2.0
*/
public CollectionOptions collation(Collation collation) {
-
- CollectionOptions options = new CollectionOptions(size, maxDocuments, capped);
- options.setCollation(collation);
- return options;
+ return new CollectionOptions(size, maxDocuments, capped, Optional.ofNullable(collation));
}
public Integer getMaxDocuments() {
@@ -151,7 +156,7 @@ public class CollectionOptions {
* @since 2.0
*/
public void setCollation(Collation collation) {
- this.collation = collation;
+ this.collation = Optional.ofNullable(collation);
}
/**
@@ -161,6 +166,6 @@ public class CollectionOptions {
* @since 2.0
*/
public Optional getCollation() {
- return Optional.ofNullable(collation);
+ return collation;
}
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindAndModifyOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindAndModifyOptions.java
index 1142f9491..6a53f18fd 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindAndModifyOptions.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FindAndModifyOptions.java
@@ -24,15 +24,15 @@ import java.util.Optional;
*/
public class FindAndModifyOptions {
- boolean returnNew;
- boolean upsert;
- boolean remove;
+ private boolean returnNew;
+ private boolean upsert;
+ private boolean remove;
private Collation collation;
/**
* Static factory method to create a FindAndModifyOptions instance
- *
+ *
* @return a new instance
*/
public static FindAndModifyOptions options() {
@@ -46,9 +46,8 @@ public class FindAndModifyOptions {
*/
public static FindAndModifyOptions of(FindAndModifyOptions source) {
-
FindAndModifyOptions options = new FindAndModifyOptions();
- if(source == null) {
+ if (source == null) {
return options;
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexConverters.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexConverters.java
index 2dbaa3fc6..66ddfdb0e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexConverters.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/IndexConverters.java
@@ -25,10 +25,6 @@ import org.springframework.data.mongodb.core.index.IndexInfo;
import org.springframework.util.ObjectUtils;
import com.mongodb.client.model.Collation;
-import com.mongodb.client.model.CollationAlternate;
-import com.mongodb.client.model.CollationCaseFirst;
-import com.mongodb.client.model.CollationMaxVariable;
-import com.mongodb.client.model.CollationStrength;
import com.mongodb.client.model.IndexOptions;
/**
@@ -129,39 +125,11 @@ abstract class IndexConverters {
return null;
}
- com.mongodb.client.model.Collation.Builder collationBuilder = Collation.builder();
-
- collationBuilder.locale(source.getString("locale"));
- if (source.containsKey("caseLevel")) {
- collationBuilder.caseLevel(source.getBoolean("caseLevel"));
- }
- if (source.containsKey("caseFirst")) {
- collationBuilder.collationCaseFirst(CollationCaseFirst.fromString(source.getString("caseFirst")));
- }
- if (source.containsKey("strength")) {
- collationBuilder.collationStrength(CollationStrength.fromInt(source.getInteger("strength")));
- }
- if (source.containsKey("numericOrdering")) {
- collationBuilder.numericOrdering(source.getBoolean("numericOrdering"));
- }
- if (source.containsKey("alternate")) {
- collationBuilder.collationAlternate(CollationAlternate.fromString(source.getString("alternate")));
- }
- if (source.containsKey("maxVariable")) {
- collationBuilder.collationMaxVariable(CollationMaxVariable.fromString(source.getString("maxVariable")));
- }
- if (source.containsKey("backwards")) {
- collationBuilder.backwards(source.getBoolean("backwards"));
- }
- if (source.containsKey("normalization")) {
- collationBuilder.normalization(source.getBoolean("normalization"));
- }
-
- return collationBuilder.build();
+ return org.springframework.data.mongodb.core.Collation.from(source).toMongoCollation();
}
private static Converter getDocumentIndexInfoConverter() {
- return ix -> IndexInfo.indexInfoOf(ix);
+ return IndexInfo::indexInfoOf;
}
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
index d039a3286..7b418aafa 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
@@ -20,19 +20,8 @@ import static org.springframework.data.mongodb.core.query.SerializationUtils.*;
import static org.springframework.data.util.Optionals.*;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import java.util.Map.Entry;
-import java.util.Optional;
-import java.util.Scanner;
-import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.bson.Document;
@@ -60,6 +49,7 @@ import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metric;
+import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
@@ -722,7 +712,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Optionals.ifAllPresent(query.getCollation(), optionsToUse.getCollation(), (l, r) -> {
throw new IllegalArgumentException(
- "Both Query and FindAndModifyOptions define the collation. Please provide the collation only via one of the two.");
+ "Both Query and FindAndModifyOptions define a collation. Please provide the collation only via one of the two.");
});
query.getCollation().ifPresent(optionsToUse::collation);
@@ -885,7 +875,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Optional extends MongoPersistentEntity>> persistentEntity = getPersistentEntity(entity.getClass());
- ifAllPresent(persistentEntity, persistentEntity.flatMap(it -> it.getVersionProperty()), (l, r) -> {
+ ifAllPresent(persistentEntity, persistentEntity.flatMap(PersistentEntity::getVersionProperty), (l, r) -> {
ConvertingPropertyAccessor accessor = new ConvertingPropertyAccessor(l.getPropertyAccessor(entity),
mongoConverter.getConversionService());
accessor.setProperty(r, Optional.of(0));
@@ -972,7 +962,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Assert.hasText(collectionName, "Collection name must not be null or empty!");
Optional extends MongoPersistentEntity>> entity = getPersistentEntity(objectToSave.getClass());
- Optional versionProperty = entity.flatMap(it -> it.getVersionProperty());
+ Optional versionProperty = entity.flatMap(PersistentEntity::getVersionProperty);
mapIfAllPresent(entity, versionProperty, //
(l, r) -> doSaveVersioned(objectToSave, l, collectionName))//
@@ -1225,18 +1215,19 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
private void increaseVersionForUpdateIfNecessary(Optional extends MongoPersistentEntity>> persistentEntity,
Update update) {
- ifAllPresent(persistentEntity, persistentEntity.flatMap(it -> it.getVersionProperty()), (entity, property) -> {
- String versionFieldName = property.getFieldName();
- if (!update.modifies(versionFieldName)) {
- update.inc(versionFieldName, 1L);
- }
- });
+ ifAllPresent(persistentEntity, persistentEntity.flatMap(PersistentEntity::getVersionProperty),
+ (entity, property) -> {
+ String versionFieldName = property.getFieldName();
+ if (!update.modifies(versionFieldName)) {
+ update.inc(versionFieldName, 1L);
+ }
+ });
}
private boolean documentContainsVersionProperty(Document document,
Optional extends MongoPersistentEntity>> persistentEntity) {
- return mapIfAllPresent(persistentEntity, persistentEntity.flatMap(it -> it.getVersionProperty()), //
+ return mapIfAllPresent(persistentEntity, persistentEntity.flatMap(PersistentEntity::getVersionProperty), //
(entity, property) -> document.containsKey(property.getFieldName()))//
.orElse(false);
}
@@ -1458,7 +1449,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Optionals.ifAllPresent(collation, mapReduceOptions.getCollation(), (l, r) -> {
throw new IllegalArgumentException(
- "Both Query and MapReduceOptions define the collation. Please provide the collation only via one of the two.");
+ "Both Query and MapReduceOptions define a collation. Please provide the collation only via one of the two.");
});
if (mapReduceOptions.getCollation().isPresent()) {
@@ -1482,9 +1473,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
}
}
- if (collation.isPresent()) {
- result = result.collation(collation.map(Collation::toMongoCollation).get());
- }
+ result = collation.map(Collation::toMongoCollation).map(result::collation).orElse(result);
List mappedResults = new ArrayList();
DocumentCallback callback = new ReadDocumentCallback(mongoConverter, entityClass, inputCollectionName);
@@ -2297,7 +2286,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("findOne using query: {} fields: {} in db.collection: {}", serializeToJsonSafely(query),
- serializeToJsonSafely(fields.orElseGet(() -> new Document())), collection.getNamespace().getFullName());
+ serializeToJsonSafely(fields.orElseGet(Document::new)), collection.getNamespace().getFullName());
}
if (fields.isPresent()) {
@@ -2336,11 +2325,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
FindIterable iterable = collection.find(query);
- if (fields.filter(val -> !val.isEmpty()).isPresent()) {
- iterable = iterable.projection(fields.get());
- }
-
- return iterable;
+ return fields.filter(val -> !val.isEmpty()).map(iterable::projection).orElse(iterable);
}
}
@@ -2399,7 +2384,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
opts.upsert(true);
}
opts.projection(fields);
- if (options.returnNew) {
+ if (options.isReturnNew()) {
opts.returnDocument(ReturnDocument.AFTER);
}
@@ -2506,16 +2491,16 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
return cursor;
}
- if (query.getSkip() <= 0 && query.getLimit() <= 0 && query.getSortObject() == null
- && !StringUtils.hasText(query.getHint()) && !query.getMeta().hasValues()) {
+ if (query.getSkip() <= 0 && query.getLimit() <= 0
+ && (query.getSortObject() == null || query.getSortObject().isEmpty()) && !StringUtils.hasText(query.getHint())
+ && !query.getMeta().hasValues() && !query.getCollation().isPresent()) {
return cursor;
}
- FindIterable cursorToUse = cursor;
+ FindIterable cursorToUse;
+
+ cursorToUse = query.getCollation().map(Collation::toMongoCollation).map(cursor::collation).orElse(cursor);
- if (query.getCollation().isPresent()) {
- cursorToUse = cursorToUse.collation(query.getCollation().map(val -> val.toMongoCollation()).get());
- }
try {
if (query.getSkip() > 0) {
cursorToUse = cursorToUse.skip((int) query.getSkip());
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java
index 0e21292c5..c658e428c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java
@@ -59,6 +59,7 @@ import org.springframework.data.convert.EntityReader;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.Metric;
+import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
@@ -573,11 +574,10 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
return createFlux(collectionName, collection -> {
Document mappedQuery = queryMapper.getMappedObject(query.getQueryObject(), getPersistentEntity(entityClass));
- FindPublisher findPublisher = collection.find(mappedQuery).projection(new Document("_id", 1));
+ FindPublisher findPublisher = collection.find(mappedQuery).projection(new Document("_id", 1));
- if (query.getCollation().isPresent()) {
- findPublisher = findPublisher.collation(query.getCollation().map(Collation::toMongoCollation).get());
- }
+ findPublisher = query.getCollation().map(Collation::toMongoCollation).map(findPublisher::collation)
+ .orElse(findPublisher);
return findPublisher.limit(1);
}).hasElements();
@@ -616,8 +616,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
public Mono findById(Object id, Class entityClass, String collectionName) {
Optional extends MongoPersistentEntity>> persistentEntity = mappingContext.getPersistentEntity(entityClass);
- MongoPersistentProperty idProperty = persistentEntity.isPresent()
- ? persistentEntity.get().getIdProperty().orElse(null) : null;
+ MongoPersistentProperty idProperty = persistentEntity.flatMap(PersistentEntity::getIdProperty).orElse(null);
String idKey = idProperty == null ? ID_FIELD : idProperty.getName();
@@ -712,7 +711,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
Optionals.ifAllPresent(query.getCollation(), optionsToUse.getCollation(), (l, r) -> {
throw new IllegalArgumentException(
- "Both Query and FindAndModifyOptions define the collation. Please provide the collation only via one of the two.");
+ "Both Query and FindAndModifyOptions define a collation. Please provide the collation only via one of the two.");
});
query.getCollation().ifPresent(optionsToUse::collation);
@@ -1091,34 +1090,35 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
return collectionToUse;
}
- protected Mono