Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d8e42b8
add Field, ScriptField and PainlessScript traits, add date time funct…
fupelaqu Sep 4, 2025
f1c3944
add script fields to elasticsearch request
fupelaqu Sep 4, 2025
8c3f8ad
add support to handle date time comparisons criteria
fupelaqu Sep 4, 2025
1ae3792
fix parser for Month
fupelaqu Sep 4, 2025
a546276
fix ne comparison for date math range query
fupelaqu Sep 4, 2025
bfc1426
add aliasOrName for all identifiers, handle date functions used with …
fupelaqu Sep 4, 2025
34434ca
fix parser specifications
fupelaqu Sep 4, 2025
c80405a
fix parser specifications lint bug
fupelaqu Sep 4, 2025
473cfcc
fix order by with buckets and aggregations
fupelaqu Sep 5, 2025
c931b70
add functions composition mechanism, add trait SQLTransformFunction a…
fupelaqu Sep 5, 2025
334c326
add SQLType and SQLTypedFunction[IN, OUT], add validations for functi…
fupelaqu Sep 5, 2025
dbff05d
add SQLScript
fupelaqu Sep 5, 2025
04a1fc4
to fix query specifications
fupelaqu Sep 5, 2025
522faa0
test extract function
fupelaqu Sep 5, 2025
2bbc83e
add diff (!=) operator
fupelaqu Sep 6, 2025
6137001
add SQLFunctionChain, SQLBinaryFunction, SQLFunctionField, implements…
fupelaqu Sep 6, 2025
6c85817
add trait Identifier, add / subtract interval as an arithmetic functi…
fupelaqu Sep 8, 2025
7c30aa6
add specifications for query with date_diff
fupelaqu Sep 8, 2025
d08ce77
to fix painless script with date_diff
fupelaqu Sep 8, 2025
75c88f2
update painless within identifier
fupelaqu Sep 8, 2025
36f8004
add SQLFunctionWithIdentifier, update date_add, date_sub, date_trunc,…
fupelaqu Sep 8, 2025
9d60fcf
introduce BinaryExpression
fupelaqu Sep 8, 2025
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
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ ThisBuild / organization := "app.softnetwork"

name := "softclient4es"

ThisBuild / version := "0.4.0"
ThisBuild / version := "0.5.0"

ThisBuild / scalaVersion := scala213

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import app.softnetwork.elastic.sql.{
BucketSelectorScript,
Count,
ElasticBoolQuery,
Field,
Max,
Min,
SQLBucket,
SQLCriteria,
SQLField,
SQLFunctionUtils,
SortOrder,
Sum
}
Expand Down Expand Up @@ -57,7 +58,7 @@ case class ElasticAggregation(

object ElasticAggregation {
def apply(
sqlAgg: SQLField,
sqlAgg: Field,
having: Option[SQLCriteria],
bucketsDirection: Map[String, SortOrder]
): ElasticAggregation = {
Expand Down Expand Up @@ -88,6 +89,23 @@ object ElasticAggregation {

var aggPath = Seq[String]()

val (aggFuncs, transformFuncs) = SQLFunctionUtils.aggregateAndTransformFunctions(identifier)

require(aggFuncs.size == 1, s"Multiple aggregate functions not supported: $aggFuncs")

def aggWithFieldOrScript(
buildField: (String, String) => Aggregation,
buildScript: (String, Script) => Aggregation
): Aggregation = {
if (transformFuncs.nonEmpty) {
val scriptSrc = identifier.painless
val script = Script(scriptSrc).lang("painless")
buildScript(aggName, script)
} else {
buildField(aggName, sourceField)
}
}

val _agg =
aggType match {
case Count =>
Expand All @@ -96,10 +114,10 @@ object ElasticAggregation {
else {
valueCountAgg(aggName, sourceField)
}
case Min => minAgg(aggName, sourceField)
case Max => maxAgg(aggName, sourceField)
case Avg => avgAgg(aggName, sourceField)
case Sum => sumAgg(aggName, sourceField)
case Min => aggWithFieldOrScript(minAgg, (name, s) => minAgg(name, sourceField).script(s))
case Max => aggWithFieldOrScript(maxAgg, (name, s) => maxAgg(name, sourceField).script(s))
case Avg => aggWithFieldOrScript(avgAgg, (name, s) => avgAgg(name, sourceField).script(s))
case Sum => aggWithFieldOrScript(sumAgg, (name, s) => sumAgg(name, sourceField).script(s))
}

val filteredAggName = "filtered_agg"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ import app.softnetwork.elastic.sql.{
ElasticNested,
ElasticParent,
SQLBetween,
SQLComparisonDateMath,
SQLExpression,
SQLIn,
SQLIsNotNull,
SQLIsNull
}
import com.sksamuel.elastic4s.ElasticApi.{bool, _}
import com.sksamuel.elastic4s.ElasticApi._
import com.sksamuel.elastic4s.searches.queries.Query

case class ElasticQuery(filter: ElasticFilter) {
Expand Down Expand Up @@ -69,6 +70,7 @@ case class ElasticQuery(filter: ElasticFilter) {
case between: SQLBetween[Double] => between
case geoDistance: ElasticGeoDistance => geoDistance
case matchExpression: ElasticMatch => matchExpression
case dateMath: SQLComparisonDateMath => dateMath
case other =>
throw new IllegalArgumentException(s"Unsupported filter type: ${other.getClass.getName}")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package app.softnetwork.elastic.sql.bridge

import app.softnetwork.elastic.sql.{SQLBucket, SQLCriteria, SQLExcept, SQLField}
import app.softnetwork.elastic.sql.{Field, SQLBucket, SQLCriteria, SQLExcept}
import com.sksamuel.elastic4s.searches.SearchRequest
import com.sksamuel.elastic4s.http.search.SearchBodyBuilderFn

case class ElasticSearchRequest(
fields: Seq[SQLField],
fields: Seq[Field],
except: Option[SQLExcept],
sources: Seq[String],
criteria: Option[SQLCriteria],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.sksamuel.elastic4s.ElasticApi
import com.sksamuel.elastic4s.ElasticApi._
import com.sksamuel.elastic4s.http.ElasticDsl.BuildableTermsNoOp
import com.sksamuel.elastic4s.http.search.SearchBodyBuilderFn
import com.sksamuel.elastic4s.script.Script
import com.sksamuel.elastic4s.searches.aggs.Aggregation
import com.sksamuel.elastic4s.searches.queries.Query
import com.sksamuel.elastic4s.searches.{MultiSearchRequest, SearchRequest}
Expand Down Expand Up @@ -95,6 +96,17 @@ package object bridge {
}
}

_search = scriptFields match {
case Nil => _search
case _ =>
_search scriptfields scriptFields.map { field =>
scriptField(
field.scriptName,
Script(script = field.painless).lang("painless").scriptType("source")
)
}
}

_search = orderBy match {
case Some(o) if aggregates.isEmpty && buckets.isEmpty =>
_search sortBy o.sorts.map(sort =>
Expand Down Expand Up @@ -134,7 +146,7 @@ package object bridge {
value match {
case n: SQLNumeric[_] if !aggregation =>
operator match {
case _: Ge.type =>
case Ge =>
maybeNot match {
case Some(_) =>
applyNumericOp(n)(
Expand All @@ -147,7 +159,7 @@ package object bridge {
d => rangeQuery(identifier.name) gte d
)
}
case _: Gt.type =>
case Gt =>
maybeNot match {
case Some(_) =>
applyNumericOp(n)(
Expand All @@ -160,7 +172,7 @@ package object bridge {
d => rangeQuery(identifier.name) gt d
)
}
case _: Le.type =>
case Le =>
maybeNot match {
case Some(_) =>
applyNumericOp(n)(
Expand All @@ -173,7 +185,7 @@ package object bridge {
d => rangeQuery(identifier.name) lte d
)
}
case _: Lt.type =>
case Lt =>
maybeNot match {
case Some(_) =>
applyNumericOp(n)(
Expand All @@ -186,7 +198,7 @@ package object bridge {
d => rangeQuery(identifier.name) lt d
)
}
case _: Eq.type =>
case Eq =>
maybeNot match {
case Some(_) =>
applyNumericOp(n)(
Expand All @@ -199,7 +211,7 @@ package object bridge {
d => termQuery(identifier.name, d)
)
}
case _: Ne.type =>
case Ne | Diff =>
maybeNot match {
case Some(_) =>
applyNumericOp(n)(
Expand All @@ -216,49 +228,49 @@ package object bridge {
}
case l: SQLLiteral if !aggregation =>
operator match {
case _: Like.type =>
case Like =>
maybeNot match {
case Some(_) =>
not(regexQuery(identifier.name, toRegex(l.value)))
case _ =>
regexQuery(identifier.name, toRegex(l.value))
}
case _: Ge.type =>
case Ge =>
maybeNot match {
case Some(_) =>
rangeQuery(identifier.name) lt l.value
case _ =>
rangeQuery(identifier.name) gte l.value
}
case _: Gt.type =>
case Gt =>
maybeNot match {
case Some(_) =>
rangeQuery(identifier.name) lte l.value
case _ =>
rangeQuery(identifier.name) gt l.value
}
case _: Le.type =>
case Le =>
maybeNot match {
case Some(_) =>
rangeQuery(identifier.name) gt l.value
case _ =>
rangeQuery(identifier.name) lte l.value
}
case _: Lt.type =>
case Lt =>
maybeNot match {
case Some(_) =>
rangeQuery(identifier.name) gte l.value
case _ =>
rangeQuery(identifier.name) lt l.value
}
case _: Eq.type =>
case Eq =>
maybeNot match {
case Some(_) =>
not(termQuery(identifier.name, l.value))
case _ =>
termQuery(identifier.name, l.value)
}
case _: Ne.type =>
case Ne | Diff =>
maybeNot match {
case Some(_) =>
termQuery(identifier.name, l.value)
Expand All @@ -269,14 +281,14 @@ package object bridge {
}
case b: SQLBoolean if !aggregation =>
operator match {
case _: Eq.type =>
case Eq =>
maybeNot match {
case Some(_) =>
not(termQuery(identifier.name, b.value))
case _ =>
termQuery(identifier.name, b.value)
}
case _: Ne.type =>
case Ne | Diff =>
maybeNot match {
case Some(_) =>
termQuery(identifier.name, b.value)
Expand All @@ -289,6 +301,26 @@ package object bridge {
}
}

implicit def dateMathToQuery(dateMath: SQLComparisonDateMath): Query = {
import dateMath._
if (aggregation)
return matchAllQuery()
dateTimeFunction match {
case _: CurrentTimeFunction =>
scriptQuery(Script(script = script).lang("painless").scriptType("source"))
case _ =>
val op = if (maybeNot.isDefined) operator.not else operator
op match {
case Gt => rangeQuery(identifier.name) gt script
case Ge => rangeQuery(identifier.name) gte script
case Lt => rangeQuery(identifier.name) lt script
case Le => rangeQuery(identifier.name) lte script
case Eq => rangeQuery(identifier.name) gte script lte script
case Ne | Diff => not(rangeQuery(identifier.name) gte script lte script)
}
}
}

implicit def isNullToQuery(
isNull: SQLIsNull
): Query = {
Expand Down
Loading