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.3.3.jar |
| ES 7.x | softclient4es7-adbc-driver-0.3.3.jar |
| ES 8.x | softclient4es8-adbc-driver-0.3.3.jar |
| ES 9.x | softclient4es9-adbc-driver-0.3.3.jar |
Build Tool Integration
<dependency> <groupId>app.softnetwork.elastic</groupId> <artifactId>softclient4es8-adbc-driver</artifactId> <version>0.3.3</version></dependency>implementation 'app.softnetwork.elastic:softclient4es8-adbc-driver:0.3.3'libraryDependencies += "app.softnetwork.elastic" % "softclient4es8-adbc-driver" % "0.3.3"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 |
JDK 16 and later: one required flag
The ADBC driver is Arrow-native in process, and Apache Arrow reaches into java.nio to address off-heap memory. JDK 16 and later deny that access by default (JEP 396), so a modern JVM must be started with:
--add-opens=java.base/java.nio=ALL-UNNAMEDUnlike the JDBC driver — where only the cross-index JOIN goes through Arrow — ADBC hands back Arrow batches for every query, so the flag is a prerequisite for the driver as a whole, not for one feature.
Below JDK 16 no flag is needed, but the floor is JDK 11 — the Arrow release bundled here is compiled for Java 11, which is also what this page’s requirements state.
Without the flag, Arrow’s allocator constructs successfully and then fails on the first vector it fills, reporting:
Failed to initialize MemoryUtil. You must start Java with`--add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED`(See https://arrow.apache.org/docs/java/install.html)Arrow’s own message names its module first, which matters when Arrow is on the module path. On a classpath deployment — which is how these fat JARs ship — --add-opens=java.base/java.nio=ALL-UNNAMED alone is sufficient; both forms work.
In a JVM you do not launch yourself (an application server, a notebook kernel, a BI tool) the same flag can be supplied through the JAVA_TOOL_OPTIONS environment variable, or through whatever mechanism that host provides for its own JVM arguments. The per-tool procedure has not yet been verified for any specific host, so this page does not print one.
Configuration
adbc.elastic { batch-size = 1000 # env: ADBC_BATCH_SIZE query-timeout-seconds = 120 # env: ADBC_QUERY_TIMEOUT}
elastic.credentials { host = "localhost" # env: ELASTIC_HOST port = 9200 # env: ELASTIC_PORT username = "elastic" # env: ELASTIC_CREDENTIALS_USERNAME password = "changeme" # env: ELASTIC_CREDENTIALS_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.