Skip to content

Quick Start ​

The examples assume a q process listening on localhost:1800, for example one started with q -p 1800. They define their own trade table, so any empty q session works.

Query, call, and send ​

python
import narwhals as nw
import pyarrow as pa
import xqdb

conn = xqdb.Q("localhost", 1800)  # PyArrow backend; connects on first use
try:
    conn.sync("trade:([] sym:`AAPL`MSFT; price:10.5 11.0; size:100 200)")

    # A q table arrives as a Narwhals DataFrame.
    frame = conn.sync("select from trade where price > 10")
    print(nw.to_native(frame))  # pyarrow.Table

    # Functional call: q applies the lambda to the argument. str becomes a symbol.
    total = conn.sync("{[s] exec sum size from trade where sym=s}", "AAPL")
    print(total)  # 100

    # Send a frame. Dictionary-encoded strings become a q symbol column.
    rows = pa.table({
        "sym": pa.array(["GOOG"]).dictionary_encode(),
        "price": [12.25],
        "size": [300],
    })
    conn.sync("insert", "trade", rows)

    # Asynchronous message: returns once written; q sends no reply.
    conn.asyn("insert", "trade", rows)
finally:
    conn.disconnect()
ts
import { Q } from "@xbbg/xqdb";
import { Table } from "apache-arrow";

const q = await Q.connect({ host: "localhost", port: 1800 });
try {
  await q.sync("trade:([] sym:`AAPL`MSFT; price:10.5 11.0; size:100 200)");

  // A q table arrives as an apache-arrow Table.
  const result = await q.sync("select from trade where price > 10");
  if (result instanceof Table) {
    console.log(result.numRows, result.schema.fields.map((field) => field.name));
  }

  // Functional call: string becomes a symbol; q longs come back as bigint.
  const total = await q.sync("{[s] exec sum size from trade where sym=s}", "AAPL");
  console.log(total); // 100n

  // Send one row as a mixed list: symbol, float, long.
  await q.sync("insert", "trade", ["GOOG", 12.25, 300n]);

  // Asynchronous message: resolves once written; q sends no reply.
  await q.asyn("insert", "trade", ["GOOG", 12.5, 100n]);
} finally {
  await q.disconnect();
}

What happened ​

  1. 01Connect

    Python opens the socket on the first query. Q.connect() in Node.js connects before it resolves. Both perform the q handshake with an empty username and password.

  2. 02Query

    sync(expression) sends a q string for evaluation and waits for the reply. The table becomes a DataFrame in Python and an Arrow Table in Node.js.

  3. 03Call

    sync(function, ...args) sends a functional call with up to eight arguments. A {...} lambda string is sent as a q lambda.

  4. 04Send

    Arguments are converted by the type mapping. asyn returns once the message is written, without a reply.

Two conversions surprise most new users:

  • Strings are symbols. A Python str or JavaScript string argument becomes a q symbol. To send a q string (a char vector), pass bytes in Python or a Buffer/Uint8Array in Node.js.
  • Numbers are floats in JavaScript. A JavaScript number becomes a q float. Use a bigint such as 300n for a q long.

A plain string column, such as Arrow string, pandas object, or Polars String, becomes a general list of q strings rather than a symbol column. Dictionary-encode or categorize the column to send symbols. Arrow JS already dictionary-encodes strings by default. See DataFrames and Arrow.

Choose a DataFrame backend (Python) ​

python
import narwhals as nw
import xqdb

conn = xqdb.Q("localhost", 1800, backend="polars")  # or "pandas"; default "pyarrow"
frame = conn.sync("([] x:1 2 3)")
print(type(nw.to_native(frame)))  # polars.DataFrame
conn.backend = "pyarrow"  # switch for later results
conn.disconnect()

Next steps ​

xbbg is independent open-source software and is not affiliated with, endorsed by, sponsored by, or approved by Bloomberg Finance L.P.