How does RethinkDB's ReQL query language differ from sending SQL query strings?
answer
- Not a string language
- Chained methods in your own code
- One call actually contacts the server
- Query object is a reusable value
- run(conn) executes; cursors stream
basics
~20 sReQL is embedded in the host language: you chain driver methods to build a query object, then call run(connection) to execute it. Nothing is sent as a string, and nothing runs until run() is called.
solid answer
~40 sReQL is RethinkDB's query language, but it is not a string language — the official drivers (JavaScript, Python, Ruby, Java) expose it as chained method calls, so `r.table('players').filter(r.row('score').gt(100)).limit(5)` is an ordinary object in your program. The driver serializes that query tree and sends it to the server only when you call `run(conn)`. Two practical consequences follow. First, queries are **composable values**: you can hold a partial query in a variable, add `filter` or `orderBy` branches conditionally, and pass it around like any other object. Second, there is no string concatenation step, so the classic "build SQL by gluing user input into text" injection shape does not arise. `run()` returns the document for a point query, or a cursor you iterate for a stream.
code
javascript · 8 lines// The chain builds a query; nothing runs yet
const top = r.table('players')
.filter(r.row('score').gt(100))
.orderBy('name')
.limit(5);
const cursor = await top.run(conn);
const rows = await cursor.toArray();go deeper
Be ready to say that a ReQL query is built by chaining driver methods and only executes when you call run(connection). Recognizing that syntax in a snippet is most of what is asked.
Explain the mechanics: the driver serializes a query tree and sends it over the wire, run() returns a document or a streaming cursor, and partial queries are values you can compose conditionally.
Show judgment about keeping work server-side with native ReQL terms, streaming through cursors instead of materializing results, and treating r.js as a slow escape hatch rather than a normal tool.
Own the portability tradeoff: an embedded DSL buys composability and tooling from your own language but locks query logic to one product's drivers and cuts you off from the SQL ecosystem of analytics and migration tools.
## What ReQL is ReQL ("RethinkDB Query Language") is the only query interface RethinkDB offers. Unlike SQL, which is a textual language your program assembles into a string and hands to the server to parse, ReQL is delivered as a **domain-specific language embedded in the host programming language**. The official drivers for JavaScript, Python, Ruby and Java each expose a root object — conventionally named `r` — whose methods build up a query by chaining. ```javascript r.table('players') .filter(r.row('score').gt(100)) .orderBy('name') .limit(5) ``` Every call in that chain returns another ReQL term. At this point nothing has happened: no connection has been used, no rows read. The value is a query *tree* living in your process. ## Building versus running Execution is an explicit, separate step: `query.run(conn)`, where `conn` is a connection to a RethinkDB server. The driver serializes the query tree into the wire protocol, the server evaluates it, and results come back. This build/run split is the single biggest difference from a SQL client, where constructing the statement and executing it are usually the same act. What comes back from `run()` depends on the query. A point query such as `r.table('players').get(id).run(conn)` yields a single document (or null). A query that produces many rows yields a **cursor** — a lazy handle you iterate (`each`, `next`) or drain into an array (`toArray`). Cursors matter because RethinkDB streams: the server does not have to materialize the full result set before the client sees the first row, and the client does not have to hold the whole set in memory. ## Composability Because a query is a value, you can build it the way you build any other data structure: ```javascript let q = r.table('orders'); if (customerId) q = q.getAll(customerId, { index: 'customerId' }); if (onlyOpen) q = q.filter({ status: 'open' }); const cursor = await q.run(conn); ``` In SQL this is the job of a query builder or an ORM layered on top of the language. In ReQL it is the language itself, which is why RethinkDB code tends not to grow a separate query-building abstraction. The same property makes helper functions natural: a function can accept a ReQL term, wrap it in `filter(...)` or `pluck(...)`, and return it, without knowing or caring where the term will eventually be run. ## Naming conventions differ per driver ReQL method names follow the idiom of the host language. The JavaScript driver uses camelCase (`orderBy`, `getAll`, `indexCreate`, and options like `includeInitial`); the Python driver uses snake_case (`order_by`, `get_all`, `index_create`, `include_initial`). The underlying terms are identical — only the surface spelling changes — so documentation and examples translate mechanically between drivers. ## Server-side expressions ReQL is expressive enough to keep work on the server rather than pulling documents to the client: `filter`, `map`, `group`, `reduce`, `eqJoin` and `innerJoin`, nested-field access, and updates written as functions of the existing document (`update(row => ...)`). For logic that ReQL cannot express directly, `r.js(...)` lets you embed a JavaScript expression the server evaluates — powerful, but slower than native terms and worth avoiding on hot paths. ## Why the design matters The practical payoffs are composability, type-checking and autocompletion from your own language's tooling, and the absence of a string-assembly step. The costs are real too: you learn a per-driver API instead of a portable standard, moving to another database means rewriting rather than re-pointing your queries, and generic tooling that speaks SQL — BI dashboards, migration tools, query analyzers — does not speak ReQL at all. For an interview, the point to land is the build/run separation and its consequences, not a recital of method names.
- What does run() give you back for a query that matches thousands of documents?A cursor, not a materialized array. You iterate it with `each`/`next`, or call `toArray()` to drain it into memory. The cursor streams batches from the server, so the client can start processing before the whole result set exists and can stop early by closing the cursor without paying for the rest.
- If ReQL is built in the host language, how do you express logic ReQL has no term for?`r.js(...)` embeds a JavaScript expression the server evaluates, for example inside a `filter` or `map`. It is a genuine escape hatch, but it is much slower than native ReQL terms and cannot use indexes, so it belongs in one-off work rather than in a hot query path.
saying these in an interview costs you the question
- Calling ReQL a SQL dialect the server parses
- Thinking the chain executes as each method is called
- Assuming results always arrive as a full array
- Claiming ReQL cannot express joins or aggregations
- Believing camelCase versus snake_case are different languages