Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring QueryStringSearchOperatorDsl #58

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
93ae590
refactor: remove expression variable in function score
jbl428 Oct 1, 2023
d531527
feat: implement lookup stage dsl
jbl428 Oct 1, 2023
1784c2d
test: add test case for lookup stage
jbl428 Oct 1, 2023
5918354
test: add lookup stage examples
jbl428 Oct 1, 2023
3015fc9
feat: implement unset stage
jbl428 Oct 1, 2023
b43b200
test: add unset stage examples
jbl428 Oct 1, 2023
10c0174
feat: implement unionWith stage dsl
jbl428 Oct 1, 2023
b0e23e3
test: add unionWith stage examples
jbl428 Oct 1, 2023
6efad4c
chore: add maven publish plugin
jbl428 Oct 2, 2023
a0bdcbe
chore: move maven publish plugin to root
jbl428 Oct 2, 2023
fed35b8
chore: add jdk option for jitpack
jbl428 Oct 2, 2023
8bef6b1
chore: apply jitpack to core module only
jbl428 Oct 2, 2023
4e9e28a
chore: add java publish setting
jbl428 Oct 2, 2023
95cf29c
feat: add near search operator
username1103 Oct 2, 2023
b0cfa3a
feat: add near search operator example
username1103 Oct 2, 2023
0e5857b
feat: add NearSearchRepository example
username1103 Oct 2, 2023
4d695c2
chore: fix comment in NearOperatorDsl
username1103 Oct 3, 2023
f5944a2
fix: typo
username1103 Oct 3, 2023
0c18d4d
chore: add link in comment
username1103 Oct 3, 2023
062cde6
refactor: use aggregate extension function
username1103 Oct 3, 2023
a7f5aee
refactor: use findAnnotation extension function
username1103 Oct 3, 2023
88e4641
docs: update README.md
jbl428 Oct 3, 2023
702e137
docs: add key feature
jbl428 Oct 3, 2023
f660504
refactor: alter return query in QueryStringQueryOptionDsl
masonJS Oct 3, 2023
d086f86
style: alter double quotes to round brackets
masonJS Oct 3, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 144 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,145 @@
# spring-data-mongodb-kotlin-dsl

This project is a Kotlin library that provides a type-safe DSL for aggregations in Spring Data MongoDB.

## Key Features

- Type-safe DSL for building pipeline of [aggregation operations](https://www.mongodb.com/docs/manual/aggregation/)
- Support for [Atlas Search](https://docs.atlas.mongodb.com/atlas-search)
- Provide Kotlin Doc (KDoc) for all DSL functions and properties

## Requirements

- Spring Data MongoDB 3.4.0 or higher
- Java 8 or higher
- Kotlin 1.8 or higher

## Installation

> [!NOTE]
> We do not have a publishing mechanism to a maven repository so the easiest way to add the library to your app is via a JitPack Dependency.

[![](https://jitpack.io/v/inflearn/spring-data-mongodb-kotlin-dsl.svg)](https://jitpack.io/#inflearn/spring-data-mongodb-kotlin-dsl)

- build.gradle.kts

```kotlin
repositories {
maven { setUrl("https://jitpack.io") }
}

dependencies {
implementation("com.github.inflearn:spring-data-mongodb-kotlin-dsl:$version")
}
```

## Examples

This project provides `example` module that contains examples of how to use the library.
The codes used in the example are all DSL representations of the examples from the official MongoDB documentation.
Each aggregation operation is implemented as a separate repository class with `MongoTemplate` as a dependency.

For example, There is an example of `text` operation from `$search` stage
in [TextSearchRepository.kt](example/spring-data-mongodb/src/main/kotlin/com/github/inflab/example/spring/data/mongodb/repository/atlas/TextSearchRepository.kt)

```kotlin
@Repository
class TextSearchRepository(
private val mongoTemplate: MongoTemplate,
) {

data class TitleAndScoreDto(
val title: String,
val score: Double,
)

/**
* @see <a href="https://www.mongodb.com/docs/atlas/atlas-search/text/#basic-example">Basic Example</a>
*/
fun findTitleWithSufer(): AggregationResults<TitleAndScoreDto> {
val aggregation = aggregation {
search {
text {
path(Movies::title)
query("surfer")
}
}

project {
excludeId()
+Movies::title
searchScore()
}
}

return mongoTemplate.aggregate<Movies, TitleAndScoreDto>(aggregation)
}
}
```

It is equivalent to the following MongoDB query
from [the official documentation](https://www.mongodb.com/docs/atlas/atlas-search/text/#basic-example).

```javascript
db.movies.aggregate([
{
$search: {
"text": {
"path": "title",
"query": "surfer"
}
}
},
{
$project: {
"_id": 0,
"title": 1,
score: {$meta: "searchScore"}
}
}
])
```

### Running tests from examples

Each example repository contains a test code that verifies that the query is executed correctly.
There are two types of tests: one that requires a MongoDB Community instance and another that requires MongoDB Atlas.

- MongoDB Community

The repositories that are not under the `com.github.inflab.example.spring.data.mongodb.repository.atlas` package requires a MongoDB Community instance.
To run the MongoDB Community instance, we use [Testcontainers](https://www.testcontainers.org/).
Therefore, you need to install and run Docker on your machine.

- MongoDB Atlas

The repositories that are under the `com.github.inflab.example.spring.data.mongodb.repository.atlas` package requires a MongoDB Atlas instance.
You need to create a MongoDB Atlas instance and set the connection information in the `application.yml` file.

> [!NOTE]
> you can copy a sample `application.yml` file
> from [application.sample.yml](example/spring-data-mongodb/src/test/resources/application.sample.yml)

```yaml
spring:
data:
mongodb:
username: "username"
password: "password"
host: "host"
```

You should refer to [the following manual](https://www.mongodb.com/docs/atlas/sample-data/) to configure sample data as well.
Because most example codes are provided based on the sample data.
If test codes are using `Atlas Search`, you also need to create a suitable search index.
Please refer to each example documentation for more information.

## Contributors

<a href="https://github.com/inflearn/spring-data-mongodb-kotlin-dsl/graphs/contributors">
<img src="https://contrib.rocks/image?repo=inflearn/spring-data-mongodb-kotlin-dsl" />
</a>

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
9 changes: 9 additions & 0 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
plugins {
`java-library`
`maven-publish`
}

dependencies {
Expand Down Expand Up @@ -30,3 +31,11 @@ kotlin {
languageVersion.set(JavaLanguageVersion.of(8))
}
}

publishing {
publications {
create<MavenPublication>("maven") {
from(components["java"])
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.inflab.spring.data.mongodb.core.aggregation

import com.github.inflab.spring.data.mongodb.core.aggregation.search.PathSearchOptionDsl
import com.github.inflab.spring.data.mongodb.core.aggregation.search.SearchMetaStageDsl
import com.github.inflab.spring.data.mongodb.core.aggregation.search.SearchStageDsl
import com.github.inflab.spring.data.mongodb.core.annotation.AggregationMarker
Expand All @@ -10,6 +11,7 @@ import org.springframework.data.mongodb.core.aggregation.AggregationExpression
import org.springframework.data.mongodb.core.aggregation.AggregationOperation
import org.springframework.data.mongodb.core.aggregation.AggregationOptions
import org.springframework.data.mongodb.core.aggregation.AggregationOptions.DomainTypeMapping
import org.springframework.data.mongodb.core.aggregation.UnsetOperation
import org.springframework.data.mongodb.core.query.Collation
import org.springframework.data.mongodb.core.query.Criteria
import org.springframework.data.mongodb.core.query.CriteriaDefinition
Expand Down Expand Up @@ -217,6 +219,43 @@ class AggregationDsl {
UnwindStageDsl().apply(unwindConfiguration).build()?.let { operations += it }
}

/**
* Configures a stage that performs a left outer join to a collection in the same database to filter in documents from the "joined" collection for processing.
* The `$lookup` stage adds a new array field to each input document.
* The new array field contains the matching documents from the "joined" collection.
* The `$lookup` stage passes these reshaped documents to the next stage.
*
* @param lookupConfiguration The configuration block for the [LookupStageDsl].
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/lookup">$lookup (aggregation)</a>
*/
fun lookup(lookupConfiguration: LookupStageDsl.() -> Unit) {
operations += LookupStageDsl().apply(lookupConfiguration).get()
}

/**
* Configures a stage that removes/excludes fields from documents.
*
* @param pathConfiguration The configuration block for the [PathSearchOptionDsl].
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/unset">$unset (aggregation)</a>
*/
fun unset(pathConfiguration: PathSearchOptionDsl<Any>.() -> Unit) {
operations += UnsetOperation.unset(*PathSearchOptionDsl<Any>().apply(pathConfiguration).get().toTypedArray())
}

/**
* Configures a stage that performs a union of two collections.
* `$unionWith` combines pipeline results from two collections into a single result set.
* The stage outputs the combined result set (including duplicates) to the next stage.
*
* The order in which the combined result set documents are output is unspecified.
*
* @param unionWithConfiguration The configuration block for the [UnionWithStageDsl].
* @see <a href="https://docs.mongodb.com/manual/reference/operator/aggregation/unionWith/">$unionWith (aggregation)</a>
*/
fun unionWith(unionWithConfiguration: UnionWithStageDsl.() -> Unit) {
UnionWithStageDsl().apply(unionWithConfiguration).build()?.let { operations += it }
}

/**
* Builds the [Aggregation] using the configured [AggregationOperation]s.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.github.inflab.spring.data.mongodb.core.aggregation

import org.bson.Document
import org.springframework.data.mongodb.core.aggregation.AggregationOperation
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext
import org.springframework.data.mongodb.core.aggregation.AggregationPipeline
import org.springframework.data.mongodb.core.aggregation.VariableOperators

class ExtendedLookupOperation : AggregationOperation {
private val document: Document = Document()
private var let: VariableOperators.Let? = null
private var pipeline: AggregationPipeline? = null

fun from(from: String?) {
document["from"] = from
}

fun localField(localField: String) {
document["localField"] = localField
}

fun foreignField(foreignField: String) {
document["foreignField"] = foreignField
}

fun pipeline(pipeline: AggregationPipeline) {
this.pipeline = pipeline
}

fun let(let: VariableOperators.Let) {
this.let = let
}

fun `as`(`as`: String) {
document["as"] = `as`
}

@Deprecated("Deprecated in Java")
override fun toDocument(context: AggregationOperationContext): Document {
let?.let {
document["let"] = it.toDocument(context).get("\$let", Document::class.java)["vars"]
}
pipeline?.let {
document["pipeline"] = it.operations.flatMap { operation -> operation.toPipelineStages(context) }
}

return Document(operator, document)
}

override fun getOperator(): String = "\$lookup"
}
Loading
Loading