Skip to content

Commit 8178158

Browse files
committed
integrate macros within ElasticClientApi
1 parent 71979ef commit 8178158

File tree

10 files changed

+377
-94
lines changed

10 files changed

+377
-94
lines changed

build.sbt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,11 @@ lazy val core = project
158158
.configs(IntegrationTest)
159159
.settings(
160160
Defaults.itSettings,
161-
moduleSettings
161+
moduleSettings,
162+
scalacOptions ++= Seq(
163+
"-language:experimental.macros",
164+
"-Ymacro-debug-lite"
165+
)
162166
)
163167
.dependsOn(
164168
macros % "compile->compile;test->test;it->it"
@@ -213,6 +217,10 @@ def testkitProject(esVersion: String, ss: Def.SettingsDefinition*): Project = {
213217
Defaults.itSettings,
214218
app.softnetwork.Info.infoSettings,
215219
moduleSettings,
220+
scalacOptions ++= Seq(
221+
"-language:experimental.macros",
222+
"-Ymacro-debug-lite"
223+
),
216224
elasticSearchVersion := esVersion,
217225
buildInfoKeys += BuildInfoKey("elasticVersion" -> elasticSearchVersion.value),
218226
buildInfoObject := "SoftClient4esCoreTestkitBuildInfo",

core/src/main/scala/app/softnetwork/elastic/client/ElasticClientDelegator.scala

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -521,9 +521,9 @@ trait ElasticClientDelegator extends ElasticClientApi with BulkTypes {
521521
* true if the entity was indexed successfully, false otherwise
522522
*/
523523
override def index(
524-
index: JSONResults,
525-
id: JSONResults,
526-
source: JSONResults,
524+
index: String,
525+
id: String,
526+
source: String,
527527
wait: Boolean = false
528528
): ElasticResult[Boolean] =
529529
delegate.index(index, id, source, wait)
@@ -990,9 +990,10 @@ trait ElasticClientDelegator extends ElasticClientApi with BulkTypes {
990990
* @return
991991
* the entities matching the query
992992
*/
993-
override def searchAs[U](
993+
override def searchAsUnchecked[U](
994994
sqlQuery: SQLQuery
995-
)(implicit m: Manifest[U], formats: Formats): ElasticResult[Seq[U]] = delegate.searchAs(sqlQuery)
995+
)(implicit m: Manifest[U], formats: Formats): ElasticResult[Seq[U]] =
996+
delegate.searchAsUnchecked(sqlQuery)
996997

997998
/** Searches and converts results into typed entities.
998999
*
@@ -1035,6 +1036,9 @@ trait ElasticClientDelegator extends ElasticClientApi with BulkTypes {
10351036
delegate.multisearchAs(elasticQueries, fieldAliases, aggregations)
10361037

10371038
/** Asynchronous search with conversion to typed entities.
1039+
*
1040+
* @note
1041+
* This method is a variant of searchAsyncAs without compile-time SQL validation.
10381042
*
10391043
* @param sqlQuery
10401044
* the SQL query
@@ -1043,11 +1047,12 @@ trait ElasticClientDelegator extends ElasticClientApi with BulkTypes {
10431047
* @return
10441048
* a Future containing the entities
10451049
*/
1046-
override def searchAsyncAs[U](sqlQuery: SQLQuery)(implicit
1050+
override def searchAsyncAsUnchecked[U](sqlQuery: SQLQuery)(implicit
10471051
m: Manifest[U],
10481052
ec: ExecutionContext,
10491053
formats: Formats
1050-
): Future[ElasticResult[Seq[U]]] = delegate.searchAsyncAs(sqlQuery)
1054+
): Future[ElasticResult[Seq[U]]] =
1055+
delegate.searchAsyncAsUnchecked(sqlQuery)
10511056

10521057
/** Asynchronous search with conversion to typed entities.
10531058
*
@@ -1150,13 +1155,32 @@ trait ElasticClientDelegator extends ElasticClientApi with BulkTypes {
11501155
system: ActorSystem
11511156
): Source[(Map[String, Any], ScrollMetrics), NotUsed] = delegate.scroll(sql, config)
11521157

1153-
/** Typed scroll source
1158+
/** Scroll and convert results into typed entities from an SQL query.
1159+
*
1160+
* @note
1161+
* This method is a variant of scrollAs without compile-time SQL validation.
1162+
*
1163+
* @param sql
1164+
* - SQL query
1165+
* @param config
1166+
* - Scroll configuration
1167+
* @param system
1168+
* - Actor system
1169+
* @param m
1170+
* - Manifest for type T
1171+
* @param formats
1172+
* - JSON formats
1173+
* @tparam T
1174+
* - Target type
1175+
* @return
1176+
* - Source of tuples (T, ScrollMetrics)
11541177
*/
1155-
override def scrollAs[T](sql: SQLQuery, config: ScrollConfig)(implicit
1178+
override def scrollAsUnchecked[T](sql: SQLQuery, config: ScrollConfig)(implicit
11561179
system: ActorSystem,
11571180
m: Manifest[T],
11581181
formats: Formats
1159-
): Source[(T, ScrollMetrics), NotUsed] = delegate.scrollAs(sql, config)
1182+
): Source[(T, ScrollMetrics), NotUsed] =
1183+
delegate.scrollAsUnchecked(sql, config)
11601184

11611185
override private[client] def scrollClassic(
11621186
elasticQuery: ElasticQuery,

core/src/main/scala/app/softnetwork/elastic/client/ScrollApi.scala

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ import app.softnetwork.elastic.client.scroll.{
2727
UseScroll,
2828
UseSearchAfter
2929
}
30+
import app.softnetwork.elastic.sql.macros.SQLQueryMacros
3031
import app.softnetwork.elastic.sql.query.{SQLAggregation, SQLQuery}
3132
import org.json4s.{Formats, JNothing}
3233
import org.json4s.jackson.JsonMethods.parse
3334

3435
import scala.concurrent.{ExecutionContext, Promise}
36+
import scala.language.experimental.macros
3537
import scala.util.{Failure, Success}
3638

3739
/** API for scrolling through search results using Akka Streams.
@@ -167,9 +169,57 @@ trait ScrollApi extends ElasticClientHelpers {
167169
hasSorts: Boolean = false
168170
)(implicit system: ActorSystem): Source[Map[String, Any], NotUsed]
169171

170-
/** Typed scroll source
172+
/** Typed scroll source converting results into typed entities from an SQL query
173+
*
174+
* @note
175+
* This method provides compile-time SQL validation via macros.
176+
*
177+
* @param sql
178+
* - SQL query
179+
* @param config
180+
* - Scroll configuration
181+
* @param system
182+
* - Actor system
183+
* @param m
184+
* - Manifest for type T
185+
* @param formats
186+
* - JSON formats
187+
* @tparam T
188+
* - Target type
189+
* @return
190+
* - Source of tuples (T, ScrollMetrics)
171191
*/
172192
def scrollAs[T](
193+
sql: String,
194+
config: ScrollConfig = ScrollConfig()
195+
)(implicit
196+
system: ActorSystem,
197+
m: Manifest[T],
198+
formats: Formats
199+
): Source[(T, ScrollMetrics), NotUsed] =
200+
macro SQLQueryMacros.scrollAsImpl[T]
201+
202+
/** Scroll and convert results into typed entities from an SQL query.
203+
*
204+
* @note
205+
* This method is a variant of scrollAs without compile-time SQL validation.
206+
*
207+
* @param sql
208+
* - SQL query
209+
* @param config
210+
* - Scroll configuration
211+
* @param system
212+
* - Actor system
213+
* @param m
214+
* - Manifest for type T
215+
* @param formats
216+
* - JSON formats
217+
* @tparam T
218+
* - Target type
219+
* @return
220+
* - Source of tuples (T, ScrollMetrics)
221+
*/
222+
def scrollAsUnchecked[T](
173223
sql: SQLQuery,
174224
config: ScrollConfig = ScrollConfig()
175225
)(implicit

core/src/main/scala/app/softnetwork/elastic/client/SearchApi.scala

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@ import app.softnetwork.elastic.client.result.{
2222
ElasticResult,
2323
ElasticSuccess
2424
}
25+
import app.softnetwork.elastic.sql.macros.SQLQueryMacros
2526
import app.softnetwork.elastic.sql.query.{SQLAggregation, SQLQuery, SQLSearchRequest}
2627
import com.google.gson.{Gson, JsonElement, JsonObject, JsonParser}
2728
import org.json4s.Formats
2829

2930
import scala.concurrent.{ExecutionContext, Future}
3031
import scala.jdk.CollectionConverters._
32+
import scala.language.experimental.macros
3133
import scala.reflect.{classTag, ClassTag}
3234
import scala.util.{Failure, Success, Try}
3335

@@ -400,14 +402,34 @@ trait SearchApi extends ElasticConversion with ElasticClientHelpers {
400402

401403
/** Searches and converts results into typed entities from an SQL query.
402404
*
403-
* @param sqlQuery
405+
* @note
406+
* This method uses compile-time macros to validate the SQL query against the type U.
407+
*
408+
* @param query
404409
* the SQL query containing fieldAliases and aggregations
405410
* @tparam U
406411
* the type of entities to return
407412
* @return
408413
* the entities matching the query
409414
*/
410415
def searchAs[U](
416+
query: String
417+
)(implicit m: Manifest[U], formats: Formats): ElasticResult[Seq[U]] =
418+
macro SQLQueryMacros.searchAsImpl[U]
419+
420+
/** Searches and converts results into typed entities from an SQL query.
421+
*
422+
* @note
423+
* This method is a variant of searchAs without compile-time SQL validation.
424+
*
425+
* @param sqlQuery
426+
* the SQL query containing fieldAliases and aggregations
427+
* @tparam U
428+
* the type of entities to return
429+
* @return
430+
* the entities matching the query
431+
*/
432+
def searchAsUnchecked[U](
411433
sqlQuery: SQLQuery
412434
)(implicit m: Manifest[U], formats: Formats): ElasticResult[Seq[U]] = {
413435
for {
@@ -473,14 +495,38 @@ trait SearchApi extends ElasticConversion with ElasticClientHelpers {
473495

474496
/** Asynchronous search with conversion to typed entities.
475497
*
476-
* @param sqlQuery
498+
* @note
499+
* This method uses compile-time macros to validate the SQL query against the type U.
500+
*
501+
* @param query
477502
* the SQL query
478503
* @tparam U
479504
* the type of entities to return
480505
* @return
481506
* a Future containing the entities
482507
*/
483508
def searchAsyncAs[U](
509+
query: String
510+
)(implicit
511+
m: Manifest[U],
512+
ec: ExecutionContext,
513+
formats: Formats
514+
): Future[ElasticResult[Seq[U]]] =
515+
macro SQLQueryMacros.searchAsyncAsImpl[U]
516+
517+
/** Asynchronous search with conversion to typed entities.
518+
*
519+
* @note
520+
* This method is a variant of searchAsyncAs without compile-time SQL validation.
521+
*
522+
* @param sqlQuery
523+
* the SQL query
524+
* @tparam U
525+
* the type of entities to return
526+
* @return
527+
* a Future containing the entities
528+
*/
529+
def searchAsyncAsUnchecked[U](
484530
sqlQuery: SQLQuery
485531
)(implicit
486532
m: Manifest[U],

core/src/main/scala/app/softnetwork/elastic/client/metrics/MetricsElasticClient.scala

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -663,14 +663,17 @@ class MetricsElasticClient(
663663
* @return
664664
* the entities matching the query
665665
*/
666-
override def searchAs[U](
666+
override def searchAsUnchecked[U](
667667
sqlQuery: SQLQuery
668668
)(implicit m: Manifest[U], formats: Formats): ElasticResult[Seq[U]] =
669669
measureResult("searchAs") {
670-
delegate.searchAs[U](sqlQuery)
670+
delegate.searchAsUnchecked[U](sqlQuery)
671671
}
672672

673673
/** Asynchronous search with conversion to typed entities.
674+
*
675+
* @note
676+
* This method is a variant of searchAsyncAs without compile-time SQL validation.
674677
*
675678
* @param sqlQuery
676679
* the SQL query
@@ -679,13 +682,13 @@ class MetricsElasticClient(
679682
* @return
680683
* a Future containing the entities
681684
*/
682-
override def searchAsyncAs[U](sqlQuery: SQLQuery)(implicit
685+
override def searchAsyncAsUnchecked[U](sqlQuery: SQLQuery)(implicit
683686
m: Manifest[U],
684687
ec: ExecutionContext,
685688
formats: Formats
686689
): Future[ElasticResult[Seq[U]]] =
687690
measureAsync("searchAsyncAs") {
688-
delegate.searchAsyncAs[U](sqlQuery)
691+
delegate.searchAsyncAsUnchecked[U](sqlQuery)
689692
}
690693

691694
override def singleSearch(
@@ -900,16 +903,33 @@ class MetricsElasticClient(
900903

901904
}
902905

903-
/** Typed scroll source
906+
/** Scroll and convert results into typed entities from an SQL query.
907+
*
908+
* @note
909+
* This method is a variant of scrollAs without compile-time SQL validation.
910+
* @param sql
911+
* - SQL query
912+
* @param config
913+
* - Scroll configuration
914+
* @param system
915+
* - Actor system
916+
* @param m
917+
* - Manifest for type T
918+
* @param formats
919+
* - JSON formats
920+
* @tparam T
921+
* - Target type
922+
* @return
923+
* - Source of tuples (T, ScrollMetrics)
904924
*/
905-
override def scrollAs[T](sql: SQLQuery, config: ScrollConfig)(implicit
925+
override def scrollAsUnchecked[T](sql: SQLQuery, config: ScrollConfig)(implicit
906926
system: ActorSystem,
907927
m: Manifest[T],
908928
formats: Formats
909929
): Source[(T, ScrollMetrics), NotUsed] = {
910930
// Note: For streams, we measure at the beginning but not every element
911931
val startTime = System.currentTimeMillis()
912-
val source = delegate.scrollAs[T](sql, config)
932+
val source = delegate.scrollAsUnchecked[T](sql, config)
913933

914934
source.watchTermination() { (_, done) =>
915935
done.onComplete { result =>

macros/src/main/scala/app/softnetwork/elastic/client/macros/TestElasticClientApi.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
/*
2+
* Copyright 2025 SOFTNETWORK
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
117
package app.softnetwork.elastic.client.macros
218

319
import app.softnetwork.elastic.sql.macros.SQLQueryMacros

0 commit comments

Comments
 (0)