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.
| Elasticsearch | Artifact |
|---|---|
| ES 6.x | softclient4es6-adbc-driver-0.2.4.jar |
| ES 7.x | softclient4es7-adbc-driver-0.2.4.jar |
| ES 8.x | softclient4es8-adbc-driver-0.2.4.jar |
| ES 9.x | softclient4es9-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 / passwordadbc:elastic://host:port?user=elastic&password=changeme
# API keyadbc:elastic://host:port?api-key=your-api-key
# Bearer tokenadbc:elastic://host:port?bearer=your-tokenimport org.apache.arrow.adbc.core._import org.apache.arrow.adbc.drivermanager.AdbcDriverManagerimport 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
| Feature | JDBC | ADBC | Arrow Flight SQL |
|---|---|---|---|
| Process model | In-process | In-process | Separate server (gRPC) |
| Data format | Row-based (ResultSet) | Columnar (Arrow) | Columnar (Arrow) |
| Protocol | JDBC API | ADBC API | gRPC (HTTP/2) |
| Use case | Java apps, BI tools | Analytics, data engineering | Multi-client, networked |
| Setup | JAR on classpath | JAR on classpath | Docker/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
| Driver | Scala | ES versions | Clients | Process model |
|---|---|---|---|---|
| ADBC | cross-built Scala 2.12 + 2.13 | ES 6.x / 7.x / 8.x / 9.x | Java/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
- JDBC Driver — the row-based in-process alternative for any JDBC tool.
- Arrow Flight SQL — the gRPC server for polyglot and networked clients.
- Cross-Index JOIN walkthrough — the full JOIN matrix (rows 1/2/3) with worked examples.
- Multi-cluster federation operator guide — the Pro+ path: JOIN across separate ES clusters.
- Known Limitations & Roadmap — what works in the current release vs what’s coming.
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.