Skip to content

Commit 3d80e7e

Browse files
committed
Documentation polishing
1 parent b2fb0a1 commit 3d80e7e

File tree

2 files changed

+77
-33
lines changed

2 files changed

+77
-33
lines changed

src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/GroupingCriteriaCollector.kt

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ typealias GroupingCriteriaReceiver = GroupingCriteriaCollector.() -> Unit
3333
* an initial criterion, and sub-criteria connected by either an "and" or an "or".
3434
*
3535
* An initial criterion can be one of four types:
36-
* - A column and condition (called with the invoke operator on a column)
36+
* - A column and condition (called with the invoke operator on a column, or an infix function)
3737
* - An exists operator (called with the "exists" function)
38-
* - A criteria group which is essentially parenthesis within the where clause (called with the group function)
39-
* - A "not" group (called with the not function)
38+
* - A criteria group which is essentially parenthesis within the where clause (called with the "group" function)
39+
* - A criteria group preceded with "not" (called with the "not" function)
4040
*
41-
* Only one of these initial criterion functions should be called within each scope. If you need more than one,
42-
* use a sub-criteria joined with "and" or "or"
41+
* Only one of the initial criterion functions should be called within each scope. If you need more than one,
42+
* use a sub-criterion joined with "and" or "or"
4343
*/
4444
@Suppress("TooManyFunctions")
4545
@MyBatisDslMarker
@@ -54,6 +54,14 @@ class GroupingCriteriaCollector {
5454

5555
internal val subCriteria = mutableListOf<AndOrCriteriaGroup>()
5656

57+
/**
58+
* Add sub criterion joined with "and" to the current context. If the receiver adds more than one
59+
* criterion that renders at runtime then parenthesis will be added.
60+
*
61+
* This function may be called multiple times in a context.
62+
*
63+
* @param criteriaReceiver a function to create the contained criteria
64+
*/
5765
fun and(criteriaReceiver: GroupingCriteriaReceiver): Unit =
5866
with(GroupingCriteriaCollector().apply(criteriaReceiver)) {
5967
this@GroupingCriteriaCollector.subCriteria.add(
@@ -64,6 +72,14 @@ class GroupingCriteriaCollector {
6472
)
6573
}
6674

75+
/**
76+
* Add sub criterion joined with "or" to the current context. If the receiver adds more than one
77+
* criterion that renders at runtime then parenthesis will be added.
78+
*
79+
* This function may be called multiple times in a context.
80+
*
81+
* @param criteriaReceiver a function to create the contained criteria
82+
*/
6783
fun or(criteriaReceiver: GroupingCriteriaReceiver): Unit =
6884
with(GroupingCriteriaCollector().apply(criteriaReceiver)) {
6985
this@GroupingCriteriaCollector.subCriteria.add(
@@ -75,7 +91,13 @@ class GroupingCriteriaCollector {
7591
}
7692

7793
/**
78-
* This should only be specified once per scope, and should be first
94+
* Add an initial criterion preceded with "not" to the current context. If the receiver adds more than one
95+
* criterion that renders at runtime then parenthesis will be added.
96+
*
97+
* This may only be called once per scope, and cannot be combined with "exists", "group", "invoke",
98+
* or any infix function in the same scope.
99+
*
100+
* @param criteriaReceiver a function to create the contained criteria
79101
*/
80102
fun not(criteriaReceiver: GroupingCriteriaReceiver): Unit =
81103
with(GroupingCriteriaCollector().apply(criteriaReceiver)) {
@@ -86,7 +108,12 @@ class GroupingCriteriaCollector {
86108
}
87109

88110
/**
89-
* This should only be specified once per scope, and should be first
111+
* Add an initial criterion composed of a sub-query preceded with "exists" to the current context.
112+
*
113+
* This should only be specified once per scope, and cannot be combined with "invoke",
114+
* "group", "not", or any infix function in the same scope.
115+
*
116+
* @param kotlinSubQueryBuilder a function to create a select statement
90117
*/
91118
fun exists(kotlinSubQueryBuilder: KotlinSubQueryBuilder.() -> Unit): Unit =
92119
with(KotlinSubQueryBuilder().apply(kotlinSubQueryBuilder)) {
@@ -95,11 +122,17 @@ class GroupingCriteriaCollector {
95122
}
96123

97124
/**
125+
* Add an initial criterion to the current context. If the receiver adds more than one
126+
* criterion that renders at runtime then parenthesis will be added.
127+
*
128+
* This should only be specified once per scope, and cannot be combined with "exists", "invoke",
129+
* "not", or any infix function in the same scope.
130+
*
98131
* This could "almost" be an operator invoke function. The problem is that
99-
* to call it a user would need to use "this" explicitly. I think that is too
132+
* to call it a user would need to use "this" explicitly. We think that is too
100133
* confusing, so we'll stick with the function name of "group"
101134
*
102-
* This should only be specified once per scope, and should be first
135+
* @param criteriaReceiver a function to create the contained criteria
103136
*/
104137
fun group(criteriaReceiver: GroupingCriteriaReceiver): Unit =
105138
with(GroupingCriteriaCollector().apply(criteriaReceiver)) {
@@ -110,8 +143,16 @@ class GroupingCriteriaCollector {
110143
}
111144

112145
/**
113-
* Build an initial criterion for a where clause, or a nested and/or/not group.
114-
* You can use it like A (isEqualTo(3))
146+
* Add an initial criterion to the current context based on a column and condition.
147+
* You can use it like "A.invoke(isEqualTo(3))" or "A (isEqualTo(3))".
148+
*
149+
* This is an extension function to a BindableColumn, but is scoped to the context of the
150+
* current collector.
151+
*
152+
* This should only be specified once per scope, and cannot be combined with "exists", "group",
153+
* "not", or any infix function in the same scope.
154+
*
155+
* @param condition the condition to be applied to this column, in this scope
115156
*/
116157
operator fun <T> BindableColumn<T>.invoke(condition: VisitableCondition<T>) {
117158
initialCriterion = ColumnAndConditionCriterion.withColumn(this)

src/site/markdown/docs/kotlinWhereClauses.md

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ overload functions, infix functions, and Kotlin receiver functions.
66

77
## Simple Where Clauses
88

9-
The simplest form of where clause includes a single condition. An example follows:
9+
The simplest form of where clause includes a single condition. See the following examples:
1010

1111
```kotlin
1212
select(foo) {
@@ -36,8 +36,8 @@ select(foo) {
3636
```
3737

3838
Most, but not all, of the built-in conditions can be expressed as infix functions. Conditions without parameters,
39-
or varargs conditions, cannot be called with infix. Good examples of those conditions are `isNull` and `isIn`. In
40-
those cases you will need to call the function directly as follows:
39+
or varargs conditions, cannot be called as an infix function. Good examples of conditions that cannot be called via
40+
an infix function are `isNull` and `isIn`. In those cases you will need to call the function directly as follows:
4141

4242
```kotlin
4343
select(foo) {
@@ -56,30 +56,30 @@ select(foo) {
5656
Many conditions support `filter` and `map` functions that can be used to test whether the condition should be rendered
5757
or to change the value of the condition parameter(s). If you need to use the `filter` or `map` functions, then you
5858
cannot use the infix functions. In this case you can use a function that creates the condition and then apply
59-
that condition to the where clause. For example:
59+
that condition to the where clause via the invoke operator. For example:
6060

6161
```kotlin
6262
select(foo) {
6363
from(bar)
64-
where { id (isEqualTo(3).map { it + 2 }) }
64+
where { firstName (isLike("fred").map { "%$it%" }) } // add wildcards for like
6565
}
6666
```
6767

68-
In this case, `isEqualTo` is a function in the `org.mybatis.dynamic.sql.util.kotlin.elements` package, not the infix
69-
function. Note that the condition is enclosed in parentheses. This is actually a function call using a Kotlin invoke
70-
operator overload. This can also be called explicitly without the operator overload as follows:
68+
In this case, `isLike` is a function in the `org.mybatis.dynamic.sql.util.kotlin.elements` package, not the infix
69+
function. Note also that the condition is enclosed in parentheses. This is actually a function call using a Kotlin
70+
invoke operator overload. This can also be called explicitly without the operator overload as follows:
7171

7272
```kotlin
7373
select(foo) {
7474
from(bar)
75-
where { id.invoke(isEqualTo(3).map { it + 2 }) }
75+
where { firstName.invoke(isLike("fred").map { "%$it%" }) } // add wildcards for like
7676
}
7777
```
7878

7979
## Compound Where Clauses
8080

8181
Of course many where clauses are composed of more than one condition. The where DSL supports arbitrarily complex
82-
where clauses with and/or/not phrases.
82+
where clauses with and/or/not phrases. See the following example of a complex where clause:
8383

8484
```kotlin
8585
select(foo) {
@@ -94,8 +94,9 @@ select(foo) {
9494
```
9595

9696
The `and`, `or`, and `not` functions each create a new context that can in turn include `and`, `or`, and `not`
97-
functions. The DSL has no limit on the depth of nesting of these functions. When there are nested `and` and `or`
98-
functions, the curly braces will essentially be rendered as parentheses in the final SQL.
97+
functions. The DSL has no practical limit on the depth of nesting of these functions. When there are nested
98+
`and` and `or` functions, the curly braces will be rendered as parentheses in the final SQL if the context contains
99+
more than one condition.
99100

100101
## Initial and Subsequent Conditions
101102

@@ -110,19 +111,21 @@ Everything is optional - if you don't specify an initial condition, or any subse
110111
render.
111112

112113
For each context, the renderer will add parenthesis around the rendered context if there is more than one condition in
113-
the context. Remember that the `filter` function can be used to remove a condition from rendering, so the parentheses
114-
are added only if the enclosed conditions all render.
114+
the context. Remember that a `filter` function can be used to remove some conditions from rendering, so the
115+
parentheses are added only if there is more than one condition that renders.
115116

116-
If you forget to specify an initial condition and only specify `and` and `or` groups, then the first "and" or "or"
117-
will be removed during rendering. This to avoid a rendered where clause like "where and id = 3".
117+
If you neglect to specify an initial condition and only specify `and` and `or` groups, then the first "and" or "or"
118+
will be removed during rendering. This to avoid a rendered where clause like "where and id = 3". This can be useful
119+
in situations where a where clause is composed by a number of different functions - there is no need to keep track
120+
of who goes first as the renderer will automatically strip the first connector.
118121

119122
### Initial Condition Types
120123

121124
There are four types of initial conditions. Only one of the initial condition types may be specified in any
122125
given context. Others must be enclosed in an `and` or an `or` block. The four types are as follows:
123126

124-
1. **Column and Criterion** (`id isEqualTo 3` - as shown above)
125-
2. **Not** (`not { id isEqualTo 3 }` - as shown above)
127+
1. **Column and Criterion** - either with the infix functions, or the invoke function as shown above
128+
2. **Not** - appends "not" to a group of criteria or a single criterion as shown above
126129
3. **Exists** - for executing an exists sub-query:
127130

128131
```kotlin
@@ -173,11 +176,11 @@ given context. Others must be enclosed in an `and` or an `or` block. The four ty
173176

174177
## Extending Where Clauses
175178

176-
In addition to the built-in conditions supplied with the library, it is possible to write your own custom
177-
conditions. Any custom condition can be used with the "invoke operator" method shown above in the "Using Filter And Map"
178-
section above.
179+
In addition to the built-in conditions supplied with the library, it is also possible to write your own custom
180+
conditions. Any custom condition can be used with the "invoke operator" method shown above in the
181+
"Using Filter And Map" section above.
179182

180183
At this time, it is not possible to add infix functions for custom conditions to the library. This is due to an
181184
underlying limitation in Kotlin itself. There is a Kotlin language enhancement on the roadmap that will likely
182-
remove this limitation. The enhancement will allow multiple receivers for an extension function. You can follow
185+
remove this limitation. That enhancement will allow multiple receivers for an extension function. You can follow
183186
progress of that enhancement here: https://youtrack.jetbrains.com/issue/KT-42435

0 commit comments

Comments
 (0)