Skip to content

Commit 7c07eef

Browse files
committed
Introduce explicit index field matching methods.
Closes #5187 Signed-off-by: dragonfsky <dragonfsky@gmail.com>
1 parent cbc9273 commit 7c07eef

3 files changed

Lines changed: 106 additions & 5 deletions

File tree

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

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.Collections;
2525
import java.util.List;
2626
import java.util.Optional;
27+
import java.util.Set;
2728
import java.util.stream.Collectors;
2829

2930
import org.bson.Document;
@@ -41,6 +42,7 @@
4142
* @author Oliver Gierke
4243
* @author Christoph Strobl
4344
* @author Mark Paluch
45+
* @author dragonfsky
4446
*/
4547
public class IndexInfo {
4648

@@ -182,16 +184,75 @@ public List<IndexField> getIndexFields() {
182184
}
183185

184186
/**
185-
* Returns whether the index is covering exactly the fields given independently of the order.
187+
* Returns whether the index contains all fields given independently of their position.
186188
*
187189
* @param keys must not be {@literal null}.
188190
* @return
191+
* @since 5.1
189192
*/
193+
public boolean containsAllFields(Collection<String> keys) {
194+
195+
Assert.notNull(keys, "Collection of keys must not be null");
196+
197+
return getIndexFieldKeys().containsAll(keys);
198+
}
199+
200+
/**
201+
* Returns whether the index contains all fields given independently of their position.
202+
*
203+
* @param keys must not be {@literal null}.
204+
* @return
205+
* @deprecated since 5.1. Use {@link #containsAllFields(Collection)}, {@link #isIndexForFieldsExactly(Collection)}, or
206+
* {@link #coversFields(Collection)} to express the intended index field matching semantics.
207+
*/
208+
@Deprecated(since = "5.1")
190209
public boolean isIndexForFields(Collection<String> keys) {
210+
return containsAllFields(keys);
211+
}
212+
213+
/**
214+
* Returns whether the index matches exactly the fields given independently of the order.
215+
*
216+
* @param keys must not be {@literal null}.
217+
* @return
218+
* @since 5.1
219+
*/
220+
public boolean isIndexForFieldsExactly(Collection<String> keys) {
191221

192222
Assert.notNull(keys, "Collection of keys must not be null");
193223

194-
return this.indexFields.stream().map(IndexField::getKey).collect(Collectors.toSet()).containsAll(keys);
224+
Set<String> indexFieldKeys = getIndexFieldKeys();
225+
Set<String> keysToCheck = keys.stream().collect(Collectors.toSet());
226+
227+
return indexFieldKeys.size() == keysToCheck.size() && indexFieldKeys.containsAll(keysToCheck);
228+
}
229+
230+
/**
231+
* Returns whether the index can efficiently support a query using the given fields according to MongoDB compound
232+
* index prefix rules.
233+
*
234+
* @param keys must not be {@literal null}.
235+
* @return
236+
* @since 5.1
237+
*/
238+
public boolean coversFields(Collection<String> keys) {
239+
240+
Assert.notNull(keys, "Collection of keys must not be null");
241+
242+
Set<String> keysToCheck = keys.stream().collect(Collectors.toSet());
243+
244+
if (keysToCheck.size() > indexFields.size()) {
245+
return false;
246+
}
247+
248+
Set<String> indexFieldPrefix = indexFields.stream().limit(keysToCheck.size()).map(IndexField::getKey)
249+
.collect(Collectors.toSet());
250+
251+
return indexFieldPrefix.containsAll(keysToCheck);
252+
}
253+
254+
private Set<String> getIndexFieldKeys() {
255+
return this.indexFields.stream().map(IndexField::getKey).collect(Collectors.toSet());
195256
}
196257

197258
public String getName() {

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexInfoUnitTests.java

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
* @author Oliver Gierke
3131
* @author Christoph Strobl
3232
* @author Stefan Tirea
33+
* @author dragonfsky
3334
*/
3435
class IndexInfoUnitTests {
3536

@@ -50,14 +51,52 @@ class IndexInfoUnitTests {
5051
}
5152
""";
5253

53-
@Test
54-
void isIndexForFieldsCorrectly() {
54+
@Test // GH-5187
55+
@SuppressWarnings("deprecation")
56+
void isIndexForFieldsRetainsContainsAllBehavior() {
5557

5658
IndexField fooField = IndexField.create("foo", Direction.ASC);
5759
IndexField barField = IndexField.create("bar", Direction.DESC);
5860

5961
IndexInfo info = new IndexInfo(Arrays.asList(fooField, barField), "myIndex", false, false, "");
6062
assertThat(info.isIndexForFields(Arrays.asList("foo", "bar"))).isTrue();
63+
assertThat(info.isIndexForFields(Arrays.asList("foo"))).isTrue();
64+
}
65+
66+
@Test // GH-5187
67+
void containsAllFieldsReturnsTrueForFieldSubset() {
68+
69+
IndexInfo info = new IndexInfo(Arrays.asList(IndexField.create("foo", Direction.ASC),
70+
IndexField.create("bar", Direction.DESC)), "myIndex", false, false, "");
71+
72+
assertThat(info.containsAllFields(Arrays.asList("foo"))).isTrue();
73+
assertThat(info.containsAllFields(Arrays.asList("bar", "foo"))).isTrue();
74+
assertThat(info.containsAllFields(Arrays.asList("foo", "baz"))).isFalse();
75+
}
76+
77+
@Test // GH-5187
78+
void isIndexForFieldsExactlyRequiresSameFields() {
79+
80+
IndexInfo info = new IndexInfo(Arrays.asList(IndexField.create("foo", Direction.ASC),
81+
IndexField.create("bar", Direction.DESC)), "myIndex", false, false, "");
82+
83+
assertThat(info.isIndexForFieldsExactly(Arrays.asList("foo", "bar"))).isTrue();
84+
assertThat(info.isIndexForFieldsExactly(Arrays.asList("bar", "foo"))).isTrue();
85+
assertThat(info.isIndexForFieldsExactly(Arrays.asList("foo"))).isFalse();
86+
assertThat(info.isIndexForFieldsExactly(Arrays.asList("foo", "bar", "baz"))).isFalse();
87+
}
88+
89+
@Test // GH-5187
90+
void coversFieldsOnlyMatchesIndexPrefixes() {
91+
92+
IndexInfo info = new IndexInfo(Arrays.asList(IndexField.create("foo", Direction.ASC),
93+
IndexField.create("bar", Direction.DESC), IndexField.create("baz", Direction.ASC)), "myIndex", false,
94+
false, "");
95+
96+
assertThat(info.coversFields(Arrays.asList("foo"))).isTrue();
97+
assertThat(info.coversFields(Arrays.asList("bar", "foo"))).isTrue();
98+
assertThat(info.coversFields(Arrays.asList("bar"))).isFalse();
99+
assertThat(info.coversFields(Arrays.asList("foo", "baz"))).isFalse();
61100
}
62101

63102
@Test // DATAMONGO-2170

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/RepositoryIndexCreationIntegrationTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
* Integration test for index creation for query methods.
4242
*
4343
* @author Oliver Gierke
44+
* @author dragonfsky
4445
*/
4546
@RunWith(SpringRunner.class)
4647
@ContextConfiguration
@@ -84,7 +85,7 @@ public void testname() {
8485
private static void assertHasIndexForField(List<IndexInfo> indexInfo, String... fields) {
8586

8687
for (IndexInfo info : indexInfo) {
87-
if (info.isIndexForFields(Arrays.asList(fields))) {
88+
if (info.containsAllFields(Arrays.asList(fields))) {
8889
return;
8990
}
9091
}

0 commit comments

Comments
 (0)