DATAMONGO-1721 - Fixed package dependencies.

Added a Degraph based tests to identify package cycles and violations in layering.

Moved Collation to the core.query package, fixing dependency cycles. Moved IndexOperations and IndexOperationsProvider to the core.index package. fixing dependency cycles. Moved GeoJsonConfiguration to config package. Replaced the original version of these interfaces/classes with a deprecated version extending the new one, in order to not break the existing API.

Removed all references to Part.Type, except for those to maintain the existing API. API using Part.Type is marked as deprecated. It violates the layering, because nothing but "config" should access "repository". Tests added to MongoRegexCreator in order to facilitate the removal of Part.Type dependencies. Using the moved/new ExampleMatcherAccessor.

Related Tickets: DATACMNS-1097.
Original pull request: #470.
This commit is contained in:
Jens Schauder
2017-06-21 14:21:36 +02:00
committed by Oliver Gierke
parent d19ea88670
commit 80ff3760ef
49 changed files with 501 additions and 123 deletions

1
.gitignore vendored
View File

@@ -15,3 +15,4 @@ src/ant/.ant-targets-upload-dist.xml
atlassian-ide-plugin.xml
/.gradle/
/.idea/
*.graphml

View File

@@ -232,6 +232,13 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.schauderhaft.degraph</groupId>
<artifactId>degraph-check</artifactId>
<version>0.1.4</version>
<scope>test</scope>
</dependency>
<!-- Kotlin extension -->
<dependency>
<groupId>org.jetbrains.kotlin</groupId>

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2015-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.config;
import org.springframework.context.annotation.Bean;
import org.springframework.data.mongodb.core.geo.GeoJsonModule;
import org.springframework.data.web.config.SpringDataJacksonModules;
/**
* Configuration class to expose {@link GeoJsonModule} as a Spring bean.
*
* @author Oliver Gierke
* @author Jens Schauder
*/
public class GeoJsonConfiguration implements SpringDataJacksonModules {
@Bean
public GeoJsonModule geoJsonModule() {
return new GeoJsonModule();
}
}

View File

@@ -17,6 +17,7 @@ package org.springframework.data.mongodb.core;
import java.util.Optional;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.util.Assert;
/**

View File

@@ -31,6 +31,7 @@ import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.convert.UpdateMapper;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.util.Pair;

View File

@@ -27,6 +27,7 @@ import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.IndexInfo;
import org.springframework.data.mongodb.core.index.IndexOperations;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.util.Assert;
@@ -89,7 +90,7 @@ public class DefaultIndexOperations implements IndexOperations {
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.IndexOperations#ensureIndex(org.springframework.data.mongodb.core.index.IndexDefinition)
* @see org.springframework.data.mongodb.core.index.IndexOperations#ensureIndex(org.springframework.data.mongodb.core.index.IndexDefinition)
*/
public String ensureIndex(final IndexDefinition indexDefinition) {
@@ -136,7 +137,7 @@ public class DefaultIndexOperations implements IndexOperations {
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.IndexOperations#dropIndex(java.lang.String)
* @see org.springframework.data.mongodb.core.index.IndexOperations#dropIndex(java.lang.String)
*/
public void dropIndex(final String name) {
@@ -149,7 +150,7 @@ public class DefaultIndexOperations implements IndexOperations {
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.IndexOperations#dropAllIndexes()
* @see org.springframework.data.mongodb.core.index.IndexOperations#dropAllIndexes()
*/
public void dropAllIndexes() {
dropIndex("*");
@@ -157,7 +158,7 @@ public class DefaultIndexOperations implements IndexOperations {
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.IndexOperations#getIndexInfo()
* @see org.springframework.data.mongodb.core.index.IndexOperations#getIndexInfo()
*/
public List<IndexInfo> getIndexInfo() {

View File

@@ -17,6 +17,8 @@ package org.springframework.data.mongodb.core;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.index.IndexOperations;
import org.springframework.data.mongodb.core.index.IndexOperationsProvider;
/**
* {@link IndexOperationsProvider} to obtain {@link IndexOperations} from a given {@link MongoDbFactory}. TODO: Review
@@ -38,7 +40,7 @@ class DefaultIndexOperationsProvider implements IndexOperationsProvider {
}
/* (non-Javadoc)
* @see org.springframework.data.mongodb.core.IndexOperationsProvider#reactiveIndexOps(java.lang.String)
* @see org.springframework.data.mongodb.core.index.IndexOperationsProvider#reactiveIndexOps(java.lang.String)
*/
@Override
public IndexOperations indexOps(String collectionName) {

View File

@@ -18,6 +18,7 @@ package org.springframework.data.mongodb.core;
import org.bson.Document;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.IndexInfo;
import org.springframework.data.mongodb.core.index.IndexOperations;
import org.springframework.util.Assert;
import com.mongodb.reactivestreams.client.ListIndexesPublisher;

View File

@@ -17,6 +17,8 @@ package org.springframework.data.mongodb.core;
import java.util.Optional;
import org.springframework.data.mongodb.core.query.Collation;
/**
* @author Mark Pollak
* @author Oliver Gierke

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 the original author or authors.
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,19 +15,17 @@
*/
package org.springframework.data.mongodb.core;
import org.springframework.context.annotation.Bean;
import org.springframework.data.mongodb.core.geo.GeoJsonModule;
import org.springframework.data.web.config.SpringDataJacksonModules;
/**
* Configuration class to expose {@link GeoJsonModule} as a Spring bean.
*
*
* @author Oliver Gierke
* @author Jens Schauder
*
* @deprecated Use {@link org.springframework.data.mongodb.config.GeoJsonConfiguration} instead.
*/
public class GeoJsonConfiguration implements SpringDataJacksonModules {
@Deprecated
public class GeoJsonConfiguration extends org.springframework.data.mongodb.config.GeoJsonConfiguration {
@Bean
public GeoJsonModule geoJsonModule() {
return new GeoJsonModule();
}
}

View File

@@ -125,7 +125,7 @@ abstract class IndexConverters {
return null;
}
return org.springframework.data.mongodb.core.Collation.from(source).toMongoCollation();
return org.springframework.data.mongodb.core.query.Collation.from(source).toMongoCollation();
}
private static Converter<Document, IndexInfo> getDocumentIndexInfoConverter() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2016 the original author or authors.
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,44 +15,17 @@
*/
package org.springframework.data.mongodb.core;
import java.util.List;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.IndexInfo;
/**
* Index operations on a collection.
*
*
* @author Mark Pollack
* @author Oliver Gierke
* @author Christoph Strobl
* @author Jens Schauder
*
* @deprecated Use {@link org.springframework.data.mongodb.core.index.IndexOperations} instead.
*/
public interface IndexOperations {
@Deprecated
public interface IndexOperations extends org.springframework.data.mongodb.core.index.IndexOperations {
/**
* Ensure that an index for the provided {@link IndexDefinition} exists for the collection indicated by the entity
* class. If not it will be created.
*
* @param indexDefinition must not be {@literal null}.
*/
String ensureIndex(IndexDefinition indexDefinition);
/**
* Drops an index from this collection.
*
* @param name name of index to drop
*/
void dropIndex(String name);
/**
* Drops all indices from this collection.
*/
void dropAllIndexes();
/**
* Returns the index information on the collection.
*
* @return index information on the collection
*/
List<IndexInfo> getIndexInfo();
}

View File

@@ -20,6 +20,7 @@ import java.util.List;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.index.IndexInfo;
import org.springframework.data.mongodb.core.index.IndexOperations;
import org.springframework.util.Assert;
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,22 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
import org.springframework.data.mongodb.core.convert.QueryMapper;
/**
* TODO: Revisit for a better pattern.
* @author Mark Paluch
* @author Jens Schauder
* @since 2.0
*
* @deprecated Use {@link org.springframework.data.mongodb.core.index.IndexOperationsProvider} instead.
*/
public interface IndexOperationsProvider {
@Deprecated
public interface IndexOperationsProvider extends org.springframework.data.mongodb.core.index.IndexOperationsProvider {
/**
* Returns the operations that can be performed on indexes
*
* @return index operations on the named collection
*/
IndexOperations indexOps(String collectionName);
}

View File

@@ -27,6 +27,7 @@ import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.index.IndexOperations;
import org.springframework.data.mongodb.core.mapreduce.GroupBy;
import org.springframework.data.mongodb.core.mapreduce.GroupByResults;
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;

View File

@@ -84,6 +84,8 @@ import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.convert.MongoWriter;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.convert.UpdateMapper;
import org.springframework.data.mongodb.core.index.IndexOperations;
import org.springframework.data.mongodb.core.index.IndexOperationsProvider;
import org.springframework.data.mongodb.core.index.MongoMappingEventPublisher;
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
@@ -102,6 +104,7 @@ import org.springframework.data.mongodb.core.mapreduce.GroupBy;
import org.springframework.data.mongodb.core.mapreduce.GroupByResults;
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
import org.springframework.data.mongodb.core.mapreduce.MapReduceResults;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Meta;
import org.springframework.data.mongodb.core.query.NearQuery;
@@ -612,8 +615,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
Document mappedQuery = queryMapper.getMappedObject(query.getQueryObject(), getPersistentEntity(entityClass));
return execute(collectionName, new ExistsCallback(mappedQuery,
query.getCollation().map(org.springframework.data.mongodb.core.Collation::toMongoCollation).orElse(null)));
return execute(collectionName,
new ExistsCallback(mappedQuery, query.getCollation().map(Collation::toMongoCollation).orElse(null)));
}
// Find methods that take a Query to express the query and that return a List of objects.

View File

@@ -56,6 +56,7 @@ import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.convert.DbRefProxyHandler;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DbRefResolverCallback;

View File

@@ -18,7 +18,7 @@ package org.springframework.data.mongodb.core.aggregation;
import java.util.Optional;
import org.bson.Document;
import org.springframework.data.mongodb.core.Collation;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.util.Assert;
import com.mongodb.DBObject;

View File

@@ -30,8 +30,7 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.query.MongoRegexCreator;
import org.springframework.data.mongodb.core.query.SerializationUtils;
import org.springframework.data.repository.core.support.ExampleMatcherAccessor;
import org.springframework.data.repository.query.parser.Part.Type;
import org.springframework.data.support.ExampleMatcherAccessor;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@@ -40,24 +39,18 @@ import org.springframework.util.StringUtils;
/**
* @author Christoph Strobl
* @author Mark Paluch
* @author Jens Schauder
* @since 1.8
*/
public class MongoExampleMapper {
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
private final MongoConverter converter;
private final Map<StringMatcher, Type> stringMatcherPartMapping = new HashMap<StringMatcher, Type>();
public MongoExampleMapper(MongoConverter converter) {
this.converter = converter;
this.mappingContext = converter.getMappingContext();
stringMatcherPartMapping.put(StringMatcher.EXACT, Type.SIMPLE_PROPERTY);
stringMatcherPartMapping.put(StringMatcher.CONTAINING, Type.CONTAINING);
stringMatcherPartMapping.put(StringMatcher.STARTING, Type.STARTING_WITH);
stringMatcherPartMapping.put(StringMatcher.ENDING, Type.ENDING_WITH);
stringMatcherPartMapping.put(StringMatcher.REGEX, Type.REGEX);
}
/**
@@ -201,7 +194,7 @@ public class MongoExampleMapper {
continue;
}
StringMatcher stringMatcher = exampleSpecAccessor.getDefaultStringMatcher();
org.springframework.data.util.StringMatcher stringMatcher = exampleSpecAccessor.getDefaultStringMatcher().toNewStringMatcher();
Object value = entry.getValue();
boolean ignoreCase = exampleSpecAccessor.isIgnoreCaseEnabled();
@@ -210,7 +203,7 @@ public class MongoExampleMapper {
mappedPropertyPath = exampleSpecAccessor.hasPropertySpecifier(propertyPath) ? propertyPath
: getMappedPropertyPath(propertyPath, probeType);
stringMatcher = exampleSpecAccessor.getStringMatcherForPath(mappedPropertyPath);
stringMatcher = exampleSpecAccessor.getStringMatcherForPath(mappedPropertyPath).toNewStringMatcher();
ignoreCase = exampleSpecAccessor.isIgnoreCaseForPath(mappedPropertyPath);
}
@@ -239,11 +232,12 @@ public class MongoExampleMapper {
return entry.getKey().equals("_id") && entry.getValue() == null || entry.getValue().equals(Optional.empty());
}
private void applyStringMatcher(Map.Entry<String, Object> entry, StringMatcher stringMatcher, boolean ignoreCase) {
private void applyStringMatcher(Map.Entry<String, Object> entry,
org.springframework.data.util.StringMatcher stringMatcher, boolean ignoreCase) {
Document document = new Document();
if (ObjectUtils.nullSafeEquals(StringMatcher.DEFAULT, stringMatcher)) {
if (org.springframework.data.util.StringMatcher.DEFAULT == stringMatcher) {
if (ignoreCase) {
document.put("$regex", Pattern.quote((String) entry.getValue()));
@@ -251,8 +245,7 @@ public class MongoExampleMapper {
}
} else {
Type type = stringMatcherPartMapping.get(stringMatcher);
String expression = MongoRegexCreator.INSTANCE.toRegularExpression((String) entry.getValue(), type);
String expression = MongoRegexCreator.INSTANCE.toRegularExpression((String) entry.getValue(), stringMatcher);
document.put("$regex", expression);
entry.setValue(document);
}
@@ -261,4 +254,8 @@ public class MongoExampleMapper {
document.put("$options", "i");
}
}
private org.springframework.data.util.StringMatcher convert(StringMatcher stringMatcher) {
return org.springframework.data.util.StringMatcher.valueOf(stringMatcher.name());
}
}

View File

@@ -18,7 +18,7 @@ package org.springframework.data.mongodb.core.index;
import java.util.Optional;
import org.bson.Document;
import org.springframework.data.mongodb.core.Collation;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

View File

@@ -23,7 +23,7 @@ import java.util.concurrent.TimeUnit;
import org.bson.Document;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.Collation;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.index;
import java.util.List;
/**
* Index operations on a collection.
*
* @author Mark Pollack
* @author Oliver Gierke
* @author Christoph Strobl
* @author Jens Schauder
*/
public interface IndexOperations {
/**
* Ensure that an index for the provided {@link IndexDefinition} exists for the collection indicated by the entity
* class. If not it will be created.
*
* @param indexDefinition must not be {@literal null}.
*/
String ensureIndex(IndexDefinition indexDefinition);
/**
* Drops an index from this collection.
*
* @param name name of index to drop
*/
void dropIndex(String name);
/**
* Drops all indices from this collection.
*/
void dropAllIndexes();
/**
* Returns the index information on the collection.
*
* @return index information on the collection
*/
List<IndexInfo> getIndexInfo();
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.index;
/**
* TODO: Revisit for a better pattern.
*
* @author Mark Paluch
* @author Jens Schauder
* @since 2.0
*/
public interface IndexOperationsProvider {
/**
* Returns the operations that can be performed on indexes
*
* @return index operations on the named collection
*/
IndexOperations indexOps(String collectionName);
}

View File

@@ -28,8 +28,6 @@ import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.MappingContextEvent;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.UncategorizedMongoDbException;
import org.springframework.data.mongodb.core.IndexOperations;
import org.springframework.data.mongodb.core.IndexOperationsProvider;
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolver.IndexDefinitionHolder;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;

View File

@@ -18,7 +18,7 @@ package org.springframework.data.mongodb.core.mapreduce;
import java.util.Optional;
import org.bson.Document;
import org.springframework.data.mongodb.core.Collation;
import org.springframework.data.mongodb.core.query.Collation;
/**
* Collects the parameters required to perform a group operation on a collection. The query condition and the input

View File

@@ -20,7 +20,7 @@ import java.util.Map;
import java.util.Optional;
import org.bson.Document;
import org.springframework.data.mongodb.core.Collation;
import org.springframework.data.mongodb.core.query.Collation;
import com.mongodb.MapReduceCommand;

View File

@@ -13,12 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
package org.springframework.data.mongodb.core.query;
import java.util.Locale;
import java.util.Optional;
@@ -34,18 +29,24 @@ import com.mongodb.client.model.CollationCaseFirst;
import com.mongodb.client.model.CollationMaxVariable;
import com.mongodb.client.model.CollationStrength;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* Central abstraction for MongoDB collation support. <br />
* Allows fluent creation of a collation {@link Document} that can be used for creating collections & indexes as well as
* querying data.
* <p />
* <p/>
* <strong>NOTE:</strong> Please keep in mind that queries will only make use of an index with collation settings if the
* query itself specifies the same collation.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 2.0
* @author Jens Schauder
* @see <a href="https://docs.mongodb.com/manual/reference/collation/">MongoDB Reference - Collation</a>
* @since 2.0
*/
public class Collation {
@@ -720,8 +721,8 @@ public class Collation {
/**
* ICU locale abstraction for usage with MongoDB {@link Collation}.
*
* @since 2.0
* @see <a href="http://site.icu-project.org">ICU - International Components for Unicode</a>
* @since 2.0
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public static class CollationLocale {

View File

@@ -18,11 +18,12 @@ package org.springframework.data.mongodb.core.query;
import java.util.regex.Pattern;
import org.springframework.data.repository.query.parser.Part.Type;
import org.springframework.util.ObjectUtils;
import org.springframework.data.util.StringMatcher;
/**
* @author Christoph Strobl
* @author Mark Paluch
* @author Jens Schauder
* @since 1.8
*/
public enum MongoRegexCreator {
@@ -37,28 +38,40 @@ public enum MongoRegexCreator {
* @param source the plain String
* @param type
* @return {@literal source} when {@literal source} or {@literal type} is {@literal null}.
* @deprecated use the {@link MongoRegexCreator#toRegularExpression(String, StringMatcher)} instead
*/
@Deprecated
public String toRegularExpression(String source, Type type) {
if (type == null || source == null) {
return toRegularExpression(source, convert(type));
}
/**
* Creates a regular expression String to be used with {@code $regex}.
*
* @param source the plain String
* @param matcherType the type of matching to perform
* @return {@literal source} when {@literal source} or {@literal matcherType} is {@literal null}.
*/
public String toRegularExpression(String source, StringMatcher matcherType) {
if (matcherType == null || source == null) {
return source;
}
String regex = prepareAndEscapeStringBeforeApplyingLikeRegex(source, type);
String regex = prepareAndEscapeStringBeforeApplyingLikeRegex(source, matcherType);
switch (type) {
case STARTING_WITH:
switch (matcherType) {
case STARTING:
regex = "^" + regex;
break;
case ENDING_WITH:
case ENDING:
regex = regex + "$";
break;
case CONTAINING:
case NOT_CONTAINING:
regex = ".*" + regex + ".*";
break;
case SIMPLE_PROPERTY:
case NEGATING_SIMPLE_PROPERTY:
case EXACT:
regex = "^" + regex + "$";
default:
}
@@ -66,13 +79,13 @@ public enum MongoRegexCreator {
return regex;
}
private String prepareAndEscapeStringBeforeApplyingLikeRegex(String source, Type type) {
private String prepareAndEscapeStringBeforeApplyingLikeRegex(String source, StringMatcher matcherType) {
if (ObjectUtils.nullSafeEquals(Type.REGEX, type)) {
if (StringMatcher.REGEX == matcherType) {
return source;
}
if (!ObjectUtils.nullSafeEquals(Type.LIKE, type) && !ObjectUtils.nullSafeEquals(Type.NOT_LIKE, type)) {
if (StringMatcher.LIKE != matcherType) {
return PUNCTATION_PATTERN.matcher(source).find() ? Pattern.quote(source) : source;
}
@@ -103,4 +116,49 @@ public enum MongoRegexCreator {
return sb.toString();
}
private StringMatcher convert(Type type) {
if (type == null)
return null;
switch (type) {
case NOT_LIKE:
case LIKE:
return StringMatcher.LIKE;
case STARTING_WITH:
return StringMatcher.STARTING;
case ENDING_WITH:
return StringMatcher.ENDING;
case NOT_CONTAINING:
case CONTAINING:
return StringMatcher.CONTAINING;
case REGEX:
return StringMatcher.REGEX;
case SIMPLE_PROPERTY:
case NEGATING_SIMPLE_PROPERTY:
return StringMatcher.EXACT;
case BETWEEN:
case IS_NOT_NULL:
case IS_NULL:
case LESS_THAN:
case LESS_THAN_EQUAL:
case GREATER_THAN:
case GREATER_THAN_EQUAL:
case BEFORE:
case AFTER:
case EXISTS:
case TRUE:
case FALSE:
case NOT_IN:
case IN:
case NEAR:
case WITHIN:
case IS_NOT_EMPTY:
case IS_EMPTY:
return StringMatcher.DEFAULT;
}
throw new IllegalStateException("Execution should never reach this position.");
}
}

View File

@@ -34,7 +34,6 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
import org.springframework.data.mongodb.core.Collation;
import org.springframework.util.Assert;
/**

View File

@@ -24,7 +24,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.IndexOperationsProvider;
import org.springframework.data.mongodb.core.index.IndexOperationsProvider;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.index.Index;
import org.springframework.data.mongodb.repository.query.MongoEntityMetadata;

View File

@@ -23,6 +23,7 @@ import org.springframework.data.geo.GeoResults
import org.springframework.data.mongodb.core.BulkOperations.BulkMode
import org.springframework.data.mongodb.core.aggregation.Aggregation
import org.springframework.data.mongodb.core.aggregation.AggregationResults
import org.springframework.data.mongodb.core.index.IndexOperations
import org.springframework.data.mongodb.core.mapreduce.GroupBy
import org.springframework.data.mongodb.core.mapreduce.GroupByResults
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions

View File

@@ -1,2 +1,2 @@
org.springframework.data.web.config.SpringDataJacksonModules=org.springframework.data.mongodb.core.GeoJsonConfiguration
org.springframework.data.web.config.SpringDataJacksonModules=org.springframework.data.mongodb.config.GeoJsonConfiguration
org.springframework.data.repository.core.support.RepositoryFactorySupport=org.springframework.data.mongodb.repository.support.MongoRepositoryFactory

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb;
import static de.schauderhaft.degraph.check.JCheck.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.data.mongodb.core.GeoJsonConfiguration;
import org.springframework.data.mongodb.core.query.MongoRegexCreator;
import de.schauderhaft.degraph.configuration.NamedPattern;
/**
* Tests package dependency constraints.
*
* @author Jens Schauder
*/
public class DependencyTests {
@Test
public void noInternalPackageCycles() {
assertThat(
classpath() //
.noJars() //
.including("org.springframework.data.mongodb.**") //
// ignoring deprecated class that will be removed soon
.excluding(org.springframework.data.mongodb.core.IndexOperations.class.getCanonicalName())
.excluding(org.springframework.data.mongodb.core.IndexOperationsProvider.class.getCanonicalName())
.excluding(GeoJsonConfiguration.class.getCanonicalName())
.filterClasspath("*target/classes") //
.printOnFailure("degraph.graphml"), //
violationFree() //
);
}
@Test
public void onlyConfigMayUseRepository() {
assertThat(
classpath() //
.including("org.springframework.data.**") //
// ignoring the MongoRegexCreator for now, since it still
// needs the reference to Part.Type to maintain the old API
.excluding(MongoRegexCreator.class.getCanonicalName() + "*")
// ignoring deprecated class that will be removed soon
.excluding(org.springframework.data.mongodb.core.IndexOperations.class.getCanonicalName())
.excluding(org.springframework.data.mongodb.core.IndexOperationsProvider.class.getCanonicalName())
.excluding(GeoJsonConfiguration.class.getCanonicalName())
.filterClasspath("*target/classes") //
.printOnFailure("onlyConfigMayUseRepository.graphml") //
.withSlicing("slices", //
"**.(config).**", //
new NamedPattern("**.cdi.**", "config"), //
"**.(repository).**", //
new NamedPattern("**", "other"))
.allow("config", "repository", "other"), //
violationFree() //
);
}
@Test
public void commonsInternaly() {
assertThat(
classpath() //
.noJars() //
.including("org.springframework.data.**") //
.excluding("org.springframework.data.mongodb.**") //
.filterClasspath("*target/classes") //
.printTo("commons.graphml"), //
violationFree() //
);
}
}

View File

@@ -22,7 +22,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.GeoJsonConfiguration;
import org.springframework.data.mongodb.core.geo.GeoJsonModule;
import org.springframework.data.web.config.EnableSpringDataWebSupport;
import org.springframework.test.context.ContextConfiguration;

View File

@@ -21,10 +21,11 @@ import java.util.Locale;
import org.bson.Document;
import org.junit.Test;
import org.springframework.data.mongodb.core.Collation.Alternate;
import org.springframework.data.mongodb.core.Collation.CaseFirst;
import org.springframework.data.mongodb.core.Collation.CollationLocale;
import org.springframework.data.mongodb.core.Collation.ComparisonLevel;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Collation.Alternate;
import org.springframework.data.mongodb.core.query.Collation.CaseFirst;
import org.springframework.data.mongodb.core.query.Collation.CollationLocale;
import org.springframework.data.mongodb.core.query.Collation.ComparisonLevel;
/**
* @author Christoph Strobl

View File

@@ -44,6 +44,7 @@ import org.springframework.data.mongodb.core.convert.UpdateMapper;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Update;
import com.mongodb.client.MongoCollection;

View File

@@ -27,7 +27,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.Collation.CaseFirst;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Collation.CaseFirst;
import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.index.Index;
import org.springframework.data.mongodb.core.index.IndexDefinition;

View File

@@ -29,7 +29,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration;
import org.springframework.data.mongodb.core.Collation.CaseFirst;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Collation.CaseFirst;
import org.springframework.data.mongodb.core.DefaultIndexOperationsIntegrationTests.DefaultIndexOperationsIntegrationTestsSample;
import org.springframework.data.mongodb.core.index.Index;
import org.springframework.data.mongodb.core.index.IndexDefinition;

View File

@@ -28,8 +28,9 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import org.springframework.data.mongodb.core.Collation.Alternate;
import org.springframework.data.mongodb.core.Collation.ComparisonLevel;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Collation.Alternate;
import org.springframework.data.mongodb.core.query.Collation.ComparisonLevel;
import org.springframework.data.mongodb.test.util.MongoVersionRule;
import org.springframework.data.util.Version;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

View File

@@ -55,6 +55,7 @@ import org.springframework.data.domain.Sort;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;

View File

@@ -32,6 +32,7 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate.QueryCursorPreparer;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Meta;
import org.springframework.data.mongodb.core.query.Query;

View File

@@ -31,6 +31,7 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.test.util.MongoVersionRule;
import org.springframework.data.util.Version;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

View File

@@ -36,6 +36,7 @@ import org.reactivestreams.Publisher;
import org.springframework.data.mongodb.core.MongoTemplateUnitTests.AutogenerateableId;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate.NoOpDbRefResolver;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.query.BasicQuery;

View File

@@ -29,7 +29,7 @@ import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.IndexOperations;
import org.springframework.data.mongodb.core.index.IndexOperations;
import org.springframework.data.mongodb.core.Venue;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
import org.springframework.data.mongodb.core.index.GeospatialIndex;

View File

@@ -26,7 +26,7 @@ import java.util.List;
import org.junit.Test;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.IndexOperations;
import org.springframework.data.mongodb.core.index.IndexOperations;
import org.springframework.data.mongodb.core.Venue;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
import org.springframework.data.mongodb.core.index.GeospatialIndex;

View File

@@ -29,7 +29,7 @@ import org.springframework.dao.DataAccessException;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.config.AbstractIntegrationTests;
import org.springframework.data.mongodb.core.CollectionCallback;
import org.springframework.data.mongodb.core.IndexOperations;
import org.springframework.data.mongodb.core.index.IndexOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.WriteResultChecking;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;

View File

@@ -26,7 +26,6 @@ import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.config.AbstractIntegrationTests;
import org.springframework.data.mongodb.core.IndexOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Language;

View File

@@ -0,0 +1,116 @@
package org.springframework.data.mongodb.core.query;
import static java.util.Arrays.*;
import static org.springframework.data.mongodb.core.query.MongoRegexCreatorUnitTests.TestParameter.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.assertj.core.api.SoftAssertions;
import org.assertj.core.api.StringAssert;
import org.junit.Test;
import org.springframework.data.repository.query.parser.Part.Type;
/**
* Tests the creation of Regex's in {@link MongoRegexCreator}
*
* @author Jens Schauder
*/
public class MongoRegexCreatorUnitTests {
List<TestParameter> testParameters = asList(TestParameter.test("anystring", null, "anystring", "type=null -> input"),
test(null, Type.AFTER, null, "source=null -> null"), //
test("anystring", Type.REGEX, "anystring", "REGEX -> input"), //
test("one.two?three", Type.AFTER, "\\Qone.two?three\\E",
"not(REGEX, LIKE, NOT_LIKE, PunctuationPattern -> quoted punctuation"), //
test("*", Type.LIKE, ".*", "LIKE * -> .*"), test("*", Type.NOT_LIKE, ".*", "LIKE * -> .*"), //
test("*.*", Type.LIKE, ".*\\Q.\\E.*", "Wildcards & Punctuation"), //
test("*.", Type.LIKE, ".*\\Q.\\E", "Leading Wildcard & Punctuation"), //
test(".*", Type.LIKE, "\\Q.\\E.*", "Trailing Wildcard & Punctuation"), //
test("other", Type.LIKE, "other", "No Wildcard & Other"), //
test("other*", Type.LIKE, "other.*", "Trailing Wildcard & Other"), //
test("*other", Type.LIKE, ".*other", "Leading Wildcard & Other"), //
test("o*t.*h.er", Type.LIKE, "\\Qo*t.*h.er\\E", "Dots & Stars"), //
test("other", Type.STARTING_WITH, "^other", "Dots & Stars"), //
test("other", Type.ENDING_WITH, "other$", "Dots & Stars"), //
test("other", Type.CONTAINING, ".*other.*", "Dots & Stars"), //
test("other", Type.NOT_CONTAINING, ".*other.*", "Dots & Stars"), //
test("other", Type.SIMPLE_PROPERTY, "^other$", "Dots & Stars"), //
test("other", Type.NEGATING_SIMPLE_PROPERTY, "^other$", "Dots & Stars"));
Map<Type, String> expectedResultsForAllTypes = new HashMap<>();
{
expectedResultsForAllTypes.put(Type.BETWEEN, "\\Qo*t.*h.er*\\E");
expectedResultsForAllTypes.put(Type.IS_NOT_NULL, "\\Qo*t.*h.er*\\E");
expectedResultsForAllTypes.put(Type.IS_NULL, "\\Qo*t.*h.er*\\E");
expectedResultsForAllTypes.put(Type.LESS_THAN, "\\Qo*t.*h.er*\\E");
expectedResultsForAllTypes.put(Type.LESS_THAN_EQUAL, "\\Qo*t.*h.er*\\E");
expectedResultsForAllTypes.put(Type.GREATER_THAN, "\\Qo*t.*h.er*\\E");
expectedResultsForAllTypes.put(Type.GREATER_THAN_EQUAL, "\\Qo*t.*h.er*\\E");
expectedResultsForAllTypes.put(Type.BEFORE, "\\Qo*t.*h.er*\\E");
expectedResultsForAllTypes.put(Type.AFTER, "\\Qo*t.*h.er*\\E");
expectedResultsForAllTypes.put(Type.NOT_LIKE, "\\Qo*t.*h.er\\E.*");
expectedResultsForAllTypes.put(Type.LIKE, "\\Qo*t.*h.er\\E.*");
expectedResultsForAllTypes.put(Type.STARTING_WITH, "^\\Qo*t.*h.er*\\E");
expectedResultsForAllTypes.put(Type.ENDING_WITH, "\\Qo*t.*h.er*\\E$");
expectedResultsForAllTypes.put(Type.IS_NOT_EMPTY, "\\Qo*t.*h.er*\\E");
expectedResultsForAllTypes.put(Type.IS_EMPTY, "\\Qo*t.*h.er*\\E");
expectedResultsForAllTypes.put(Type.NOT_CONTAINING, ".*\\Qo*t.*h.er*\\E.*");
expectedResultsForAllTypes.put(Type.CONTAINING, ".*\\Qo*t.*h.er*\\E.*");
expectedResultsForAllTypes.put(Type.NOT_IN, "\\Qo*t.*h.er*\\E");
expectedResultsForAllTypes.put(Type.IN, "\\Qo*t.*h.er*\\E");
expectedResultsForAllTypes.put(Type.NEAR, "\\Qo*t.*h.er*\\E");
expectedResultsForAllTypes.put(Type.WITHIN, "\\Qo*t.*h.er*\\E");
expectedResultsForAllTypes.put(Type.REGEX, "o*t.*h.er*");
expectedResultsForAllTypes.put(Type.EXISTS, "\\Qo*t.*h.er*\\E");
expectedResultsForAllTypes.put(Type.TRUE, "\\Qo*t.*h.er*\\E");
expectedResultsForAllTypes.put(Type.FALSE, "\\Qo*t.*h.er*\\E");
expectedResultsForAllTypes.put(Type.NEGATING_SIMPLE_PROPERTY, "^\\Qo*t.*h.er*\\E$");
expectedResultsForAllTypes.put(Type.SIMPLE_PROPERTY, "^\\Qo*t.*h.er*\\E$");
}
@Test
public void testSpecialCases() {
SoftAssertions.assertSoftly(sa -> testParameters.forEach(tp -> tp.check(sa)));
}
@Test
public void testAllTypes() {
SoftAssertions.assertSoftly(
sa -> Arrays.stream(Type.values()).forEach(t -> //
test("o*t.*h.er*", t, expectedResultsForAllTypes.getOrDefault(t,"missed one"), t.toString())//
.check(sa)));
}
static class TestParameter {
TestParameter(String source, Type type, String expectedResult, String comment) {
this.source = source;
this.type = type;
this.expectedResult = expectedResult;
this.comment = comment;
}
static TestParameter test(String source, Type type, String expectedResult, String comment) {
return new TestParameter(source, type, expectedResult, comment);
}
private final String source;
private final Type type;
private final String expectedResult;
private final String comment;
private StringAssert check(SoftAssertions sa) {
return sa
.assertThat( //
MongoRegexCreator.INSTANCE.toRegularExpression(source, type)) //
.describedAs(comment) //
.isEqualTo(expectedResult);
}
}
}

View File

@@ -35,7 +35,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.mongodb.config.AbstractIntegrationTests;
import org.springframework.data.mongodb.core.IndexOperations;
import org.springframework.data.mongodb.core.index.IndexOperations;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.index.IndexDefinition;
import org.springframework.data.mongodb.core.mapping.Field;