diff --git a/.gemini/commands/core/javadocs.toml b/.gemini/commands/core/javadocs.toml
new file mode 100644
index 0000000..16944f2
--- /dev/null
+++ b/.gemini/commands/core/javadocs.toml
@@ -0,0 +1,6 @@
+description="Update javadocs in the core module"
+prompt="""
+Analyze the local main git branch and current one that is currently checkout and check what change in code were added.
+Based on your analyzis update javadocs to public methods in production that were changed.
+Or add javadocs to new public methods and types in production code.
+"""
\ No newline at end of file
diff --git a/.gemini/commands/core/readme.toml b/.gemini/commands/core/readme.toml
new file mode 100644
index 0000000..221ede7
--- /dev/null
+++ b/.gemini/commands/core/readme.toml
@@ -0,0 +1,5 @@
+description="Update README.md file in the core module"
+prompt="""
+Analyze the local main git branch and current one that is currently checkout and check what change in code were added.
+Based on your analyzis update README.md file in the core module and mention what was changed or added.
+"""
\ No newline at end of file
diff --git a/.gemini/commands/root/changelog.toml b/.gemini/commands/root/changelog.toml
new file mode 100644
index 0000000..c71e0a7
--- /dev/null
+++ b/.gemini/commands/root/changelog.toml
@@ -0,0 +1,5 @@
+description="Update CHANGELOG.md file in the root module"
+prompt="""
+Analyze the local main git branch and current one that is currently checkout and check what changes were done.
+Based on your analyzis update GHANGELOG.md file in the root module.
+"""
\ No newline at end of file
diff --git a/.gemini/commands/root/readme.toml b/.gemini/commands/root/readme.toml
new file mode 100644
index 0000000..79ac1ab
--- /dev/null
+++ b/.gemini/commands/root/readme.toml
@@ -0,0 +1,5 @@
+description="Update README.md file in the root module"
+prompt="""
+Analyze the local main git branch and current one that is currently checkout and check what changes were done.
+Based on your analyzis update README.md file in the root module and mention what was changed or added.
+"""
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b8647dd..87dcec0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
* [Unreleased](#unreleased)
+* [0.8.0](#080---2026-03-16)
* [0.7.0](#070---2026-03-06)
* [0.6.0](#060---2026-03-04)
* [0.5.1](#051---2026-03-03)
@@ -18,6 +19,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+## [0.8.0] - 2026-03-16
+
+### Added
+
+#### Core Module
+* Added support for `$search` operator translation to MongoDB Atlas Search aggregation stages. ([#37](https://github.com/starnowski/jamolingo/issues/37))
+* `com.github.starnowski.jamolingo.core.operators.search.ODataSearchToMongoAtlasSearchParser` class for parsing OData search options. ([#37](https://github.com/starnowski/jamolingo/issues/37))
+* `com.github.starnowski.jamolingo.core.operators.search.ODataSearchToMongoAtlasSearchOptions` and `DefaultODataSearchToMongoAtlasSearchOptions` for search configuration. ([#37](https://github.com/starnowski/jamolingo/issues/37))
+* Support for search score filtering using `$set` and `$match` stages with configurable field names. ([#37](https://github.com/starnowski/jamolingo/issues/37))
+* `com.github.starnowski.jamolingo.core.operators.search.SearchOperatorResult` and `SearchOperatorResultForAtlasSearch` interfaces. ([#37](https://github.com/starnowski/jamolingo/issues/37))
+
+### Changed
+
## [0.7.0] - 2026-03-06
### Added
diff --git a/README.md b/README.md
index 30f1d12..ce2fa2f 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,7 @@ A Java library for translating OData queries and concepts into MongoDB aggregati
### Prerequisites
* **Java 8** or higher.
* **MongoDB 4.4** or higher (supporting aggregation pipelines and explain).
+* **MongoDB Atlas** or **MongoDB Atlas Local** (required for `$search` operator support).
### Installation (Maven)
Add the following dependencies to your `pom.xml`:
@@ -33,13 +34,13 @@ Add the following dependencies to your `pom.xml`:
com.github.starnowski.jamolingo
core
- 0.7.0
+ 0.8.0-SNAPSHOT
com.github.starnowski.jamolingo
perf
- 0.7.0
+ 0.8.0-SNAPSHOT
```
@@ -84,6 +85,9 @@ The `core` module contains the primary logic for translating OData concepts and
* Comparison (`eq`, `ne`, `in`, etc.) and Logical (`and`, `or`, `not`) operators.
* String, Math, and Date/Time functions.
* Collection operators (`any`, `all`) and `/$count`.
+* Translates `$search` to MongoDB Atlas Search stages (`$search`, `$set`, `$match`) with support for:
+ * Full-text search with logical operators (`AND`, `OR`, `NOT`).
+ * Search score filtering and custom score field names.
* Translates `$select` to MongoDB `$project` stages.
* Translates `$orderby`, `$top`, `$skip`, and `$count` to corresponding MongoDB stages (`$sort`, `$limit`, `$skip`, `$count`).
* Handles OData-to-MongoDB mapping configuration and supports customizing mappings via overrides.
diff --git a/common/json/pom.xml b/common/json/pom.xml
index fbadfc2..822aafa 100644
--- a/common/json/pom.xml
+++ b/common/json/pom.xml
@@ -6,7 +6,7 @@
com.github.starnowski.jamolingo
common
- 0.7.0
+ 0.8.0-SNAPSHOT
json
diff --git a/common/pom.xml b/common/pom.xml
index c26557a..a5c66f6 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -6,7 +6,7 @@
com.github.starnowski.jamolingo
parent
- 0.7.0
+ 0.8.0-SNAPSHOT
pom
diff --git a/compat-driver-5.x/pom.xml b/compat-driver-5.x/pom.xml
index 3781125..6791668 100644
--- a/compat-driver-5.x/pom.xml
+++ b/compat-driver-5.x/pom.xml
@@ -6,7 +6,7 @@
com.github.starnowski.jamolingo
parent
- 0.7.0
+ 0.8.0-SNAPSHOT
compat-driver-5.x
@@ -72,6 +72,11 @@
1.5.3
test
+
+ org.testcontainers
+ testcontainers
+ test
+
com.github.starnowski.jamolingo
perf
diff --git a/compat-driver-5.x/src/test/java/com/github/starnowski/jamolingo/MongoAtlasResource.java b/compat-driver-5.x/src/test/java/com/github/starnowski/jamolingo/MongoAtlasResource.java
new file mode 100644
index 0000000..dc94ee3
--- /dev/null
+++ b/compat-driver-5.x/src/test/java/com/github/starnowski/jamolingo/MongoAtlasResource.java
@@ -0,0 +1,48 @@
+package com.github.starnowski.jamolingo;
+
+import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
+import java.time.Duration;
+import java.util.Collections;
+import java.util.Map;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+
+public class MongoAtlasResource implements QuarkusTestResourceLifecycleManager {
+
+ private GenericContainer> mongoAtlasContainer;
+
+ @Override
+ public Map start() {
+ mongoAtlasContainer =
+ new GenericContainer<>("mongodb/mongodb-atlas-local:7.0.11")
+ .withPrivilegedMode(true)
+ .withCreateContainerCmdModifier(
+ cmd -> {
+ cmd.getHostConfig().withMemory(4 * 1024 * 1024 * 1024L);
+ cmd.getHostConfig().withShmSize(2 * 1024 * 1024 * 1024L);
+ })
+ .withExposedPorts(27017, 27027)
+ .withEnv("MONGOT_LOG_FILE", "/dev/stdout")
+ .waitingFor(Wait.forListeningPort().withStartupTimeout(Duration.ofMinutes(5)));
+
+ mongoAtlasContainer.start();
+ Wait.forLogMessage(".*Starting TCP server.*", 2)
+ .withStartupTimeout(Duration.ofSeconds(30))
+ .waitUntilReady(mongoAtlasContainer);
+ Wait.forLogMessage(".*Starting message server.*", 2)
+ .withStartupTimeout(Duration.ofSeconds(30))
+ .waitUntilReady(mongoAtlasContainer);
+ String connectionString =
+ String.format(
+ "mongodb://%s:%d/?directConnection=true",
+ mongoAtlasContainer.getHost(), mongoAtlasContainer.getMappedPort(27017));
+ return Collections.singletonMap("quarkus.mongodb.connection-string", connectionString);
+ }
+
+ @Override
+ public void stop() {
+ if (mongoAtlasContainer != null) {
+ mongoAtlasContainer.stop();
+ }
+ }
+}
diff --git a/compat-driver-5.x/src/test/java/com/github/starnowski/jamolingo/compat/driver/operators/search/SearchOperatorTest.java b/compat-driver-5.x/src/test/java/com/github/starnowski/jamolingo/compat/driver/operators/search/SearchOperatorTest.java
new file mode 100644
index 0000000..41d06e5
--- /dev/null
+++ b/compat-driver-5.x/src/test/java/com/github/starnowski/jamolingo/compat/driver/operators/search/SearchOperatorTest.java
@@ -0,0 +1,202 @@
+package com.github.starnowski.jamolingo.compat.driver.operators.search;
+
+import com.github.starnowski.jamolingo.AbstractItTest;
+import com.github.starnowski.jamolingo.MongoAtlasResource;
+import com.github.starnowski.jamolingo.core.operators.search.DefaultODataSearchToMongoAtlasSearchOptions;
+import com.github.starnowski.jamolingo.core.operators.search.ODataSearchToMongoAtlasSearchOptions;
+import com.github.starnowski.jamolingo.core.operators.search.ODataSearchToMongoAtlasSearchParser;
+import com.github.starnowski.jamolingo.core.operators.search.SearchDocumentForQueryStringFactory;
+import com.github.starnowski.jamolingo.core.operators.search.SearchDocumentForQueryStringFactory.QueryStringParsingResult;
+import com.github.starnowski.jamolingo.core.operators.search.SearchOperatorResult;
+import com.github.starnowski.jamolingo.junit5.MongoDocument;
+import com.github.starnowski.jamolingo.junit5.MongoSetup;
+import com.mongodb.client.MongoClient;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoDatabase;
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusTest;
+import jakarta.inject.Inject;
+import java.util.*;
+import java.util.stream.Collectors;
+import javax.xml.stream.XMLStreamException;
+import org.apache.olingo.commons.api.edm.Edm;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ODataApplicationException;
+import org.apache.olingo.server.api.uri.UriInfo;
+import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitException;
+import org.apache.olingo.server.api.uri.queryoption.search.SearchExpression;
+import org.apache.olingo.server.core.uri.parser.Parser;
+import org.apache.olingo.server.core.uri.parser.UriParserException;
+import org.apache.olingo.server.core.uri.validator.UriValidationException;
+import org.bson.Document;
+import org.bson.conversions.Bson;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+@QuarkusTest
+@QuarkusTestResource(MongoAtlasResource.class)
+public class SearchOperatorTest extends AbstractItTest {
+
+ @Inject protected MongoClient mongoClient;
+
+ @ParameterizedTest
+ @MethodSource("provideSearchTests")
+ @MongoSetup(
+ mongoDocuments = {
+ @MongoDocument(
+ database = "testdb",
+ collection = "Items",
+ bsonFilePath = "bson/search/search1.json"),
+ @MongoDocument(
+ database = "testdb",
+ collection = "Items",
+ bsonFilePath = "bson/search/search2.json")
+ })
+ public void shouldReturnExpectedDocumentsBasedOnSearchOperator(
+ String search, Set expectedPlainStrings)
+ throws UriValidationException,
+ UriParserException,
+ XMLStreamException,
+ ExpressionVisitException,
+ ODataApplicationException,
+ InterruptedException {
+ // GIVEN
+ MongoDatabase database = mongoClient.getDatabase("testdb");
+ MongoCollection collection = database.getCollection("Items");
+ ensureSearchIndex(collection);
+
+ Edm edm = loadEmdProvider("edm/edm6_filter_main.xml");
+ UriInfo uriInfo =
+ new Parser(edm, OData.newInstance()).parseUri("examples2", "$search=" + search, null, null);
+ ODataSearchToMongoAtlasSearchParser tested =
+ new ODataSearchToMongoAtlasSearchParser(
+ new SearchDocumentForQueryStringFactory() {
+ @Override
+ public Bson build(
+ SearchExpression searchExpression,
+ QueryStringParsingResult queryStringParsingResult,
+ ODataSearchToMongoAtlasSearchOptions options) {
+ return new Document("index", "atlas_search_index")
+ .append(
+ "queryString",
+ new Document("query", queryStringParsingResult.getQuery())
+ .append("defaultPath", "plainString"));
+ }
+ });
+
+ // WHEN
+ SearchOperatorResult result = tested.parse(uriInfo.getSearchOption());
+ List pipeline = new ArrayList<>(result.getStageObjects());
+ System.out.println(new Document("pipeline", pipeline).toJson());
+
+ // THEN
+ List results = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ results.clear();
+ collection.aggregate(pipeline).into(results);
+ if (results.size() == expectedPlainStrings.size()) {
+ break;
+ }
+ Thread.sleep(500);
+ }
+
+ Assertions.assertEquals(expectedPlainStrings.size(), results.size());
+ Set actual =
+ results.stream()
+ .map(d -> d.get("plainString"))
+ .filter(Objects::nonNull)
+ .map(s -> (String) s)
+ .collect(Collectors.toSet());
+ Assertions.assertEquals(expectedPlainStrings, actual);
+ }
+
+ @Test
+ @MongoSetup(
+ mongoDocuments = {
+ @MongoDocument(
+ database = "testdb",
+ collection = "Items",
+ bsonFilePath = "bson/search/search1.json"),
+ @MongoDocument(
+ database = "testdb",
+ collection = "Items",
+ bsonFilePath = "bson/search/search2.json")
+ })
+ public void shouldReturnExpectedDocumentsBasedOnSearchOperatorWithDefaultScore()
+ throws UriValidationException,
+ UriParserException,
+ XMLStreamException,
+ ExpressionVisitException,
+ ODataApplicationException,
+ InterruptedException {
+ // GIVEN
+ MongoDatabase database = mongoClient.getDatabase("testdb");
+ MongoCollection collection = database.getCollection("Items");
+ ensureSearchIndex(collection);
+
+ Edm edm = loadEmdProvider("edm/edm6_filter_main.xml");
+ UriInfo uriInfo =
+ new Parser(edm, OData.newInstance()).parseUri("examples2", "$search=search", null, null);
+ ODataSearchToMongoAtlasSearchParser tested =
+ new ODataSearchToMongoAtlasSearchParser(
+ new SearchDocumentForQueryStringFactory() {
+ @Override
+ public Bson build(
+ SearchExpression searchExpression,
+ QueryStringParsingResult queryStringParsingResult,
+ ODataSearchToMongoAtlasSearchOptions options) {
+ return new Document("index", "atlas_search_index")
+ .append(
+ "queryString",
+ new Document("query", queryStringParsingResult.getQuery())
+ .append("defaultPath", "plainString"));
+ }
+ });
+ ODataSearchToMongoAtlasSearchOptions options =
+ new DefaultODataSearchToMongoAtlasSearchOptions();
+ options.setDefaultTextScore(0.01d);
+
+ // WHEN
+ SearchOperatorResult result = tested.parse(uriInfo.getSearchOption(), options);
+ List pipeline = new ArrayList<>(result.getStageObjects());
+ System.out.println(new Document("pipeline", pipeline).toJson());
+
+ // THEN
+ List results = new ArrayList<>();
+ collection.aggregate(pipeline).into(results);
+ Assertions.assertFalse(results.isEmpty());
+ }
+
+ private void ensureSearchIndex(MongoCollection collection) {
+ try {
+ collection.createSearchIndex(
+ "atlas_search_index", new Document("mappings", new Document("dynamic", true)));
+ // Wait for index to be ready
+ while (true) {
+ boolean ready = false;
+ for (Document index : collection.listSearchIndexes()) {
+ if ("atlas_search_index".equals(index.getString("name"))
+ && "READY".equals(index.getString("status"))) {
+ ready = true;
+ break;
+ }
+ }
+ if (ready) break;
+ Thread.sleep(500);
+ }
+ } catch (Exception e) {
+ // Index might already exist
+ }
+ }
+
+ private static java.util.stream.Stream provideSearchTests() {
+ return java.util.stream.Stream.of(
+ Arguments.of("database", Set.of("database search")),
+ Arguments.of("search", Set.of("database search", "only search")),
+ Arguments.of("database AND search", Set.of("database search")),
+ Arguments.of("database OR \"only search\"", Set.of("database search", "only search")));
+ }
+}
diff --git a/compat-driver-5.x/src/test/resources/application.properties b/compat-driver-5.x/src/test/resources/application.properties
index 40b5083..d31ec51 100644
--- a/compat-driver-5.x/src/test/resources/application.properties
+++ b/compat-driver-5.x/src/test/resources/application.properties
@@ -1 +1,2 @@
-quarkus.mongodb.uuid-representation=STANDARD
\ No newline at end of file
+quarkus.mongodb.uuid-representation=STANDARD
+quarkus.mongodb.devservices.enabled=false
\ No newline at end of file
diff --git a/compat-driver-5.x/src/test/resources/bson/search/search1.json b/compat-driver-5.x/src/test/resources/bson/search/search1.json
new file mode 100644
index 0000000..a9a4b60
--- /dev/null
+++ b/compat-driver-5.x/src/test/resources/bson/search/search1.json
@@ -0,0 +1,8 @@
+{
+ "plainString": "database search",
+ "tags": [
+ "mongo",
+ "atlas"
+ ],
+ "active": true
+}
diff --git a/compat-driver-5.x/src/test/resources/bson/search/search2.json b/compat-driver-5.x/src/test/resources/bson/search/search2.json
new file mode 100644
index 0000000..acaf264
--- /dev/null
+++ b/compat-driver-5.x/src/test/resources/bson/search/search2.json
@@ -0,0 +1,8 @@
+{
+ "plainString": "only search",
+ "tags": [
+ "olingo",
+ "search"
+ ],
+ "active": true
+}
diff --git a/core/README.md b/core/README.md
index 908444f..8e6f22d 100644
--- a/core/README.md
+++ b/core/README.md
@@ -90,6 +90,52 @@ List stages = result.getStageObjects();
// e.g. collection.aggregate(stages);
```
+#### $search
+
+The `$search` operator allows clients to perform full-text search. The `core` module translates this into MongoDB Atlas Search aggregation stages.
+
+**Translation Details:**
+- Translates to a `$search` aggregation stage.
+- Optionally adds `$set` and `$match` stages to filter by search score.
+- Supports logical operators (`AND`, `OR`, `NOT`) within the search expression.
+- Allows specifying a minimum score and a custom field name for the score value.
+
+**Usage:**
+
+The `ODataSearchToMongoAtlasSearchParser` class is responsible for this translation.
+
+```java
+import com.github.starnowski.jamolingo.core.operators.search.ODataSearchToMongoAtlasSearchParser;
+import com.github.starnowski.jamolingo.core.operators.search.ODataSearchToMongoAtlasSearchOptions;
+import com.github.starnowski.jamolingo.core.operators.search.DefaultODataSearchToMongoAtlasSearchOptions;
+import com.github.starnowski.jamolingo.core.operators.search.SearchOperatorResult;
+import org.apache.olingo.server.api.uri.queryoption.SearchOption;
+// ... other imports
+
+// 1. Initialize the parser with a SearchDocumentFactory
+// SearchDocumentFactory factory = ...;
+ODataSearchToMongoAtlasSearchParser parser = new ODataSearchToMongoAtlasSearchParser(factory);
+
+// 2. Obtain the SearchOption from the Olingo UriInfo
+SearchOption searchOption = uriInfo.getSearchOption();
+
+// 3. (Optional) Provide search options (e.g., minimum score)
+ODataSearchToMongoAtlasSearchOptions options = DefaultODataSearchToMongoAtlasSearchOptions.builder()
+ .withDefaultTextScore(0.05)
+ .withScoreFieldName("search_score")
+ .build();
+
+// 4. Parse the option
+SearchOperatorResult result = parser.parse(searchOption, options);
+
+// 5. Use the result in your MongoDB aggregation pipeline
+List stages = result.getStageObjects();
+// Or get specific stages
+List searchStages = result.getSearchStages();
+List scoreFilterStages = result.getScoreFilterStages();
+// e.g. collection.aggregate(stages);
+```
+
#### $orderby
The `$orderby` operator specifies the sort order of the returned items. The `core` module translates this into a MongoDB aggregation stage.
diff --git a/core/pom.xml b/core/pom.xml
index 8e32954..f773372 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -6,7 +6,7 @@
com.github.starnowski.jamolingo
parent
- 0.7.0
+ 0.8.0-SNAPSHOT
core
diff --git a/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/DefaultODataSearchToMongoAtlasSearchOptions.java b/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/DefaultODataSearchToMongoAtlasSearchOptions.java
new file mode 100644
index 0000000..5cfa2b4
--- /dev/null
+++ b/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/DefaultODataSearchToMongoAtlasSearchOptions.java
@@ -0,0 +1,108 @@
+package com.github.starnowski.jamolingo.core.operators.search;
+
+import java.util.Objects;
+
+/** Default implementation of ODataSearchToMongoAtlasSearchOptions. */
+public class DefaultODataSearchToMongoAtlasSearchOptions
+ implements ODataSearchToMongoAtlasSearchOptions {
+
+ private Double defaultTextScore;
+ private String scoreFieldName;
+
+ @Override
+ public void setDefaultTextScore(Double defaultTextScore, String scoreFieldName) {
+ this.defaultTextScore = defaultTextScore;
+ this.scoreFieldName = scoreFieldName;
+ }
+
+ @Override
+ public Double getDefaultTextScore() {
+ return defaultTextScore;
+ }
+
+ @Override
+ public String getScoreFieldName() {
+ return scoreFieldName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ DefaultODataSearchToMongoAtlasSearchOptions that =
+ (DefaultODataSearchToMongoAtlasSearchOptions) o;
+ return Objects.equals(defaultTextScore, that.defaultTextScore)
+ && Objects.equals(scoreFieldName, that.scoreFieldName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(defaultTextScore, scoreFieldName);
+ }
+
+ @Override
+ public String toString() {
+ return "DefaultODataSearchToMongoAtlasSearchOptions{"
+ + "defaultTextScore="
+ + defaultTextScore
+ + ", scoreFieldName='"
+ + scoreFieldName
+ + '\''
+ + '}';
+ }
+
+ /**
+ * Returns a new builder for DefaultODataSearchToMongoAtlasSearchOptions.
+ *
+ * @return a new builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /** Builder for DefaultODataSearchToMongoAtlasSearchOptions. */
+ public static class Builder {
+
+ private Double defaultTextScore;
+ private String scoreFieldName =
+ ODataSearchToMongoAtlasSearchParser.SEARCH_SCORE_DEFAULT_VARIABLE;
+
+ /**
+ * Sets the minimum text score.
+ *
+ * @param defaultTextScore the default text score
+ * @return the builder instance
+ */
+ public Builder withDefaultTextScore(Double defaultTextScore) {
+ this.defaultTextScore = defaultTextScore;
+ return this;
+ }
+
+ /**
+ * Sets the score field name.
+ *
+ * @param scoreFieldName the name of the field to store the score
+ * @return the builder instance
+ */
+ public Builder withScoreFieldName(String scoreFieldName) {
+ this.scoreFieldName = scoreFieldName;
+ return this;
+ }
+
+ /**
+ * Builds a new DefaultODataSearchToMongoAtlasSearchOptions instance.
+ *
+ * @return a new instance
+ */
+ public DefaultODataSearchToMongoAtlasSearchOptions build() {
+ DefaultODataSearchToMongoAtlasSearchOptions options =
+ new DefaultODataSearchToMongoAtlasSearchOptions();
+ options.setDefaultTextScore(defaultTextScore, scoreFieldName);
+ return options;
+ }
+ }
+}
diff --git a/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/ODataSearchToMongoAtlasSearchOptions.java b/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/ODataSearchToMongoAtlasSearchOptions.java
new file mode 100644
index 0000000..c583bdc
--- /dev/null
+++ b/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/ODataSearchToMongoAtlasSearchOptions.java
@@ -0,0 +1,7 @@
+package com.github.starnowski.jamolingo.core.operators.search;
+
+/**
+ * Interface representing options specific to MongoDB Atlas Search translation. It extends {@link
+ * ODataSearchToMongoTextSearchOptions} to include general text search options.
+ */
+public interface ODataSearchToMongoAtlasSearchOptions extends ODataSearchToMongoTextSearchOptions {}
diff --git a/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/ODataSearchToMongoAtlasSearchParser.java b/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/ODataSearchToMongoAtlasSearchParser.java
new file mode 100644
index 0000000..fdd3df9
--- /dev/null
+++ b/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/ODataSearchToMongoAtlasSearchParser.java
@@ -0,0 +1,125 @@
+package com.github.starnowski.jamolingo.core.operators.search;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.apache.olingo.server.api.uri.queryoption.SearchOption;
+import org.bson.Document;
+import org.bson.conversions.Bson;
+
+/**
+ * Parser responsible for converting OData search options into MongoDB Atlas Search aggregation
+ * pipeline stages.
+ */
+public class ODataSearchToMongoAtlasSearchParser
+ implements ODataSearchToMongoTextSearchParser<
+ ODataSearchToMongoAtlasSearchOptions, SearchOperatorResultForAtlasSearch> {
+
+ /** Default variable name used to store the search score in the pipeline. */
+ public static final String SEARCH_SCORE_DEFAULT_VARIABLE = "jamolingo_search_score";
+
+ private final SearchDocumentFactory searchDocumentFactory;
+
+ /**
+ * Constructs a new ODataSearchToMongoAtlasSearchParser.
+ *
+ * @param searchDocumentFactory the factory to build search documents
+ */
+ public ODataSearchToMongoAtlasSearchParser(SearchDocumentFactory searchDocumentFactory) {
+ this.searchDocumentFactory = searchDocumentFactory;
+ }
+
+ @Override
+ public SearchOperatorResultForAtlasSearch parse(SearchOption searchOption) {
+ return parse(searchOption, null);
+ }
+
+ @Override
+ public SearchOperatorResultForAtlasSearch parse(
+ SearchOption searchOption, ODataSearchToMongoAtlasSearchOptions options) {
+ List searchStages = new ArrayList<>();
+ List scoreFilterStages = new ArrayList<>();
+ Document searchStage =
+ new Document(
+ "$search", searchDocumentFactory.build(searchOption.getSearchExpression(), options));
+ searchStages.add(searchStage);
+ if (options != null && options.getDefaultTextScore() != null) {
+ searchStage
+ .get("$search", Document.class)
+ .append("scoreDetails", true); // Optional, but can be useful
+
+ String scoreFieldName = options.getScoreFieldName();
+ scoreFilterStages.add(
+ new Document("$set", new Document(scoreFieldName, new Document("$meta", "searchScore"))));
+ scoreFilterStages.add(
+ new Document(
+ "$match",
+ new Document(scoreFieldName, new Document("$gte", options.getDefaultTextScore()))));
+ }
+ List allStages = new ArrayList<>(searchStages);
+ allStages.addAll(scoreFilterStages);
+ return new DefaultSearchOperatorResult(allStages, searchStages, scoreFilterStages, options);
+ }
+
+ private static class DefaultSearchOperatorResult implements SearchOperatorResultForAtlasSearch {
+
+ private final List stageObjects;
+ private final List searchStages;
+ private final List scoreFilterStages;
+ private final ODataSearchToMongoAtlasSearchOptions options;
+
+ private DefaultSearchOperatorResult(
+ List stages,
+ List searchStages,
+ List scoreFilterStages,
+ ODataSearchToMongoAtlasSearchOptions options) {
+ this.stageObjects = Collections.unmodifiableList(stages);
+ this.searchStages = Collections.unmodifiableList(searchStages);
+ this.scoreFilterStages = Collections.unmodifiableList(scoreFilterStages);
+ this.options = options;
+ }
+
+ @Override
+ public List getStageObjects() {
+ return stageObjects;
+ }
+
+ @Override
+ public List getSearchStages() {
+ return searchStages;
+ }
+
+ @Override
+ public List getScoreFilterStages() {
+ return scoreFilterStages;
+ }
+
+ @Override
+ public List getUsedMongoDocumentProperties() {
+ return List.of();
+ }
+
+ @Override
+ public List getWrittenMongoDocumentProperties() {
+ return List.of();
+ }
+
+ @Override
+ public List getAddedMongoDocumentProperties() {
+ if (options != null && options.getDefaultTextScore() != null) {
+ return List.of(options.getScoreFieldName());
+ }
+ return List.of();
+ }
+
+ @Override
+ public List getRemovedMongoDocumentProperties() {
+ return List.of();
+ }
+
+ @Override
+ public boolean isDocumentShapeRedefined() {
+ return false;
+ }
+ }
+}
diff --git a/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/ODataSearchToMongoTextSearchOptions.java b/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/ODataSearchToMongoTextSearchOptions.java
new file mode 100644
index 0000000..54ece8e
--- /dev/null
+++ b/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/ODataSearchToMongoTextSearchOptions.java
@@ -0,0 +1,37 @@
+package com.github.starnowski.jamolingo.core.operators.search;
+
+import static com.github.starnowski.jamolingo.core.operators.search.ODataSearchToMongoAtlasSearchParser.SEARCH_SCORE_DEFAULT_VARIABLE;
+
+public interface ODataSearchToMongoTextSearchOptions {
+
+ /**
+ * Sets the minimum text score required for a document to be returned.
+ *
+ * @param defaultTextScore the minimum text score
+ */
+ default void setDefaultTextScore(Double defaultTextScore) {
+ setDefaultTextScore(defaultTextScore, SEARCH_SCORE_DEFAULT_VARIABLE);
+ }
+
+ /**
+ * Sets the minimum text score and the field name for the score value.
+ *
+ * @param defaultTextScore the minimum text score
+ * @param scoreFieldName the name of the field that will store the score value
+ */
+ void setDefaultTextScore(Double defaultTextScore, String scoreFieldName);
+
+ /**
+ * Returns the minimum text score required for a document to be returned.
+ *
+ * @return the minimum text score, or null if not set
+ */
+ Double getDefaultTextScore();
+
+ /**
+ * Returns the name of the field that will store the text search score value.
+ *
+ * @return the name of the field
+ */
+ String getScoreFieldName();
+}
diff --git a/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/ODataSearchToMongoTextSearchParser.java b/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/ODataSearchToMongoTextSearchParser.java
new file mode 100644
index 0000000..92c3512
--- /dev/null
+++ b/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/ODataSearchToMongoTextSearchParser.java
@@ -0,0 +1,31 @@
+package com.github.starnowski.jamolingo.core.operators.search;
+
+import org.apache.olingo.server.api.uri.queryoption.SearchOption;
+
+/**
+ * Interface for parsing OData search options into MongoDB text search aggregation pipeline stages.
+ *
+ * @param the type of search options
+ * @param the type of search operator result
+ */
+public interface ODataSearchToMongoTextSearchParser<
+ T extends ODataSearchToMongoTextSearchOptions, R extends SearchOperatorResult> {
+
+ /**
+ * Parses the given OData search option into a search operator result.
+ *
+ * @param searchOption the OData search option to parse
+ * @return the resulting search operator result
+ */
+ R parse(SearchOption searchOption);
+
+ /**
+ * Parses the given OData search option into a search operator result, applying the provided
+ * options.
+ *
+ * @param searchOption the OData search option to parse
+ * @param options the search options to apply
+ * @return the resulting search operator result
+ */
+ R parse(SearchOption searchOption, T options);
+}
diff --git a/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/SearchDocumentFactory.java b/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/SearchDocumentFactory.java
new file mode 100644
index 0000000..b7e3560
--- /dev/null
+++ b/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/SearchDocumentFactory.java
@@ -0,0 +1,18 @@
+package com.github.starnowski.jamolingo.core.operators.search;
+
+import org.apache.olingo.server.api.uri.queryoption.search.SearchExpression;
+import org.bson.conversions.Bson;
+
+/** Factory interface for building MongoDB Bson documents from OData search expressions. */
+public interface SearchDocumentFactory {
+
+ /**
+ * Builds a Bson document representing the search stage based on the provided search expression
+ * and options.
+ *
+ * @param searchExpression the OData search expression
+ * @param options the search options
+ * @return the Bson document representing the search operation
+ */
+ Bson build(SearchExpression searchExpression, ODataSearchToMongoAtlasSearchOptions options);
+}
diff --git a/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/SearchDocumentForQueryStringFactory.java b/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/SearchDocumentForQueryStringFactory.java
new file mode 100644
index 0000000..89de883
--- /dev/null
+++ b/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/SearchDocumentForQueryStringFactory.java
@@ -0,0 +1,139 @@
+package com.github.starnowski.jamolingo.core.operators.search;
+
+import org.apache.olingo.server.api.uri.queryoption.search.SearchBinary;
+import org.apache.olingo.server.api.uri.queryoption.search.SearchBinaryOperatorKind;
+import org.apache.olingo.server.api.uri.queryoption.search.SearchExpression;
+import org.apache.olingo.server.api.uri.queryoption.search.SearchTerm;
+import org.apache.olingo.server.api.uri.queryoption.search.SearchUnary;
+import org.apache.olingo.server.api.uri.queryoption.search.SearchUnaryOperatorKind;
+import org.bson.conversions.Bson;
+
+/**
+ * Abstract factory class for building search documents based on query strings. It parses the OData
+ * search expression into a query string result, which is then used by concrete implementations.
+ */
+public abstract class SearchDocumentForQueryStringFactory implements SearchDocumentFactory {
+ @Override
+ public Bson build(
+ SearchExpression searchExpression, ODataSearchToMongoAtlasSearchOptions options) {
+ return build(searchExpression, pares(searchExpression), options);
+ }
+
+ private QueryStringParsingResult pares(SearchExpression searchExpression) {
+ try {
+ return new QueryStringParsingResult(
+ parseSearchExpressionToString(searchExpression), true, null);
+ } catch (Exception ex) {
+ return new QueryStringParsingResult(null, false, ex);
+ }
+ }
+
+ /**
+ * Parses the search expression and converts it to a formatted string.
+ *
+ * @param searchExpression the OData search expression
+ * @return the formatted string representing the query
+ */
+ protected String parseSearchExpressionToString(SearchExpression searchExpression) {
+ if (searchExpression instanceof SearchTerm) {
+ return formatTerm(((SearchTerm) searchExpression).getSearchTerm());
+ } else if (searchExpression instanceof SearchBinary) {
+ SearchBinary binary = (SearchBinary) searchExpression;
+ String left = parseSearchExpressionToString(binary.getLeftOperand());
+ String right = parseSearchExpressionToString(binary.getRightOperand());
+
+ if (binary.getLeftOperand() instanceof SearchBinary) {
+ left = "(" + left + ")";
+ }
+
+ if (binary.getRightOperand() instanceof SearchBinary) {
+ right = "(" + right + ")";
+ }
+
+ if (binary.getOperator() == SearchBinaryOperatorKind.AND
+ && binary.getRightOperand() instanceof SearchUnary) {
+ if (((SearchUnary) binary.getRightOperand()).getOperator() == SearchUnaryOperatorKind.NOT) {
+ return left + " " + right;
+ }
+ }
+
+ return left + " " + binary.getOperator().toString() + " " + right;
+ } else if (searchExpression instanceof SearchUnary) {
+ SearchUnary unary = (SearchUnary) searchExpression;
+ String operand = parseSearchExpressionToString(unary.getOperand());
+ if (unary.getOperand() instanceof SearchBinary) {
+ operand = "(" + operand + ")";
+ }
+ return "NOT " + operand;
+ }
+ return "";
+ }
+
+ private String formatTerm(String term) {
+ if (term.contains(" ") || term.equals("AND") || term.equals("OR") || term.equals("NOT")) {
+ return "\"" + term.replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
+ }
+ return term;
+ }
+
+ /**
+ * Builds the MongoDB Bson document using the parsed query string result and options.
+ *
+ * @param searchExpression the original search expression
+ * @param queryStringParsingResult the result of parsing the query string
+ * @param options the search options
+ * @return the Bson document representing the search stage
+ */
+ public abstract Bson build(
+ SearchExpression searchExpression,
+ QueryStringParsingResult queryStringParsingResult,
+ ODataSearchToMongoAtlasSearchOptions options);
+
+ /** Result of parsing the query string, including the query itself and status. */
+ public static class QueryStringParsingResult {
+
+ private final String query;
+ private final boolean success;
+ private final Exception cause;
+
+ /**
+ * Gets the parsed query string.
+ *
+ * @return the query string
+ */
+ public String getQuery() {
+ return query;
+ }
+
+ /**
+ * Checks if the parsing was successful.
+ *
+ * @return true if successful, false otherwise
+ */
+ public boolean isSuccess() {
+ return success;
+ }
+
+ /**
+ * Gets the exception that caused the parsing to fail, if any.
+ *
+ * @return the cause exception
+ */
+ public Exception getCause() {
+ return cause;
+ }
+
+ /**
+ * Constructs a new QueryStringParsingResult.
+ *
+ * @param query the parsed query string
+ * @param success the success status
+ * @param cause the exception cause
+ */
+ public QueryStringParsingResult(String query, boolean success, Exception cause) {
+ this.query = query;
+ this.success = success;
+ this.cause = cause;
+ }
+ }
+}
diff --git a/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/SearchOperatorResult.java b/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/SearchOperatorResult.java
new file mode 100644
index 0000000..5c098ad
--- /dev/null
+++ b/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/SearchOperatorResult.java
@@ -0,0 +1,22 @@
+package com.github.starnowski.jamolingo.core.operators.search;
+
+import com.github.starnowski.jamolingo.core.operators.OlingoOperatorResult;
+import java.util.List;
+import org.bson.conversions.Bson;
+
+public interface SearchOperatorResult extends OlingoOperatorResult {
+
+ /**
+ * MongoDB aggregation pipeline stages related to search operations.
+ *
+ * @return list of Bson objects representing the stages
+ */
+ List getSearchStages();
+
+ /**
+ * MongoDB aggregation pipeline stages that filter documents based on the score value.
+ *
+ * @return list of Bson objects representing the stages
+ */
+ List getScoreFilterStages();
+}
diff --git a/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/SearchOperatorResultForAtlasSearch.java b/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/SearchOperatorResultForAtlasSearch.java
new file mode 100644
index 0000000..110eb6c
--- /dev/null
+++ b/core/src/main/java/com/github/starnowski/jamolingo/core/operators/search/SearchOperatorResultForAtlasSearch.java
@@ -0,0 +1,6 @@
+package com.github.starnowski.jamolingo.core.operators.search;
+
+/**
+ * Represents the result of parsing an OData search option specifically for MongoDB Atlas Search.
+ */
+public interface SearchOperatorResultForAtlasSearch extends SearchOperatorResult {}
diff --git a/core/src/test/groovy/com/github/starnowski/jamolingo/core/operators/search/ODataSearchToMongoAtlasSearchParserTest.groovy b/core/src/test/groovy/com/github/starnowski/jamolingo/core/operators/search/ODataSearchToMongoAtlasSearchParserTest.groovy
new file mode 100644
index 0000000..368901a
--- /dev/null
+++ b/core/src/test/groovy/com/github/starnowski/jamolingo/core/operators/search/ODataSearchToMongoAtlasSearchParserTest.groovy
@@ -0,0 +1,165 @@
+package com.github.starnowski.jamolingo.core.operators.search
+
+import com.github.starnowski.jamolingo.core.AbstractSpecification
+import com.mongodb.MongoClientSettings
+import org.apache.olingo.commons.api.edm.Edm
+import org.apache.olingo.server.api.OData
+import org.apache.olingo.server.api.uri.UriInfo
+import org.apache.olingo.server.api.uri.queryoption.search.SearchExpression
+import org.apache.olingo.server.core.uri.parser.Parser
+import org.bson.Document
+import org.bson.UuidRepresentation
+import org.bson.codecs.DocumentCodec
+import org.bson.codecs.UuidCodecProvider
+import org.bson.codecs.configuration.CodecRegistries
+import org.bson.codecs.configuration.CodecRegistry
+import org.bson.conversions.Bson
+import org.bson.json.JsonWriterSettings
+import spock.lang.Unroll
+
+class ODataSearchToMongoAtlasSearchParserTest extends AbstractSpecification {
+
+
+ def "should return separate search and score filter stages"(){
+ given:
+ Edm edm = loadEmdProvider("edm/edm6_filter_main.xml")
+ JsonWriterSettings settings = JsonWriterSettings.builder().build()
+ CodecRegistry registry = CodecRegistries.fromRegistries(
+ CodecRegistries.fromProviders(new UuidCodecProvider(UuidRepresentation.STANDARD)),
+ MongoClientSettings.getDefaultCodecRegistry()
+ )
+ DocumentCodec codec = new DocumentCodec(registry)
+
+ UriInfo uriInfo = new Parser(edm, OData.newInstance())
+ .parseUri("examples2",
+ "\$search=database"
+ , null, null)
+ ODataSearchToMongoAtlasSearchParser tested = new ODataSearchToMongoAtlasSearchParser(new SearchDocumentForQueryStringFactory() {
+ @Override
+ Bson build(SearchExpression searchExpression, SearchDocumentForQueryStringFactory.QueryStringParsingResult queryStringParsingResult, ODataSearchToMongoAtlasSearchOptions options) {
+ return new Document().append("index", "default")
+ .append("queryString", new Document()
+ .append("query", queryStringParsingResult.getQuery())
+ .append("path", Arrays.asList("name","description"))
+ )
+ }
+ })
+ ODataSearchToMongoAtlasSearchOptions options = DefaultODataSearchToMongoAtlasSearchOptions.builder().withDefaultTextScore(0.5d).build()
+
+ when:
+ def result = tested.parse(uriInfo.getSearchOption(), options)
+
+ then:
+ result.getSearchStages().size() == 1
+ ((Document)result.getSearchStages().get(0)).get("\$search") != null
+ ((Document)result.getSearchStages().get(0)).get("\$search", Document.class).get("scoreDetails") == true
+ result.getScoreFilterStages().size() == 2
+ ((Document)result.getScoreFilterStages().get(0)).get("\$set") != null
+ ((Document)result.getScoreFilterStages().get(0)).get("\$set", Document.class).get("jamolingo_search_score", Document.class).get("\$meta") == "searchScore"
+ ((Document)result.getScoreFilterStages().get(1)).get("\$match") != null
+ ((Document)result.getScoreFilterStages().get(1)).get("\$match", Document.class).get("jamolingo_search_score", Document.class).get("\$gte") == 0.5d
+ result.getStageObjects().size() == 3
+ result.getStageObjects().get(0) == result.getSearchStages().get(0)
+ result.getStageObjects().get(1) == result.getScoreFilterStages().get(0)
+ result.getStageObjects().get(2) == result.getScoreFilterStages().get(1)
+ result.getAddedMongoDocumentProperties() == ["jamolingo_search_score"]
+ }
+
+ def "should return separate search and score filter stages with custom score field name"(){
+ given:
+ Edm edm = loadEmdProvider("edm/edm6_filter_main.xml")
+ String customScoreField = "my_custom_score"
+
+ UriInfo uriInfo = new Parser(edm, OData.newInstance())
+ .parseUri("examples2",
+ "\$search=database"
+ , null, null)
+ ODataSearchToMongoAtlasSearchParser tested = new ODataSearchToMongoAtlasSearchParser(new SearchDocumentForQueryStringFactory() {
+ @Override
+ Bson build(SearchExpression searchExpression, SearchDocumentForQueryStringFactory.QueryStringParsingResult queryStringParsingResult, ODataSearchToMongoAtlasSearchOptions options) {
+ return new Document().append("index", "default")
+ .append("queryString", new Document()
+ .append("query", queryStringParsingResult.getQuery())
+ .append("path", Arrays.asList("name","description"))
+ )
+ }
+ })
+ ODataSearchToMongoAtlasSearchOptions options = DefaultODataSearchToMongoAtlasSearchOptions.builder()
+ .withDefaultTextScore(0.5d)
+ .withScoreFieldName(customScoreField)
+ .build()
+
+ when:
+ def result = tested.parse(uriInfo.getSearchOption(), options)
+
+ then:
+ result.getSearchStages().size() == 1
+ result.getScoreFilterStages().size() == 2
+ ((Document)result.getScoreFilterStages().get(0)).get("\$set", Document.class).get(customScoreField) != null
+ ((Document)result.getScoreFilterStages().get(1)).get("\$match", Document.class).get(customScoreField) != null
+ result.getAddedMongoDocumentProperties() == [customScoreField]
+ }
+
+ /**
+ * Verifies that the generated MongoDB $match stage matches the expected BSON document.
+ */
+ @Unroll
+ def "should return expected stage bson objects"(){
+ given:
+ System.out.println("Testing search: " + searchValue)
+ Bson expectedBson = Document.parse(expectedBsonJson)
+ Edm edm = loadEmdProvider("edm/edm6_filter_main.xml")
+ JsonWriterSettings settings = JsonWriterSettings.builder().build()
+ CodecRegistry registry = CodecRegistries.fromRegistries(
+ CodecRegistries.fromProviders(new UuidCodecProvider(UuidRepresentation.STANDARD)),
+ MongoClientSettings.getDefaultCodecRegistry()
+ )
+ DocumentCodec codec = new DocumentCodec(registry)
+
+ UriInfo uriInfo = new Parser(edm, OData.newInstance())
+ .parseUri("examples2",
+ "\$search=" +searchValue
+ , null, null)
+ ODataSearchToMongoAtlasSearchParser tested = new ODataSearchToMongoAtlasSearchParser(new SearchDocumentForQueryStringFactory() {
+ @Override
+ Bson build(SearchExpression searchExpression, SearchDocumentForQueryStringFactory.QueryStringParsingResult queryStringParsingResult, ODataSearchToMongoAtlasSearchOptions options) {
+ return new Document().append("index", "default")
+ .append("queryString", new Document()
+ .append("query", queryStringParsingResult.getQuery())
+ .append("path", Arrays.asList("name","description"))
+ )
+ }
+ })
+
+ when:
+ def result = tested.parse(uriInfo.getSearchOption())
+
+ then:
+ [((Document)result.getStageObjects().get(0)).toJson(settings, codec)] == [expectedBson.toJson(settings, codec)]
+
+ where:
+ searchValue || expectedBsonJson
+ """database AND search""" || """{ "\$search": { "index": "default", "queryString": { "query": "database AND search", "path": ["name","description"] }}}"""
+ """database OR search""" || """{ "\$search": { "index": "default", "queryString": { "query": "database OR search", "path": ["name","description"] }}}"""
+ """database NOT legacy""" || """{ "\$search": { "index": "default", "queryString": { "query": "database NOT legacy", "path": ["name","description"] }}}"""
+ """\"AND\"""" || """{ "\$search": { "index": "default", "queryString": { "query": "\\"AND\\"", "path": ["name","description"] }}}"""
+ """\"OR\"""" || """{ "\$search": { "index": "default", "queryString": { "query": "\\"OR\\"", "path": ["name","description"] }}}"""
+ """\"NOT\"""" || """{ "\$search": { "index": "default", "queryString": { "query": "\\"NOT\\"", "path": ["name","description"] }}}"""
+ """\"AND operator\"""" || """{ "\$search": { "index": "default", "queryString": { "query": "\\"AND operator\\"", "path": ["name","description"] }}}"""
+ """\"rock AND roll\"""" || """{ "\$search": { "index": "default", "queryString": { "query": "\\"rock AND roll\\"", "path": ["name","description"] }}}"""
+ """"OR condition" AND database""" || """{ "\$search": { "index": "default", "queryString": { "query": "\\"OR condition\\" AND database", "path": ["name","description"] }}}"""
+ """"NOT operator" OR logic""" || """{ "\$search": { "index": "default", "queryString": { "query": "\\"NOT operator\\" OR logic", "path": ["name","description"] }}}"""
+ """(database OR search) AND index""" || """{ "\$search": { "index": "default", "queryString": { "query": "(database OR search) AND index", "path": ["name","description"] }}}"""
+ """("AND" OR "OR") AND logic""" || """{ "\$search": { "index": "default", "queryString": { "query": "(\\"AND\\" OR \\"OR\\") AND logic", "path": ["name","description"] }}}"""
+ """(database OR "AND") AND system""" || """{ "\$search": { "index": "default", "queryString": { "query": "(database OR \\"AND\\") AND system", "path": ["name","description"] }}}"""
+ """\"database OR search\"""" || """{ "\$search": { "index": "default", "queryString": { "query": "\\"database OR search\\"", "path": ["name","description"] }}}"""
+ """\"AND OR NOT\"""" || """{ "\$search": { "index": "default", "queryString": { "query": "\\"AND OR NOT\\"", "path": ["name","description"] }}}"""
+ """database AND ("OR" OR "NOT")""" || """{ "\$search": { "index": "default", "queryString": { "query": "database AND (\\"OR\\" OR \\"NOT\\")", "path": ["name","description"] }}}"""
+ """\"logical AND operator\"""" || """{ "\$search": { "index": "default", "queryString": { "query": "\\"logical AND operator\\"", "path": ["name","description"] }}}"""
+ """("database search" OR "full text") AND engine""" || """{ "\$search": { "index": "default", "queryString": { "query": "(\\"database search\\" OR \\"full text\\") AND engine", "path": ["name","description"] }}}"""
+ """\"\\"AND\\" keyword\"""" || """{ "\$search": { "index": "default", "queryString": { "query": "\\"\\\\\\"AND\\\\\\" keyword\\"", "path": ["name","description"] }}}"""
+ """(database AND search) OR ("AND operator" AND logic)""" || """{ "\$search": { "index": "default", "queryString": { "query": "(database AND search) OR (\\"AND operator\\" AND logic)", "path": ["name","description"] }}}"""
+ }
+
+
+}
diff --git a/demos/pom.xml b/demos/pom.xml
index 44970f8..c15084a 100644
--- a/demos/pom.xml
+++ b/demos/pom.xml
@@ -6,7 +6,7 @@
com.github.starnowski.jamolingo
parent
- 0.7.0
+ 0.8.0-SNAPSHOT
demos
diff --git a/demos/quarkus-webapp/pom.xml b/demos/quarkus-webapp/pom.xml
index fd6f699..44f23b9 100644
--- a/demos/quarkus-webapp/pom.xml
+++ b/demos/quarkus-webapp/pom.xml
@@ -6,7 +6,7 @@
com.github.starnowski.jamolingo
demos
- 0.7.0
+ 0.8.0-SNAPSHOT
quarkus-webapp
diff --git a/demos/spring-boot-webapp/pom.xml b/demos/spring-boot-webapp/pom.xml
index d038314..4e51e29 100644
--- a/demos/spring-boot-webapp/pom.xml
+++ b/demos/spring-boot-webapp/pom.xml
@@ -6,7 +6,7 @@
com.github.starnowski.jamolingo
demos
- 0.7.0
+ 0.8.0-SNAPSHOT
spring-boot-webapp
diff --git a/junit5-mongo-extension-parent/junit5-mongo-extension-quarkus/pom.xml b/junit5-mongo-extension-parent/junit5-mongo-extension-quarkus/pom.xml
index 148d071..f6915c8 100644
--- a/junit5-mongo-extension-parent/junit5-mongo-extension-quarkus/pom.xml
+++ b/junit5-mongo-extension-parent/junit5-mongo-extension-quarkus/pom.xml
@@ -6,7 +6,7 @@
com.github.starnowski.jamolingo
junit5-mongo-extension-parent
- 0.7.0
+ 0.8.0-SNAPSHOT
junit5-mongo-extension-quarkus
diff --git a/junit5-mongo-extension-parent/junit5-mongo-extension-spring/pom.xml b/junit5-mongo-extension-parent/junit5-mongo-extension-spring/pom.xml
index 04b89ed..c51eb21 100644
--- a/junit5-mongo-extension-parent/junit5-mongo-extension-spring/pom.xml
+++ b/junit5-mongo-extension-parent/junit5-mongo-extension-spring/pom.xml
@@ -6,7 +6,7 @@
com.github.starnowski.jamolingo
junit5-mongo-extension-parent
- 0.7.0
+ 0.8.0-SNAPSHOT
junit5-mongo-extension-spring
diff --git a/junit5-mongo-extension-parent/junit5-mongo-extension/pom.xml b/junit5-mongo-extension-parent/junit5-mongo-extension/pom.xml
index f13ca63..4330470 100644
--- a/junit5-mongo-extension-parent/junit5-mongo-extension/pom.xml
+++ b/junit5-mongo-extension-parent/junit5-mongo-extension/pom.xml
@@ -6,7 +6,7 @@
com.github.starnowski.jamolingo
junit5-mongo-extension-parent
- 0.7.0
+ 0.8.0-SNAPSHOT
junit5-mongo-extension
diff --git a/junit5-mongo-extension-parent/pom.xml b/junit5-mongo-extension-parent/pom.xml
index 7d55f50..be24f4d 100644
--- a/junit5-mongo-extension-parent/pom.xml
+++ b/junit5-mongo-extension-parent/pom.xml
@@ -6,7 +6,7 @@
com.github.starnowski.jamolingo
parent
- 0.7.0
+ 0.8.0-SNAPSHOT
pom
diff --git a/perf/pom.xml b/perf/pom.xml
index 3e2fb15..229b96b 100644
--- a/perf/pom.xml
+++ b/perf/pom.xml
@@ -6,7 +6,7 @@
com.github.starnowski.jamolingo
parent
- 0.7.0
+ 0.8.0-SNAPSHOT
perf
diff --git a/pom.xml b/pom.xml
index b8a247f..f38ccaf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.github.starnowski.jamolingo
parent
- 0.7.0
+ 0.8.0-SNAPSHOT
pom
${project.groupId}:${project.artifactId}