Skip to content

Commit dc4a30a

Browse files
christophstroblodrotbohm
authored andcommitted
DATAMONGO-1467 - Add support for MongoDB 3.2 partialFilterExpression for index creation.
We now support partial filter expression on indexes via Index.partial(…). This allows to create partial indexes that only index the documents in a collection that meet a specified filter expression. new Index().named("idx").on("k3y", ASC).partial(filter(where("age").gte(10))) The filter expression can be set via a plain DBObject or a CriteriaDefinition and is mapped against the associated domain type. Original pull request: #431.
1 parent 29e405b commit dc4a30a

File tree

9 files changed

+414
-60
lines changed

9 files changed

+414
-60
lines changed

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

Lines changed: 50 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2015 the original author or authors.
2+
* Copyright 2011-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,17 +15,15 @@
1515
*/
1616
package org.springframework.data.mongodb.core;
1717

18-
import static org.springframework.data.domain.Sort.Direction.*;
19-
2018
import java.util.ArrayList;
21-
import java.util.Arrays;
2219
import java.util.Collection;
2320
import java.util.List;
2421

2522
import org.springframework.dao.DataAccessException;
23+
import org.springframework.data.mongodb.core.convert.QueryMapper;
2624
import org.springframework.data.mongodb.core.index.IndexDefinition;
27-
import org.springframework.data.mongodb.core.index.IndexField;
2825
import org.springframework.data.mongodb.core.index.IndexInfo;
26+
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
2927
import org.springframework.util.Assert;
3028

3129
import com.mongodb.DBCollection;
@@ -42,12 +40,11 @@
4240
*/
4341
public class DefaultIndexOperations implements IndexOperations {
4442

45-
private static final Double ONE = Double.valueOf(1);
46-
private static final Double MINUS_ONE = Double.valueOf(-1);
47-
private static final Collection<String> TWO_D_IDENTIFIERS = Arrays.asList("2d", "2dsphere");
48-
43+
public static final String PARTIAL_FILTER_EXPRESSION_KEY = "partialFilterExpression";
4944
private final MongoOperations mongoOperations;
5045
private final String collectionName;
46+
private final QueryMapper mapper;
47+
private final Class<?> type;
5148

5249
/**
5350
* Creates a new {@link DefaultIndexOperations}.
@@ -56,29 +53,72 @@ public class DefaultIndexOperations implements IndexOperations {
5653
* @param collectionName must not be {@literal null}.
5754
*/
5855
public DefaultIndexOperations(MongoOperations mongoOperations, String collectionName) {
56+
this(mongoOperations, collectionName, null);
57+
}
58+
59+
/**
60+
* Creates a new {@link DefaultIndexOperations}.
61+
*
62+
* @param mongoOperations must not be {@literal null}.
63+
* @param collectionName must not be {@literal null}.
64+
* @param type Type used for mapping potential partial index filter expression. Can be {@literal null}.
65+
* @since 1.10
66+
*/
67+
public DefaultIndexOperations(MongoOperations mongoOperations, String collectionName, Class<?> type) {
5968

6069
Assert.notNull(mongoOperations, "MongoOperations must not be null!");
6170
Assert.notNull(collectionName, "Collection name can not be null!");
6271

6372
this.mongoOperations = mongoOperations;
6473
this.collectionName = collectionName;
74+
this.mapper = new QueryMapper(mongoOperations.getConverter());
75+
this.type = type;
6576
}
6677

6778
/*
6879
* (non-Javadoc)
6980
* @see org.springframework.data.mongodb.core.IndexOperations#ensureIndex(org.springframework.data.mongodb.core.index.IndexDefinition)
7081
*/
7182
public void ensureIndex(final IndexDefinition indexDefinition) {
83+
7284
mongoOperations.execute(collectionName, new CollectionCallback<Object>() {
7385
public Object doInCollection(DBCollection collection) throws MongoException, DataAccessException {
7486
DBObject indexOptions = indexDefinition.getIndexOptions();
87+
88+
if (indexOptions != null && indexOptions.containsField(PARTIAL_FILTER_EXPRESSION_KEY)) {
89+
90+
Assert.isInstanceOf(DBObject.class, indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY));
91+
92+
indexOptions.put(PARTIAL_FILTER_EXPRESSION_KEY,
93+
mapper.getMappedObject((DBObject) indexOptions.get(PARTIAL_FILTER_EXPRESSION_KEY),
94+
lookupPersistentEntity(type, collectionName)));
95+
}
96+
7597
if (indexOptions != null) {
7698
collection.createIndex(indexDefinition.getIndexKeys(), indexOptions);
7799
} else {
78100
collection.createIndex(indexDefinition.getIndexKeys());
79101
}
80102
return null;
81103
}
104+
105+
private MongoPersistentEntity<?> lookupPersistentEntity(Class<?> entityType, String collection) {
106+
107+
if (entityType != null) {
108+
return mongoOperations.getConverter().getMappingContext().getPersistentEntity(entityType);
109+
}
110+
111+
Collection<? extends MongoPersistentEntity<?>> entities = mongoOperations.getConverter().getMappingContext()
112+
.getPersistentEntities();
113+
114+
for (MongoPersistentEntity<?> entity : entities) {
115+
if (entity.getCollection().equals(collection)) {
116+
return entity;
117+
}
118+
}
119+
120+
return null;
121+
}
82122
});
83123
}
84124

@@ -136,44 +176,7 @@ private List<IndexInfo> getIndexData(List<DBObject> dbObjectList) {
136176
List<IndexInfo> indexInfoList = new ArrayList<IndexInfo>();
137177

138178
for (DBObject ix : dbObjectList) {
139-
140-
DBObject keyDbObject = (DBObject) ix.get("key");
141-
int numberOfElements = keyDbObject.keySet().size();
142-
143-
List<IndexField> indexFields = new ArrayList<IndexField>(numberOfElements);
144-
145-
for (String key : keyDbObject.keySet()) {
146-
147-
Object value = keyDbObject.get(key);
148-
149-
if (TWO_D_IDENTIFIERS.contains(value)) {
150-
indexFields.add(IndexField.geo(key));
151-
} else if ("text".equals(value)) {
152-
153-
DBObject weights = (DBObject) ix.get("weights");
154-
for (String fieldName : weights.keySet()) {
155-
indexFields.add(IndexField.text(fieldName, Float.valueOf(weights.get(fieldName).toString())));
156-
}
157-
158-
} else {
159-
160-
Double keyValue = new Double(value.toString());
161-
162-
if (ONE.equals(keyValue)) {
163-
indexFields.add(IndexField.create(key, ASC));
164-
} else if (MINUS_ONE.equals(keyValue)) {
165-
indexFields.add(IndexField.create(key, DESC));
166-
}
167-
}
168-
}
169-
170-
String name = ix.get("name").toString();
171-
172-
boolean unique = ix.containsField("unique") ? (Boolean) ix.get("unique") : false;
173-
boolean dropDuplicates = ix.containsField("dropDups") ? (Boolean) ix.get("dropDups") : false;
174-
boolean sparse = ix.containsField("sparse") ? (Boolean) ix.get("sparse") : false;
175-
String language = ix.containsField("default_language") ? (String) ix.get("default_language") : "";
176-
indexInfoList.add(new IndexInfo(indexFields, name, unique, dropDuplicates, sparse, language));
179+
indexInfoList.add(IndexInfo.indexInfoOf(ix));
177180
}
178181

179182
return indexInfoList;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ public IndexOperations indexOps(String collectionName) {
557557
}
558558

559559
public IndexOperations indexOps(Class<?> entityClass) {
560-
return new DefaultIndexOperations(this, determineCollectionName(entityClass));
560+
return new DefaultIndexOperations(this, determineCollectionName(entityClass), entityClass);
561561
}
562562

563563
public BulkOperations bulkOps(BulkMode bulkMode, String collectionName) {

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/GeospatialIndex.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2010-2014 the original author or authors.
2+
* Copyright 2010-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@ public class GeospatialIndex implements IndexDefinition {
3939
private GeoSpatialIndexType type = GeoSpatialIndexType.GEO_2D;
4040
private Double bucketSize = 1.0;
4141
private String additionalField;
42+
private IndexFilter filter;
4243

4344
/**
4445
* Creates a new {@link GeospatialIndex} for the given field.
@@ -119,6 +120,22 @@ public GeospatialIndex withAdditionalField(String fieldName) {
119120
return this;
120121
}
121122

123+
124+
/**
125+
* Only index the documents in a collection that meet a specified {@link IndexFilter filter expression}.
126+
*
127+
* @param filter can be {@literal null}.
128+
* @return
129+
* @see <a href=
130+
* "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/</a>
131+
* @since 1.10
132+
*/
133+
public GeospatialIndex partial(IndexFilter filter) {
134+
135+
this.filter = filter;
136+
return this;
137+
}
138+
122139
public DBObject getIndexKeys() {
123140

124141
DBObject dbo = new BasicDBObject();
@@ -186,6 +203,10 @@ public DBObject getIndexOptions() {
186203
break;
187204
}
188205

206+
if (filter != null) {
207+
dbo.put("partialFilterExpression", filter.getFilterObject());
208+
}
209+
189210
return dbo;
190211
}
191212

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Index.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2010-2015 the original author or authors.
2+
* Copyright 2010-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -63,6 +63,8 @@ public enum Duplicates {
6363

6464
private long expire = -1;
6565

66+
private IndexFilter filter;
67+
6668
public Index() {}
6769

6870
public Index(String key, Direction direction) {
@@ -176,6 +178,21 @@ public Index unique(Duplicates duplicates) {
176178
return unique();
177179
}
178180

181+
/**
182+
* Only index the documents in a collection that meet a specified {@link IndexFilter filter expression}.
183+
*
184+
* @param filter can be {@literal null}.
185+
* @return
186+
* @see <a href=
187+
* "https://docs.mongodb.com/manual/core/index-partial/">https://docs.mongodb.com/manual/core/index-partial/</a>
188+
* @since 1.10
189+
*/
190+
public Index partial(IndexFilter filter) {
191+
192+
this.filter = filter;
193+
return this;
194+
}
195+
179196
/*
180197
* (non-Javadoc)
181198
* @see org.springframework.data.mongodb.core.index.IndexDefinition#getIndexKeys()
@@ -213,6 +230,9 @@ public DBObject getIndexOptions() {
213230
dbo.put("expireAfterSeconds", expire);
214231
}
215232

233+
if (filter != null) {
234+
dbo.put("partialFilterExpression", filter.getFilterObject());
235+
}
216236
return dbo;
217237
}
218238

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2016. the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.core.index;
17+
18+
import com.mongodb.DBObject;
19+
20+
/**
21+
* Use {@link IndexFilter} to create the partial filter expression used when creating
22+
* <a href="https://docs.mongodb.com/manual/core/index-partial/">Partial Indexes</a>.
23+
*
24+
* @author Christoph Strobl
25+
* @since 1.10
26+
*/
27+
public interface IndexFilter {
28+
29+
/**
30+
* Get the raw (unmapped) filter expression.
31+
*
32+
* @return
33+
*/
34+
DBObject getFilterObject();
35+
36+
}

0 commit comments

Comments
 (0)