Skip to content

Commit a0b9d0b

Browse files
authored
Merge pull request #49 from SOFTNETWORK-APP/feature/demos
- fix #41 - fix #46 - fix #47 - fix #48
2 parents 4299685 + edd4c81 commit a0b9d0b

17 files changed

Lines changed: 436 additions & 113 deletions

File tree

README.md

Lines changed: 52 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,10 @@ Download the self-contained fat JAR for your Elasticsearch version:
172172

173173
| Elasticsearch Version | Artifact |
174174
|-----------------------|----------------------------------------|
175-
| ES 6.x | `softclient4es6-jdbc-driver-0.1.0.jar` |
176-
| ES 7.x | `softclient4es7-jdbc-driver-0.1.0.jar` |
177-
| ES 8.x | `softclient4es8-jdbc-driver-0.1.0.jar` |
178-
| ES 9.x | `softclient4es9-jdbc-driver-0.1.0.jar` |
175+
| ES 6.x | `softclient4es6-jdbc-driver-0.1.1.jar` |
176+
| ES 7.x | `softclient4es7-jdbc-driver-0.1.1.jar` |
177+
| ES 8.x | `softclient4es8-jdbc-driver-0.1.1.jar` |
178+
| ES 9.x | `softclient4es9-jdbc-driver-0.1.1.jar` |
179179

180180
```text
181181
JDBC URL: jdbc:elastic://localhost:9200
@@ -184,30 +184,58 @@ Driver class: app.softnetwork.elastic.jdbc.ElasticDriver
184184

185185
### Maven / Gradle / sbt
186186

187+
**Maven:**
188+
187189
```xml
188190
<dependency>
189191
<groupId>app.softnetwork.elastic</groupId>
190192
<artifactId>softclient4es8-jdbc-driver</artifactId>
191-
<version>0.1.0</version>
193+
<version>0.1.1</version>
192194
</dependency>
193195
```
194196

197+
**Gradle:**
198+
199+
```groovy
200+
implementation 'app.softnetwork.elastic:softclient4es8-jdbc-driver:0.1.1'
201+
```
202+
203+
**sbt:**
204+
205+
```scala
206+
libraryDependencies += "app.softnetwork.elastic" % "softclient4es8-jdbc-driver" % "0.1.1"
207+
```
208+
195209
The JDBC driver JARs are Scala-version-independent (no `_2.12` or `_2.13` suffix) and include all required dependencies.
196210

197211
---
198212

199213
## 🛠️ Scala Library Integration
200214

201-
For programmatic access, add SoftClient4ES to your project:
215+
For programmatic access, add SoftClient4ES to your project.
216+
217+
### Client Library Matrix
218+
219+
| Elasticsearch | Artifact | Scala | JDK |
220+
|----------------|------------------------------|------------|------|
221+
| 6.x | `softclient4es6-jest-client` | 2.12, 2.13 | 8+ |
222+
| 6.x | `softclient4es6-rest-client` | 2.12, 2.13 | 8+ |
223+
| 7.x | `softclient4es7-rest-client` | 2.12, 2.13 | 8+ |
224+
| 8.x | `softclient4es8-java-client` | 2.12, 2.13 | 8+ |
225+
| 9.x | `softclient4es9-java-client` | 2.13 only | 17+ |
226+
227+
### sbt Setup
202228

203229
```scala
204230
// build.sbt
205231
resolvers += "Softnetwork" at "https://softnetwork.jfrog.io/artifactory/releases/"
206232

207233
// Choose your Elasticsearch version
208-
libraryDependencies += "app.softnetwork.elastic" %% "softclient4es8-java-client" % "0.17.4"
234+
libraryDependencies += "app.softnetwork.elastic" %% "softclient4es8-java-client" % "0.18.0"
209235
// Add the community extensions for materialized views (optional)
210236
libraryDependencies += "app.softnetwork.elastic" %% "softclient4es-community-extensions" % "0.1.1"
237+
// Add the JDBC driver if you want to use it from Scala (optional)
238+
libraryDependencies += "app.softnetwork.elastic" %% "softclient4es-jdbc-driver" % "0.1.1"
211239
```
212240

213241
```scala
@@ -286,34 +314,25 @@ Seamlessly sync event-sourced systems with Elasticsearch.
286314

287315
## 📦 Editions and Licensing
288316

289-
SoftClient4ES is available in two editions:
290-
291-
### Community Edition (Open Source)
292-
293-
Licensed under the **Apache License 2.0**. Includes the core SQL engine, REPL client, Scala library, and the community extensions library with limited materialized views support:
294-
295-
| Feature | Community |
296-
|--------------------------------------------------------------------|-------------|
297-
| Full SQL DDL (CREATE, ALTER, DROP TABLE) | Yes |
298-
| Full SQL DML (INSERT, UPDATE, DELETE, COPY INTO) | Yes |
299-
| Full SQL DQL (SELECT, JOIN UNNEST, aggregations, window functions) | Yes |
300-
| Pipelines, Watchers, Enrich Policies | Yes |
301-
| Interactive REPL client | Yes |
302-
| Scala library (Akka Streams) | Yes |
303-
| Community extensions library (Scala) | Yes |
304-
| Materialized Views (CREATE, REFRESH, DESCRIBE) | Yes (max 3) |
305-
| Elasticsearch 6, 7, 8, 9 support | Yes |
317+
SoftClient4ES uses a dual-license model:
306318

307-
### Pro / Enterprise Edition (Commercial)
319+
- **Core** (SQL engine, REPL client, Scala library) — **Apache License 2.0** (open source)
320+
- **JDBC Driver** and **Materialized Views****Elastic License 2.0** (free to use, not open source)
308321

309-
Adds the **JDBC driver** (which includes the community extensions) and raises materialized view limits:
322+
### Feature Matrix
310323

311-
| Feature | Community | Pro | Enterprise |
312-
|--------------------------------------|-----------|---------|------------|
313-
| Everything in Community | Yes | Yes | Yes |
314-
| JDBC driver (DBeaver, Tableau, etc.) | - | Yes | Yes |
315-
| Maximum materialized views | 3 | Limited | Unlimited |
316-
| Priority support | - | - | Yes |
324+
| Feature | Community | Pro | Enterprise |
325+
|--------------------------------------------------------------------|-----------|---------|------------|
326+
| Full SQL DDL (CREATE, ALTER, DROP TABLE) | Yes | Yes | Yes |
327+
| Full SQL DML (INSERT, UPDATE, DELETE, COPY INTO) | Yes | Yes | Yes |
328+
| Full SQL DQL (SELECT, JOIN UNNEST, aggregations, window functions) | Yes | Yes | Yes |
329+
| Pipelines, Watchers, Enrich Policies | Yes | Yes | Yes |
330+
| Interactive REPL client | Yes | Yes | Yes |
331+
| Scala library (Akka Streams) | Yes | Yes | Yes |
332+
| Elasticsearch 6, 7, 8, 9 support | Yes | Yes | Yes |
333+
| JDBC driver (DBeaver, Tableau, etc.) | Yes | Yes | Yes |
334+
| Materialized Views (CREATE, REFRESH, DESCRIBE) | Max 3 | Limited | Unlimited |
335+
| Priority support | - | - | Yes |
317336

318337
### Elasticsearch License Requirements
319338

@@ -349,7 +368,7 @@ Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines
349368

350369
The core SQL engine and REPL client are licensed under the **Apache License 2.0** — see [LICENSE](LICENSE) for details.
351370

352-
The JDBC driver and Materialized Views extension are available under a commercial license. Contact us for pricing information.
371+
The JDBC driver and Materialized Views extension are licensed under the **Elastic License 2.0** — free to use, not open source.
353372

354373
---
355374

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

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2486,7 +2486,7 @@ class SQLQuerySpec extends AnyFlatSpec with Matchers {
24862486
| "source": "def param1 = (doc['identifier'].size() == 0 ? null : doc['identifier'].value); (param1 == null) ? null : (param1 % 2)"
24872487
| }
24882488
| },
2489-
| "identifier_mul_identifier2_minus_10": {
2489+
| "__c7": {
24902490
| "script": {
24912491
| "lang": "painless",
24922492
| "source": "def param1 = (doc['identifier'].size() == 0 ? null : doc['identifier'].value); def param2 = (doc['identifier2'].size() == 0 ? null : doc['identifier2'].value); def lv0 = ((param1 == null || param2 == null) ? null : (param1 * param2)); (lv0 == null) ? null : (lv0 - 10)"
@@ -2546,109 +2546,109 @@ class SQLQuerySpec extends AnyFlatSpec with Matchers {
25462546
| }
25472547
| },
25482548
| "script_fields": {
2549-
| "abs_identifier_plus_1_0_mul_2": {
2549+
| "__c2": {
25502550
| "script": {
25512551
| "lang": "painless",
25522552
| "source": "def param1 = (doc['identifier'].size() == 0 ? null : doc['identifier'].value); ((param1 == null) ? null : Double.valueOf(Math.abs(param1)) + 1.0) * ((double) 2)"
25532553
| }
25542554
| },
2555-
| "ceil_identifier": {
2555+
| "__c3": {
25562556
| "script": {
25572557
| "lang": "painless",
25582558
| "source": "def param1 = (doc['identifier'].size() == 0 ? null : doc['identifier'].value); (param1 == null) ? null : Double.valueOf(Math.ceil(param1))"
25592559
| }
25602560
| },
2561-
| "floor_identifier": {
2561+
| "__c4": {
25622562
| "script": {
25632563
| "lang": "painless",
25642564
| "source": "def param1 = (doc['identifier'].size() == 0 ? null : doc['identifier'].value); (param1 == null) ? null : Double.valueOf(Math.floor(param1))"
25652565
| }
25662566
| },
2567-
| "sqrt_identifier": {
2567+
| "__c5": {
25682568
| "script": {
25692569
| "lang": "painless",
25702570
| "source": "def param1 = (doc['identifier'].size() == 0 ? null : doc['identifier'].value); (param1 == null) ? null : Double.valueOf(Math.sqrt(param1))"
25712571
| }
25722572
| },
2573-
| "exp_identifier": {
2573+
| "__c6": {
25742574
| "script": {
25752575
| "lang": "painless",
25762576
| "source": "def param1 = (doc['identifier'].size() == 0 ? null : doc['identifier'].value); (param1 == null) ? null : Double.valueOf(Math.exp(param1))"
25772577
| }
25782578
| },
2579-
| "log_identifier": {
2579+
| "__c7": {
25802580
| "script": {
25812581
| "lang": "painless",
25822582
| "source": "def param1 = (doc['identifier'].size() == 0 ? null : doc['identifier'].value); (param1 == null) ? null : Double.valueOf(Math.log(param1))"
25832583
| }
25842584
| },
2585-
| "log10_identifier": {
2585+
| "__c8": {
25862586
| "script": {
25872587
| "lang": "painless",
25882588
| "source": "def param1 = (doc['identifier'].size() == 0 ? null : doc['identifier'].value); (param1 == null) ? null : Double.valueOf(Math.log10(param1))"
25892589
| }
25902590
| },
2591-
| "pow_identifier_3": {
2591+
| "__c9": {
25922592
| "script": {
25932593
| "lang": "painless",
25942594
| "source": "def param1 = (doc['identifier'].size() == 0 ? null : doc['identifier'].value); (param1 == null) ? null : Double.valueOf(Math.pow(param1, 3))"
25952595
| }
25962596
| },
2597-
| "round_identifier": {
2597+
| "__c10": {
25982598
| "script": {
25992599
| "lang": "painless",
26002600
| "source": "def param1 = (doc['identifier'].size() == 0 ? null : doc['identifier'].value); def param2 = Math.pow(10, 0); (param1 == null || param2 == null) ? null : Long.valueOf(Math.round((param1 * param2) / param2))"
26012601
| }
26022602
| },
2603-
| "round_identifier_2": {
2603+
| "__c11": {
26042604
| "script": {
26052605
| "lang": "painless",
26062606
| "source": "def param1 = (doc['identifier'].size() == 0 ? null : doc['identifier'].value); def param2 = Math.pow(10, 2); (param1 == null || param2 == null) ? null : Long.valueOf(Math.round((param1 * param2) / param2))"
26072607
| }
26082608
| },
2609-
| "sign_identifier": {
2609+
| "__c12": {
26102610
| "script": {
26112611
| "lang": "painless",
26122612
| "source": "def param1 = (doc['identifier'].size() == 0 ? null : doc['identifier'].value); (param1 == null) ? null : (param1 > 0 ? 1 : (param1 < 0 ? -1 : 0))"
26132613
| }
26142614
| },
2615-
| "cos_identifier": {
2615+
| "__c13": {
26162616
| "script": {
26172617
| "lang": "painless",
26182618
| "source": "def param1 = (doc['identifier'].size() == 0 ? null : doc['identifier'].value); (param1 == null) ? null : Double.valueOf(Math.cos(param1))"
26192619
| }
26202620
| },
2621-
| "acos_identifier": {
2621+
| "__c14": {
26222622
| "script": {
26232623
| "lang": "painless",
26242624
| "source": "def param1 = (doc['identifier'].size() == 0 ? null : doc['identifier'].value); (param1 == null) ? null : Double.valueOf(Math.acos(param1))"
26252625
| }
26262626
| },
2627-
| "sin_identifier": {
2627+
| "__c15": {
26282628
| "script": {
26292629
| "lang": "painless",
26302630
| "source": "def param1 = (doc['identifier'].size() == 0 ? null : doc['identifier'].value); (param1 == null) ? null : Double.valueOf(Math.sin(param1))"
26312631
| }
26322632
| },
2633-
| "asin_identifier": {
2633+
| "__c16": {
26342634
| "script": {
26352635
| "lang": "painless",
26362636
| "source": "def param1 = (doc['identifier'].size() == 0 ? null : doc['identifier'].value); (param1 == null) ? null : Double.valueOf(Math.asin(param1))"
26372637
| }
26382638
| },
2639-
| "tan_identifier": {
2639+
| "__c17": {
26402640
| "script": {
26412641
| "lang": "painless",
26422642
| "source": "def param1 = (doc['identifier'].size() == 0 ? null : doc['identifier'].value); (param1 == null) ? null : Double.valueOf(Math.tan(param1))"
26432643
| }
26442644
| },
2645-
| "atan_identifier": {
2645+
| "__c18": {
26462646
| "script": {
26472647
| "lang": "painless",
26482648
| "source": "def param1 = (doc['identifier'].size() == 0 ? null : doc['identifier'].value); (param1 == null) ? null : Double.valueOf(Math.atan(param1))"
26492649
| }
26502650
| },
2651-
| "atan2_identifier_3_0": {
2651+
| "__c19": {
26522652
| "script": {
26532653
| "lang": "painless",
26542654
| "source": "def param1 = (doc['identifier'].size() == 0 ? null : doc['identifier'].value); (param1 == null) ? null : Double.valueOf(Math.atan2(param1, 3.0))"

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ ThisBuild / organization := "app.softnetwork"
2020

2121
name := "softclient4es"
2222

23-
ThisBuild / version := "0.17.4"
23+
ThisBuild / version := "0.18.0"
2424

2525
ThisBuild / scalaVersion := scala213
2626

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

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -449,16 +449,7 @@ trait ElasticConversion {
449449
wrapperAggs.flatMap { entry =>
450450
val aggName = normalizeAggregationKey(entry.getKey)
451451
val aggValue = entry.getValue
452-
val docCount = Option(aggValue.get("doc_count"))
453-
.map(_.asLong())
454-
.getOrElse(0L)
455-
456-
// Add the doc_count to the context if necessary
457-
val currentContext = if (docCount > 0) {
458-
parentContext + (s"${aggName}_doc_count" -> docCount)
459-
} else {
460-
parentContext
461-
}
452+
val currentContext = parentContext
462453

463454
// Extract subaggregations (excluding doc_count)
464455
val subAggsNode = mapper.createObjectNode()
@@ -502,13 +493,9 @@ trait ElasticConversion {
502493
val allTopHits = extractAllTopHits(bucket, fieldAliases, aggregations)
503494

504495
val bucketKey = extractBucketKey(bucket)
505-
val docCount = Option(bucket.get("doc_count"))
506-
.map(_.asLong())
507-
.getOrElse(0L)
508496

509497
val currentContext = parentContext ++ ListMap(
510-
aggName -> bucketKey,
511-
s"${aggName}_doc_count" -> docCount
498+
aggName -> bucketKey
512499
) ++ metrics ++ allTopHits
513500

514501
// Check for sub-aggregations

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1675,6 +1675,7 @@ trait GatewayApi extends IndicesApi with ElasticClientHelpers {
16751675
// ========================================================================
16761676

16771677
def run(sql: String)(implicit system: ActorSystem): Future[ElasticResult[QueryResult]] = {
1678+
logger.info(s"📥 SQL: $sql")
16781679
val normalizedQuery =
16791680
sql
16801681
.split("\n")

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ trait SearchApi extends ElasticConversion with ElasticClientHelpers {
6161
* alias if present, otherwise the source field name. Returns empty Seq for SELECT * queries.
6262
*/
6363
protected def extractOutputFieldNames(single: SingleSearch): Seq[String] = {
64-
val fields = single.select.fields
64+
val fields = single.select.fieldsWithComputedAliases
6565
if (fields.size == 1 && fields.head.identifier.identifierName == "*") Seq.empty
6666
else fields.map(f => f.fieldAlias.map(_.alias).getOrElse(f.sourceField))
6767
}

0 commit comments

Comments
 (0)