Skip to content

Commit cda49ac

Browse files
committed
support nested filtered aggregation
1 parent c88174d commit cda49ac

File tree

6 files changed

+271
-33
lines changed

6 files changed

+271
-33
lines changed

es6/sql-bridge/src/main/scala/app/softnetwork/elastic/sql/bridge/package.scala

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,31 @@ import scala.language.implicitConversions
2020

2121
package object bridge {
2222

23+
implicit def requestToNestedFilterAggregation(
24+
request: SQLSearchRequest,
25+
innerHitsName: String
26+
): Option[FilterAggregation] =
27+
request.where.flatMap(_.criteria) match {
28+
case Some(f) =>
29+
f.nestedCriteria(innerHitsName) match {
30+
case Nil => None
31+
case cs =>
32+
val boolQuery = ElasticBoolQuery(group = true)
33+
cs.map(c => boolQuery.filter(c.asFilter(Option(boolQuery))))
34+
Some(
35+
filterAgg(
36+
s"filtered_$innerHitsName",
37+
boolQuery.query(
38+
request.aggregates.flatMap(_.identifier.innerHitsName).toSet,
39+
Option(boolQuery)
40+
)
41+
)
42+
)
43+
}
44+
case _ =>
45+
None
46+
}
47+
2348
implicit def requestToFilterAggregation(
2449
request: SQLSearchRequest
2550
): Option[FilterAggregation] =
@@ -120,6 +145,8 @@ package object bridge {
120145
case Some(b) => Seq(b)
121146
case _ => aggregations
122147
}
148+
val nestedFilteredAgg: Option[FilterAggregation] =
149+
requestToNestedFilterAggregation(request, n.innerHitsName)
123150
val filtered: Option[Aggregation] =
124151
requestToFilterAggregation(request).map(filtered => filtered.subAggregations(buckets))
125152
val children = n.children
@@ -135,12 +162,20 @@ package object bridge {
135162
nestedAggregation(
136163
n.innerHitsName,
137164
n.path
138-
) subaggs buckets ++ Seq(combinedAgg)
165+
) subaggs (nestedFilteredAgg match {
166+
case Some(filteredAgg) =>
167+
Seq(filteredAgg subaggs buckets ++ Seq(combinedAgg))
168+
case _ => buckets ++ Seq(combinedAgg)
169+
})
139170
} else {
140171
nestedAggregation(
141172
n.innerHitsName,
142173
n.path
143-
) subaggs filtered.map(Seq(_)).getOrElse(buckets)
174+
) subaggs (nestedFilteredAgg match {
175+
case Some(filteredAgg) =>
176+
Seq(filteredAgg subaggs filtered.map(Seq(_)).getOrElse(buckets))
177+
case _ => filtered.map(Seq(_)).getOrElse(buckets)
178+
})
144179
}
145180
}
146181
buildNestedAgg(tree)
@@ -258,7 +293,7 @@ package object bridge {
258293
_search scriptfields scriptFields.map { field =>
259294
scriptField(
260295
field.scriptName,
261-
Script(script = field.painless)
296+
Script(script = field.painless(None))
262297
.lang("painless")
263298
.scriptType("source")
264299
.params(field.identifier.functions.headOption match {
@@ -313,7 +348,7 @@ package object bridge {
313348
case _ => true
314349
}))
315350
) {
316-
return scriptQuery(Script(script = painless).lang("painless").scriptType("source"))
351+
return scriptQuery(Script(script = painless(None)).lang("painless").scriptType("source"))
317352
}
318353
// Geo distance special case
319354
identifier.functions.headOption match {
@@ -527,10 +562,10 @@ package object bridge {
527562
case NE | DIFF => not(rangeQuery(identifier.name) gte script lte script)
528563
}
529564
case _ =>
530-
scriptQuery(Script(script = painless).lang("painless").scriptType("source"))
565+
scriptQuery(Script(script = painless(None)).lang("painless").scriptType("source"))
531566
}
532567
case _ =>
533-
scriptQuery(Script(script = painless).lang("painless").scriptType("source"))
568+
scriptQuery(Script(script = painless(None)).lang("painless").scriptType("source"))
534569
}
535570
case _ => matchAllQuery()
536571
}
@@ -684,7 +719,7 @@ package object bridge {
684719
case _ =>
685720
scriptQuery(
686721
Script(
687-
script = distanceCriteria.painless,
722+
script = distanceCriteria.painless(None),
688723
lang = Some("painless"),
689724
scriptType = Source,
690725
params = distance.params

es6/sql-bridge/src/test/scala/app/softnetwork/elastic/sql/SQLQuerySpec.scala

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3491,4 +3491,75 @@ class SQLQuerySpec extends AnyFlatSpec with Matchers {
34913491
|}""".stripMargin.replaceAll("\\s+", "")
34923492
}
34933493

3494+
it should "handle where filters according to scope" in {
3495+
val select: ElasticSearchRequest =
3496+
SQLQuery(whereFiltersAccordingToScope)
3497+
val query = select.query
3498+
println(query)
3499+
query shouldBe
3500+
"""{
3501+
| "query": {
3502+
| "bool": {
3503+
| "filter": [
3504+
| {
3505+
| "term": {
3506+
| "status": {
3507+
| "value": "active"
3508+
| }
3509+
| }
3510+
| },
3511+
| {
3512+
| "nested": {
3513+
| "path": "comments",
3514+
| "query": {
3515+
| "term": {
3516+
| "comments.sentiment": {
3517+
| "value": "positive"
3518+
| }
3519+
| }
3520+
| },
3521+
| "inner_hits": {
3522+
| "name": "comments"
3523+
| }
3524+
| }
3525+
| }
3526+
| ]
3527+
| }
3528+
| },
3529+
| "size": 0,
3530+
| "_source": true,
3531+
| "aggs": {
3532+
| "comments": {
3533+
| "nested": {
3534+
| "path": "comments"
3535+
| },
3536+
| "aggs": {
3537+
| "filtered_comments": {
3538+
| "filter": {
3539+
| "bool": {
3540+
| "filter": [
3541+
| {
3542+
| "term": {
3543+
| "comments.sentiment": {
3544+
| "value": "positive"
3545+
| }
3546+
| }
3547+
| }
3548+
| ]
3549+
| }
3550+
| },
3551+
| "aggs": {
3552+
| "nb_comments": {
3553+
| "value_count": {
3554+
| "field": "comments.id"
3555+
| }
3556+
| }
3557+
| }
3558+
| }
3559+
| }
3560+
| }
3561+
| }
3562+
|}""".stripMargin.replaceAll("\\s+", "")
3563+
}
3564+
34943565
}

sql/bridge/src/main/scala/app/softnetwork/elastic/sql/bridge/package.scala

Lines changed: 57 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,31 @@ import scala.language.implicitConversions
2424

2525
package object bridge {
2626

27+
implicit def requestToNestedFilterAggregation(
28+
request: SQLSearchRequest,
29+
innerHitsName: String
30+
): Option[FilterAggregation] =
31+
request.where.flatMap(_.criteria) match {
32+
case Some(f) =>
33+
f.nestedCriteria(innerHitsName) match {
34+
case Nil => None
35+
case cs =>
36+
val boolQuery = ElasticBoolQuery(group = true)
37+
cs.map(c => boolQuery.filter(c.asFilter(Option(boolQuery))))
38+
Some(
39+
filterAgg(
40+
s"filtered_$innerHitsName",
41+
boolQuery.query(request.aggregates.flatMap(_.identifier.innerHitsName).toSet, Option(boolQuery))
42+
)
43+
)
44+
}
45+
case _ =>
46+
None
47+
}
48+
2749
implicit def requestToFilterAggregation(
28-
request: SQLSearchRequest
29-
): Option[FilterAggregation] =
50+
request: SQLSearchRequest
51+
): Option[FilterAggregation] =
3052
request.having.flatMap(_.criteria) match {
3153
case Some(f) =>
3254
val boolQuery = Option(ElasticBoolQuery(group = true))
@@ -42,8 +64,8 @@ package object bridge {
4264
}
4365

4466
implicit def requestToRootAggregations(
45-
request: SQLSearchRequest
46-
): Seq[Aggregation] = {
67+
request: SQLSearchRequest
68+
): Seq[Aggregation] = {
4769
val aggregations = request.aggregates.map(
4870
ElasticAggregation(_, request.having.flatMap(_.criteria), request.sorts)
4971
)
@@ -79,8 +101,8 @@ package object bridge {
79101
}
80102

81103
implicit def requestToScopedAggregations(
82-
request: SQLSearchRequest
83-
): Seq[NestedAggregation] = {
104+
request: SQLSearchRequest
105+
): Seq[NestedAggregation] = {
84106
val aggregations = request.aggregates.map(
85107
ElasticAggregation(_, request.having.flatMap(_.criteria), request.sorts)
86108
)
@@ -124,6 +146,8 @@ package object bridge {
124146
case Some(b) => Seq(b)
125147
case _ => aggregations
126148
}
149+
val nestedFilteredAgg: Option[FilterAggregation] =
150+
requestToNestedFilterAggregation(request, n.innerHitsName)
127151
val filtered: Option[Aggregation] =
128152
requestToFilterAggregation(request).map(filtered => filtered.subAggregations(buckets))
129153
val children = n.children
@@ -139,12 +163,20 @@ package object bridge {
139163
nestedAggregation(
140164
n.innerHitsName,
141165
n.path
142-
) subaggs buckets ++ Seq(combinedAgg)
166+
) subaggs (nestedFilteredAgg match {
167+
case Some(filteredAgg) =>
168+
Seq(filteredAgg subaggs buckets ++ Seq(combinedAgg))
169+
case _ => buckets ++ Seq(combinedAgg)
170+
})
143171
} else {
144172
nestedAggregation(
145173
n.innerHitsName,
146174
n.path
147-
) subaggs filtered.map(Seq(_)).getOrElse(buckets)
175+
) subaggs (nestedFilteredAgg match {
176+
case Some(filteredAgg) =>
177+
Seq(filteredAgg subaggs filtered.map(Seq(_)).getOrElse(buckets))
178+
case _ => filtered.map(Seq(_)).getOrElse(buckets)
179+
})
148180
}
149181
}
150182
buildNestedAgg(tree)
@@ -262,7 +294,7 @@ package object bridge {
262294
_search scriptfields scriptFields.map { field =>
263295
scriptField(
264296
field.scriptName,
265-
Script(script = field.painless)
297+
Script(script = field.painless(None))
266298
.lang("painless")
267299
.scriptType("source")
268300
.params(field.identifier.functions.headOption match {
@@ -317,7 +349,7 @@ package object bridge {
317349
case _ => true
318350
}))
319351
) {
320-
return scriptQuery(Script(script = painless).lang("painless").scriptType("source"))
352+
return scriptQuery(Script(script = painless(None)).lang("painless").scriptType("source"))
321353
}
322354
// Geo distance special case
323355
identifier.functions.headOption match {
@@ -531,25 +563,25 @@ package object bridge {
531563
case NE | DIFF => not(rangeQuery(identifier.name) gte script lte script)
532564
}
533565
case _ =>
534-
scriptQuery(Script(script = painless).lang("painless").scriptType("source"))
566+
scriptQuery(Script(script = painless(None)).lang("painless").scriptType("source"))
535567
}
536568
case _ =>
537-
scriptQuery(Script(script = painless).lang("painless").scriptType("source"))
569+
scriptQuery(Script(script = painless(None)).lang("painless").scriptType("source"))
538570
}
539571
case _ => matchAllQuery()
540572
}
541573
}
542574

543575
implicit def isNullToQuery(
544-
isNull: IsNullExpr
545-
): Query = {
576+
isNull: IsNullExpr
577+
): Query = {
546578
import isNull._
547579
not(existsQuery(identifier.name))
548580
}
549581

550582
implicit def isNotNullToQuery(
551-
isNotNull: IsNotNullExpr
552-
): Query = {
583+
isNotNull: IsNotNullExpr
584+
): Query = {
553585
import isNotNull._
554586
existsQuery(identifier.name)
555587
}
@@ -688,7 +720,7 @@ package object bridge {
688720
case _ =>
689721
scriptQuery(
690722
Script(
691-
script = distanceCriteria.painless,
723+
script = distanceCriteria.painless(None),
692724
lang = Some("painless"),
693725
scriptType = Source,
694726
params = distance.params
@@ -698,29 +730,29 @@ package object bridge {
698730
}
699731

700732
implicit def matchToQuery(
701-
matchExpression: ElasticMatch
702-
): Query = {
733+
matchExpression: ElasticMatch
734+
): Query = {
703735
import matchExpression._
704736
matchQuery(identifier.name, value.value)
705737
}
706738

707739
implicit def criteriaToElasticCriteria(
708-
criteria: Criteria
709-
): ElasticCriteria = {
740+
criteria: Criteria
741+
): ElasticCriteria = {
710742
ElasticCriteria(
711743
criteria
712744
)
713745
}
714746

715747
implicit def filterToQuery(
716-
filter: ElasticFilter
717-
): ElasticQuery = {
748+
filter: ElasticFilter
749+
): ElasticQuery = {
718750
ElasticQuery(filter)
719751
}
720752

721753
implicit def sqlQueryToAggregations(
722-
query: SQLQuery
723-
): Seq[ElasticAggregation] = {
754+
query: SQLQuery
755+
): Seq[ElasticAggregation] = {
724756
import query._
725757
request
726758
.map {

0 commit comments

Comments
 (0)