Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -20,7 +20,7 @@ ThisBuild / organization := "app.softnetwork"

name := "softclient4es"

ThisBuild / version := "0.19.2"
ThisBuild / version := "0.20-SNAPSHOT"

ThisBuild / scalaVersion := scala213

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2025 SOFTNETWORK
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package app.softnetwork.elastic.client

import app.softnetwork.elastic.client.result.{ElasticFailure, ElasticResult, ElasticSuccess}

import java.util.concurrent.atomic.AtomicReference

trait ClusterApi extends ElasticClientHelpers {

// ========================================================================
// PUBLIC METHODS
// ========================================================================

// Cache cluster name (avoids calling it every time)
private val cachedClusterName = new AtomicReference[Option[String]](None)

/** Get Elasticsearch cluster name.
* @return
* the Elasticsearch cluster name
*/
def clusterName: ElasticResult[String] = {
cachedClusterName.get match {
case Some(name) =>
ElasticSuccess(name)
case None =>
executeGetClusterName() match {
case ElasticSuccess(name) =>
logger.info(s"✅ Elasticsearch cluster name: $name")
cachedClusterName.compareAndSet(None, Some(name))
ElasticSuccess(cachedClusterName.get.getOrElse(name))
case failure @ ElasticFailure(error) =>
logger.error(s"❌ Failed to get Elasticsearch cluster name: ${error.message}")
failure
}
}
}

// ========================================================================
// METHODS TO IMPLEMENT
// ========================================================================

private[client] def executeGetClusterName(): ElasticResult[String]
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ trait ElasticClientApi
with RefreshApi
with FlushApi
with VersionApi
with ClusterApi
with PipelineApi
with TemplateApi
with EnrichPolicyApi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,17 @@ trait ElasticClientDelegator extends ElasticClientApi with BulkTypes {
override private[client] def executeVersion(): ElasticResult[String] =
delegate.executeVersion()

/** Get Elasticsearch cluster name.
*
* @return
* the Elasticsearch cluster name
*/
override def clusterName: ElasticResult[String] =
delegate.clusterName

override private[client] def executeGetClusterName(): ElasticResult[String] =
delegate.executeGetClusterName()

// ==================== IndicesApi ====================

/** Create an index with the provided name and settings.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import app.softnetwork.elastic.sql.parser.Parser
import app.softnetwork.elastic.sql.query.{
AlterTable,
AlterTableSetting,
ClusterStatement,
CopyInto,
CreateEnrichPolicy,
CreatePipeline,
Expand All @@ -59,6 +60,7 @@ import app.softnetwork.elastic.sql.query.{
PipelineStatement,
SearchStatement,
SelectStatement,
ShowClusterName,
ShowCreatePipeline,
ShowCreateTable,
ShowEnrichPolicies,
Expand Down Expand Up @@ -1570,12 +1572,38 @@ class TableExecutor(
}
}

class ClusterExecutor(
api: ClusterApi,
logger: Logger
) extends Executor[ClusterStatement] {
override def execute(
statement: ClusterStatement
)(implicit system: ActorSystem): Future[ElasticResult[QueryResult]] = {
statement match {
case ShowClusterName =>
api.clusterName match {
case ElasticSuccess(name) =>
Future.successful(
ElasticResult.success(QueryRows(Seq(ListMap("name" -> name))))
)
case ElasticFailure(elasticError) =>
Future.successful(
ElasticFailure(
elasticError.copy(operation = Some("cluster"))
)
)
}
}
}
}

class DqlRouterExecutor(
searchExec: SearchExecutor,
pipelineExec: PipelineExecutor,
tableExec: TableExecutor,
watcherExec: WatcherExecutor,
policyExec: EnrichPolicyExecutor
policyExec: EnrichPolicyExecutor,
clusterExec: ClusterExecutor
) extends Executor[DqlStatement] {

override def execute(
Expand All @@ -1587,6 +1615,7 @@ class DqlRouterExecutor(
case t: TableStatement => tableExec.execute(t)
case w: WatcherStatement => watcherExec.execute(w)
case e: EnrichPolicyStatement => policyExec.execute(e)
case c: ClusterStatement => clusterExec.execute(c)

case _ =>
Future.successful(
Expand Down Expand Up @@ -1655,12 +1684,18 @@ trait GatewayApi extends IndicesApi with ElasticClientHelpers {
logger = logger
)

lazy val clusterExecutor = new ClusterExecutor(
api = this,
logger = logger
)

lazy val dqlExecutor = new DqlRouterExecutor(
searchExec = searchExecutor,
pipelineExec = pipelineExecutor,
tableExec = tableExecutor,
watcherExec = watcherExecutor,
policyExec = policyExecutor
policyExec = policyExecutor,
clusterExec = clusterExecutor
)

lazy val ddlExecutor = new DdlRouterExecutor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,9 @@ trait NopeClientApi extends ElasticClientApi {
override private[client] def executeVersion(): ElasticResult[String] =
ElasticResult.success("0.0.0")

override private[client] def executeGetClusterName(): ElasticResult[String] =
ElasticResult.success("nope-cluster")

override private[client] def executeGetIndex(index: String): ElasticResult[Option[String]] =
ElasticResult.success(None)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ class MetricsElasticClient(
delegate.version
}

// ==================== ClusterApi ====================

override def clusterName: ElasticResult[String] =
measureResult("cluster_name") {
delegate.clusterName
}

// ==================== IndicesApi ====================

override def createIndex(
Expand Down
22 changes: 22 additions & 0 deletions documentation/sql/dql_statements.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ DQL supports:
- [SHOW WATCHER STATUS](#show-watcher-status)
- [SHOW ENRICH POLICIES](#show-enrich-policies)
- [SHOW ENRICH POLICY](#show-enrich-policy)
- [SHOW CLUSTER NAME](#show-cluster-name)

---

Expand Down Expand Up @@ -1282,4 +1283,25 @@ SHOW ENRICH POLICY my_policy;

---

## SHOW CLUSTER NAME

```sql
SHOW CLUSTER NAME;
```

Returns the name of the Elasticsearch cluster. The cluster name is cached after the first call.

**Example:**

```sql
SHOW CLUSTER NAME;
```

| name |
|----------------|
| docker-cluster |
📊 1 row(s) (3ms)

---

[Back to index](README.md)
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ trait JestClientApi
with JestScrollApi
with JestBulkApi
with JestVersionApi
with JestClusterApi
with JestPipelineApi
with JestTemplateApi
with JestEnrichPolicyApi
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2025 SOFTNETWORK
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package app.softnetwork.elastic.client.jest

import app.softnetwork.elastic.client.{result, ClusterApi}
import app.softnetwork.elastic.client.jest.actions.GetClusterInfo
import org.json4s.DefaultFormats
import org.json4s.jackson.JsonMethods

trait JestClusterApi extends ClusterApi with JestClientHelpers {
_: JestClientCompanion =>
override private[client] def executeGetClusterName(): result.ElasticResult[String] =
executeJestAction(
"cluster_name",
retryable = true
)(
new GetClusterInfo.Builder().build()
) { result =>
val jsonString = result.getJsonString
implicit val formats: DefaultFormats.type = DefaultFormats
val json = JsonMethods.parse(jsonString)
(json \ "cluster_name").extract[String]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2025 SOFTNETWORK
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package app.softnetwork.elastic.client.jest.actions

import io.searchbox.action.AbstractAction
import io.searchbox.action.GenericResultAbstractAction
import io.searchbox.client.config.ElasticsearchVersion

object GetClusterInfo {
class Builder extends AbstractAction.Builder[GetClusterInfo, GetClusterInfo.Builder] {
override def build = new GetClusterInfo(this)
}
}

class GetClusterInfo protected (builder: GetClusterInfo.Builder)
extends GenericResultAbstractAction(builder) {
override def getRestMethodName = "GET"

override def buildURI(elasticsearchVersion: ElasticsearchVersion): String = "/"
}
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ trait RestHighLevelClientApi
with RestHighLevelClientScrollApi
with RestHighLevelClientCompanion
with RestHighLevelClientVersionApi
with RestHighLevelClientClusterApi
with RestHighLevelClientPipelineApi
with RestHighLevelClientTemplateApi
with RestHighLevelClientEnrichPolicyApi
Expand Down Expand Up @@ -179,6 +180,27 @@ trait RestHighLevelClientVersionApi extends VersionApi with RestHighLevelClientH
)
}

trait RestHighLevelClientClusterApi extends ClusterApi with RestHighLevelClientHelpers {
_: RestHighLevelClientCompanion =>

override private[client] def executeGetClusterName(): ElasticResult[String] =
executeRestLowLevelAction[String](
operation = "cluster_name",
index = None,
retryable = true
)(
request = new Request("GET", "/")
)(
transformer = resp => {
val jsonString = EntityUtils.toString(resp.getEntity)
implicit val formats: DefaultFormats.type = DefaultFormats
val json = JsonMethods.parse(jsonString)
(json \ "cluster_name").extract[String]
}
)

}

/** Indices management API for RestHighLevelClient
* @see
* [[IndicesApi]] for generic API documentation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ trait RestHighLevelClientApi
with RestHighLevelClientScrollApi
with RestHighLevelClientCompanion
with RestHighLevelClientVersionApi
with RestHighLevelClientClusterApi
with RestHighLevelClientPipelineApi
with RestHighLevelClientTemplateApi
with RestHighLevelClientEnrichPolicyApi
Expand Down Expand Up @@ -237,6 +238,27 @@ trait RestHighLevelClientVersionApi extends VersionApi with RestHighLevelClientH

}

trait RestHighLevelClientClusterApi extends ClusterApi with RestHighLevelClientHelpers {
_: RestHighLevelClientCompanion =>

override private[client] def executeGetClusterName(): ElasticResult[String] =
executeRestLowLevelAction[String](
operation = "cluster_name",
index = None,
retryable = true
)(
request = new Request("GET", "/")
)(
transformer = resp => {
val jsonString = EntityUtils.toString(resp.getEntity)
implicit val formats: DefaultFormats.type = DefaultFormats
val json = JsonMethods.parse(jsonString)
(json \ "cluster_name").extract[String]
}
)

}

/** Indices management API for RestHighLevelClient
* @see
* [[IndicesApi]] for generic API documentation
Expand Down
Loading
Loading