Skip to content

Commit fb898ec

Browse files
committed
Polishing.
Remove Field type. Refactor container to subclass MongoDBAtlasLocalContainer. Introduce wait/synchronization to avoid container crashes on create index + list search indexes.
1 parent 0fa92e1 commit fb898ec

File tree

13 files changed

+143
-242
lines changed

13 files changed

+143
-242
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ private JsonSchemaProperty computeSchemaForProperty(List<MongoPersistentProperty
185185
Class<?> rawTargetType = computeTargetType(property); // target type before conversion
186186
Class<?> targetType = converter.getTypeMapper().getWriteTargetTypeFor(rawTargetType); // conversion target type
187187

188-
if((rawTargetType.isPrimitive() || ClassUtils.isPrimitiveArray(rawTargetType)) && targetType == Object.class) {
188+
if ((rawTargetType.isPrimitive() || ClassUtils.isPrimitiveArray(rawTargetType)) && targetType == Object.class) {
189189
targetType = rawTargetType;
190190
}
191191

@@ -338,8 +338,8 @@ private TypedJsonSchemaObject createSchemaObject(Object type, Collection<?> poss
338338

339339
private String computePropertyFieldName(PersistentProperty<?> property) {
340340

341-
return property instanceof MongoPersistentProperty mongoPersistentProperty ?
342-
mongoPersistentProperty.getFieldName() : property.getName();
341+
return property instanceof MongoPersistentProperty mongoPersistentProperty ? mongoPersistentProperty.getFieldName()
342+
: property.getName();
343343
}
344344

345345
private boolean isRequiredProperty(PersistentProperty<?> property) {

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

Lines changed: 33 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,14 @@
1616
package org.springframework.data.mongodb.core.aggregation;
1717

1818
import java.util.Arrays;
19-
import java.util.LinkedHashSet;
2019
import java.util.List;
21-
import java.util.Map;
2220
import java.util.Set;
2321
import java.util.function.Consumer;
2422
import java.util.stream.Collectors;
2523

2624
import org.bson.BinaryVector;
2725
import org.bson.Document;
26+
2827
import org.springframework.data.domain.Limit;
2928
import org.springframework.data.domain.Vector;
3029
import org.springframework.data.mongodb.core.mapping.MongoVector;
@@ -177,7 +176,7 @@ public String getKey() {
177176
* can't specify a number less than the number of documents to return (limit). This field is required if
178177
* {@link #searchType(SearchType)} is {@link SearchType#ANN} or {@link SearchType#DEFAULT}.
179178
*
180-
* @param numCandidates
179+
* @param numCandidates number of nearest neighbors to use during the search
181180
* @return a new {@link VectorSearchOperation} with {@code numCandidates} applied.
182181
*/
183182
@Contract("_ -> new")
@@ -338,20 +337,25 @@ public enum SearchType {
338337
ENN
339338
}
340339

341-
// A query path cannot only contain the name of the filed but may also hold additional information about the
342-
// analyzer to use;
343-
// "path": [ "names", "notes", { "value": "comments", "multi": "mySecondaryAnalyzer" } ]
344-
// see: https://www.mongodb.com/docs/atlas/atlas-search/path-construction/#std-label-ref-path
340+
/**
341+
* Value object capturing query paths.
342+
*/
345343
public static class QueryPaths {
346344

347-
Set<QueryPath<?>> paths;
345+
private final Set<QueryPath<?>> paths;
348346

349-
public static QueryPaths of(QueryPath<String> path) {
347+
private QueryPaths(Set<QueryPath<?>> paths) {
348+
this.paths = paths;
349+
}
350350

351-
QueryPaths queryPaths = new QueryPaths();
352-
queryPaths.paths = new LinkedHashSet<>(2);
353-
queryPaths.paths.add(path);
354-
return queryPaths;
351+
/**
352+
* Factory method to create {@link QueryPaths} from a single {@link QueryPath}.
353+
*
354+
* @param path
355+
* @return a new {@link QueryPaths} instance.
356+
*/
357+
public static QueryPaths of(QueryPath<String> path) {
358+
return new QueryPaths(Set.of(path));
355359
}
356360

357361
Object getPathObject() {
@@ -363,21 +367,19 @@ Object getPathObject() {
363367
}
364368
}
365369

370+
/**
371+
* Interface describing a query path contract. Query paths might be simple field names, wildcard paths, or
372+
* multi-paths. paths.
373+
*
374+
* @param <T>
375+
*/
366376
public interface QueryPath<T> {
367377

368378
T value();
369379

370380
static QueryPath<String> path(String field) {
371381
return new SimplePath(field);
372382
}
373-
374-
static QueryPath<Map<String, Object>> wildcard(String field) {
375-
return new WildcardPath(field);
376-
}
377-
378-
static QueryPath<Map<String, Object>> multi(String field, String analyzer) {
379-
return new MultiPath(field, analyzer);
380-
}
381383
}
382384

383385
public static class SimplePath implements QueryPath<String> {
@@ -394,36 +396,9 @@ public String value() {
394396
}
395397
}
396398

397-
public static class WildcardPath implements QueryPath<Map<String, Object>> {
398-
399-
String name;
400-
401-
public WildcardPath(String name) {
402-
this.name = name;
403-
}
404-
405-
@Override
406-
public Map<String, Object> value() {
407-
return Map.of("wildcard", name);
408-
}
409-
}
410-
411-
public static class MultiPath implements QueryPath<Map<String, Object>> {
412-
413-
String field;
414-
String analyzer;
415-
416-
public MultiPath(String field, String analyzer) {
417-
this.field = field;
418-
this.analyzer = analyzer;
419-
}
420-
421-
@Override
422-
public Map<String, Object> value() {
423-
return Map.of("value", field, "multi", analyzer);
424-
}
425-
}
426-
399+
/**
400+
* Fluent API to configure a path on the VectorSearchOperation builder.
401+
*/
427402
public interface PathContributor {
428403

429404
/**
@@ -436,6 +411,9 @@ public interface PathContributor {
436411
VectorContributor path(String path);
437412
}
438413

414+
/**
415+
* Fluent API to configure a vector on the VectorSearchOperation builder.
416+
*/
439417
public interface VectorContributor {
440418

441419
/**
@@ -458,7 +436,7 @@ default LimitContributor vector(float... vector) {
458436
* @return
459437
*/
460438
@Contract("_ -> this")
461-
default LimitContributor vector(byte... vector) {
439+
default LimitContributor vector(byte[] vector) {
462440
return vector(BinaryVector.int8Vector(vector));
463441
}
464442

@@ -510,6 +488,9 @@ default LimitContributor vector(BinaryVector vector) {
510488
LimitContributor vector(Vector vector);
511489
}
512490

491+
/**
492+
* Fluent API to configure a limit on the VectorSearchOperation builder.
493+
*/
513494
public interface LimitContributor {
514495

515496
/**

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616
package org.springframework.data.mongodb.core.convert;
1717

18-
import static org.springframework.data.convert.ConverterBuilder.reading;
18+
import static org.springframework.data.convert.ConverterBuilder.*;
1919

2020
import java.math.BigDecimal;
2121
import java.math.BigInteger;
@@ -47,6 +47,7 @@
4747
import org.bson.types.Code;
4848
import org.bson.types.Decimal128;
4949
import org.bson.types.ObjectId;
50+
5051
import org.springframework.core.convert.ConversionFailedException;
5152
import org.springframework.core.convert.TypeDescriptor;
5253
import org.springframework.core.convert.converter.ConditionalConverter;
@@ -118,8 +119,6 @@ static Collection<Object> getConvertersToRegister() {
118119
converters.add(reading(BsonUndefined.class, Object.class, it -> null));
119120
converters.add(reading(String.class, URI.class, URI::create).andWriting(URI::toString));
120121

121-
converters.add(ByteArrayConverterFactory.INSTANCE);
122-
123122
return converters;
124123
}
125124

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ public interface IndexOperations {
3333
*
3434
* @param indexDefinition must not be {@literal null}.
3535
* @return the index name.
36-
* @deprecated in favor of {@link #createIndex(IndexDefinition)}.
36+
* @deprecated since 4.5, in favor of {@link #createIndex(IndexDefinition)}.
3737
*/
3838
@Deprecated(since = "4.5", forRemoval = true)
3939
String ensureIndex(IndexDefinition indexDefinition);
4040

4141
/**
42-
* Create the index for the provided {@link IndexDefinition} exists for the collection indicated by the entity
43-
* class. If not it will be created.
42+
* Create the index for the provided {@link IndexDefinition} exists for the collection indicated by the entity class.
43+
* If not it will be created.
4444
*
4545
* @param indexDefinition must not be {@literal null}.
4646
* @return the index name.

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,23 @@ public interface ReactiveIndexOperations {
3333
*
3434
* @param indexDefinition must not be {@literal null}.
3535
* @return a {@link Mono} emitting the name of the index on completion.
36+
* @deprecated since 4.5, in favor of {@link #createIndex(IndexDefinition)}.
3637
*/
38+
@Deprecated(since = "4.5", forRemoval = true)
3739
Mono<String> ensureIndex(IndexDefinition indexDefinition);
3840

41+
/**
42+
* Create the index for the provided {@link IndexDefinition} exists for the collection indicated by the entity class.
43+
* If not it will be created.
44+
*
45+
* @param indexDefinition must not be {@literal null}.
46+
* @return the index name.
47+
* @since 4.5
48+
*/
49+
default Mono<String> createIndex(IndexDefinition indexDefinition) {
50+
return ensureIndex(indexDefinition);
51+
}
52+
3953
/**
4054
* Alters the index with given {@literal name}.
4155
*

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

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,6 @@
2727
*/
2828
public interface SearchIndexOperations {
2929

30-
/**
31-
* Create the index for the given {@link SearchIndexDefinition} in the collection indicated by the entity class.
32-
*
33-
* @param indexDefinition must not be {@literal null}.
34-
* @return the index name.
35-
*/
36-
// TODO: keep or just go with createIndex?
37-
default String ensureIndex(SearchIndexDefinition indexDefinition) {
38-
return createIndex(indexDefinition);
39-
}
40-
4130
/**
4231
* Create the index for the given {@link SearchIndexDefinition} in the collection indicated by the entity class.
4332
*
@@ -53,7 +42,6 @@ default String ensureIndex(SearchIndexDefinition indexDefinition) {
5342
*
5443
* @param indexDefinition the index definition.
5544
*/
56-
// TODO: keep or remove since it does not work reliably?
5745
void updateIndex(SearchIndexDefinition indexDefinition);
5846

5947
/**

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818
import java.util.Date;
1919
import java.util.regex.Pattern;
2020

21-
import org.bson.BinaryVector;
22-
import org.bson.BsonBinary;
2321
import org.bson.types.BSONTimestamp;
2422
import org.bson.types.Binary;
2523
import org.bson.types.Code;
@@ -57,8 +55,7 @@ public enum FieldType {
5755
INT32(15, Integer.class), //
5856
TIMESTAMP(16, BSONTimestamp.class), //
5957
INT64(17, Long.class), //
60-
DECIMAL128(18, Decimal128.class),
61-
VECTOR(5, BinaryVector.class);
58+
DECIMAL128(18, Decimal128.class);
6259

6360
private final int bsonType;
6461
private final Class<?> javaClass;

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/VectorSearchOperationUnitTests.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,22 @@
1515
*/
1616
package org.springframework.data.mongodb.core.aggregation;
1717

18+
import static org.assertj.core.api.Assertions.*;
19+
1820
import java.util.List;
1921

20-
import org.assertj.core.api.Assertions;
2122
import org.bson.Document;
2223
import org.junit.jupiter.api.Test;
24+
2325
import org.springframework.data.annotation.Id;
2426
import org.springframework.data.mongodb.core.aggregation.VectorSearchOperation.SearchType;
2527
import org.springframework.data.mongodb.core.mapping.Field;
2628
import org.springframework.data.mongodb.core.query.Criteria;
2729
import org.springframework.data.mongodb.util.aggregation.TestAggregationContext;
2830

2931
/**
32+
* Unit tests for {@link VectorSearchOperation}.
33+
*
3034
* @author Christoph Strobl
3135
*/
3236
class VectorSearchOperationUnitTests {
@@ -40,7 +44,7 @@ class VectorSearchOperationUnitTests {
4044
void requiredArgs() {
4145

4246
List<Document> stages = SEARCH_OPERATION.toPipelineStages(Aggregation.DEFAULT_CONTEXT);
43-
Assertions.assertThat(stages).containsExactly(new Document("$vectorSearch", $VECTOR_SEARCH));
47+
assertThat(stages).containsExactly(new Document("$vectorSearch", $VECTOR_SEARCH));
4448
}
4549

4650
@Test // GH-4706
@@ -53,15 +57,15 @@ void optionalArgs() {
5357

5458
Document filter = new Document("$and",
5559
List.of(new Document("year", new Document("$gt", 1955)), new Document("year", new Document("$lt", 1975))));
56-
Assertions.assertThat(stages).containsExactly(new Document("$vectorSearch",
60+
assertThat(stages).containsExactly(new Document("$vectorSearch",
5761
new Document($VECTOR_SEARCH).append("exact", true).append("filter", filter).append("numCandidates", 150)));
5862
}
5963

6064
@Test // GH-4706
6165
void withScore() {
6266

6367
List<Document> stages = SEARCH_OPERATION.withSearchScore().toPipelineStages(Aggregation.DEFAULT_CONTEXT);
64-
Assertions.assertThat(stages).containsExactly(new Document("$vectorSearch", $VECTOR_SEARCH),
68+
assertThat(stages).containsExactly(new Document("$vectorSearch", $VECTOR_SEARCH),
6569
new Document("$addFields", new Document("score", new Document("$meta", "vectorSearchScore"))));
6670
}
6771

@@ -70,7 +74,7 @@ void withScoreFilter() {
7074

7175
List<Document> stages = SEARCH_OPERATION.withFilterBySore(score -> score.gt(50))
7276
.toPipelineStages(Aggregation.DEFAULT_CONTEXT);
73-
Assertions.assertThat(stages).containsExactly(new Document("$vectorSearch", $VECTOR_SEARCH),
77+
assertThat(stages).containsExactly(new Document("$vectorSearch", $VECTOR_SEARCH),
7478
new Document("$addFields", new Document("score", new Document("$meta", "vectorSearchScore"))),
7579
new Document("$match", new Document("score", new Document("$gt", 50))));
7680
}
@@ -80,7 +84,7 @@ void withScoreFilterOnCustomFieldName() {
8084

8185
List<Document> stages = SEARCH_OPERATION.withFilterBySore(score -> score.gt(50)).withSearchScore("s-c-o-r-e")
8286
.toPipelineStages(Aggregation.DEFAULT_CONTEXT);
83-
Assertions.assertThat(stages).containsExactly(new Document("$vectorSearch", $VECTOR_SEARCH),
87+
assertThat(stages).containsExactly(new Document("$vectorSearch", $VECTOR_SEARCH),
8488
new Document("$addFields", new Document("s-c-o-r-e", new Document("$meta", "vectorSearchScore"))),
8589
new Document("$match", new Document("s-c-o-r-e", new Document("$gt", 50))));
8690
}
@@ -95,7 +99,7 @@ void mapsCriteriaToDomainType() {
9599

96100
Document filter = new Document("$and",
97101
List.of(new Document("year", new Document("$gt", 1955)), new Document("year", new Document("$lt", 1975))));
98-
Assertions.assertThat(stages)
102+
assertThat(stages)
99103
.containsExactly(new Document("$vectorSearch", new Document($VECTOR_SEARCH).append("filter", filter)));
100104
}
101105

0 commit comments

Comments
 (0)