Skip to content

ADBC Driver

Arrow-native, in-process columnar access to Elasticsearch. Same SQL as JDBC (including cross-index JOIN), but your consuming code receives native Arrow VectorSchemaRoot batches — no row-materialization, no JSON. The SoftClient4ES ADBC driver is Java/JVM in-process; polyglot clients (Python, Go, DuckDB, C++) connect to the Arrow Flight SQL server instead.

Being Arrow-native, the ADBC driver requires Java 11+ at runtime (Apache Arrow 18.x ships Java-11 bytecode).

Connection setup

Driver JARs

Download the self-contained fat JAR for your Elasticsearch version. The JARs are Scala-version-independent and include all required dependencies.

ElasticsearchArtifact
ES 6.xsoftclient4es6-adbc-driver-0.2.4.jar
ES 7.xsoftclient4es7-adbc-driver-0.2.4.jar
ES 8.xsoftclient4es8-adbc-driver-0.2.4.jar
ES 9.xsoftclient4es9-adbc-driver-0.2.4.jar

Build Tool Integration

<dependency>
<groupId>app.softnetwork.elastic</groupId>
<artifactId>softclient4es8-adbc-driver</artifactId>
<version>0.2.4</version>
</dependency>
implementation 'app.softnetwork.elastic:softclient4es8-adbc-driver:0.2.4'
libraryDependencies += "app.softnetwork.elastic" % "softclient4es8-adbc-driver" % "0.2.4"

Connection URI

The driver is reached through the standard AdbcDriverManager; the connection URI is adbc:elastic://host:port[?params], set via AdbcDriver.PARAM_URI:

# Username / password
adbc:elastic://host:port?user=elastic&password=changeme
# API key
adbc:elastic://host:port?api-key=your-api-key
# Bearer token
adbc:elastic://host:port?bearer=your-token
import org.apache.arrow.adbc.core._
import org.apache.arrow.adbc.drivermanager.AdbcDriverManager
import org.apache.arrow.memory.RootAllocator
val allocator = new RootAllocator()
val params = new java.util.HashMap[String, AnyRef]()
params.put(AdbcDriver.PARAM_URI.getKey,
"adbc:elastic://localhost:9200?user=elastic&password=changeme")
val db = AdbcDriverManager.getInstance().connect(params, allocator)
val conn = db.connect()
val stmt = conn.createStatement()

Your first query

A plain SELECT reading the Arrow stream is the fastest way to confirm the connection works:

stmt.setSqlQuery("SELECT * FROM my_index LIMIT 10")
val result = stmt.executeQuery()
val reader = result.getReader
while (reader.loadNextBatch()) {
val root = reader.getVectorSchemaRoot
// Process Arrow columnar data...
}
reader.close(); stmt.close(); conn.close(); db.close(); allocator.close()

Your first JOIN

Elasticsearch SQL can’t JOIN across indices — SoftClient4ES does. Free in Community: up to 2 cross-index JOINs per query (a 3-table JOIN). ADBC routes the JOIN through the same embedded DuckDB engine as the JDBC driver, so the SQL is identical — only the connection setup differs:

stmt.setSqlQuery("""
SELECT e.name, e.salary, d.dept_name
FROM jdbc_join_emp e
JOIN jdbc_join_dept d ON e.dept_id = d.dept_id
""")
val result = stmt.executeQuery()
// 5 rows (the orphan employee with dept_id = 99 is dropped by the INNER JOIN)

For the full JOIN matrix — passthrough, cross-cluster conveyor, and the multi-source coordinator — see the Cross-Index JOIN walkthrough.

Arrow-native

ADBC delivers Elasticsearch results directly as columnar Arrow VectorSchemaRoot batches: there is no row-by-row materialization and no JSON handed to the consuming code. That keeps the data in the same in-memory format your analytics layer already speaks (DuckDB, pandas/PyArrow, Polars), so there is no format translation between the driver and your computation.

ADBC is the standard in modern data platforms — adopted by Databricks, DuckDB, and the Python analytics ecosystem.

ADBC vs JDBC vs Arrow Flight SQL

FeatureJDBCADBCArrow Flight SQL
Process modelIn-processIn-processSeparate server (gRPC)
Data formatRow-based (ResultSet)Columnar (Arrow)Columnar (Arrow)
ProtocolJDBC APIADBC APIgRPC (HTTP/2)
Use caseJava apps, BI toolsAnalytics, data engineeringMulti-client, networked
SetupJAR on classpathJAR on classpathDocker/server deployment

Configuration

adbc.elastic {
batch-size = 1000 # env: ADBC_BATCH_SIZE
query-timeout-seconds = 120 # env: ADBC_QUERY_TIMEOUT
}
elastic.credentials {
host = "localhost" # env: ES_HOST
port = 9200 # env: ES_PORT
user = "elastic" # env: ES_USER
password = "changeme" # env: ES_PASSWORD
}

Version compatibility

DriverScalaES versionsClientsProcess model
ADBCcross-built Scala 2.12 + 2.13ES 6.x / 7.x / 8.x / 9.xJava/JVM only (polyglot → Flight SQL)In-process

The fat JARs are Scala-version-independent for consumers — they bundle their own Scala runtime, so you almost never need to think about the Scala axis. For non-JVM languages (Python, Go, DuckDB, C++), use the Arrow Flight SQL server with the Arrow project’s standard adbc_driver_flightsql client.

Licensing & self-selection

All client drivers (JDBC, ADBC, and the REPL) plus the Arrow Flight SQL sidecar are free in Community, including up to 2 cross-index JOINs per query (a 3-table JOIN). A 4-table JOIN (a 3rd cross-index JOIN in one query) is rejected by the planner with a message ending … Upgrade to Pro … See: https://portal.softclient4es.com/pricing.

Multi-cluster federation — joining across separate ES clusters — is Pro+ (maxClusters 1 / 5 / ∞). This quickstart covers the single-cluster shape, which is free. See the pricing & licensing page for the full quota matrix.

What does NOT work yet

Subqueries (IN (SELECT …), EXISTS, scalar, derived tables) and CTEs (WITH) are not supported in the current release — they arrive in a later release. Write the JOIN explicitly instead. See Known Limitations & Roadmap for the full list.

Going further

Telemetry

The ADBC driver sends one anonymous usage ping per day (no IP, no SQL, no PII). Opt out with softclient4es.telemetry.enabled = false in your HOCON config, the SOFTCLIENT4ES_TELEMETRY_ENABLED=false environment variable, or -Dsoftclient4es.telemetry.enabled=false. ADBC has no connection-string opt-out — the adbc:elastic:// URI carries no telemetry option. See Telemetry & Privacy for the full field list and per-surface details.

License

The ADBC driver is licensed under the Elastic License 2.0 — free to use, not open source.