diff --git a/connections/connection-form.mdx b/connections/connection-form.mdx index 56ced1b..7d46986 100644 --- a/connections/connection-form.mdx +++ b/connections/connection-form.mdx @@ -199,6 +199,7 @@ Metadata connections are the extra ones opened to read a database's object list | [Elasticsearch](/databases/elasticsearch) | 9200 | Yes | Yes | Yes | No | Yes | Yes | | [Typesense](/databases/typesense) | 8108 | Yes | Yes | Yes | No | Yes | Yes | | [Weaviate](/databases/weaviate) | 8080 | Yes | Yes | Yes | No | Yes | Yes | +| [Qdrant](/databases/qdrant) | 6333 | Yes | Yes | Yes | No | Yes | Yes | | [SAP HANA](/databases/sap-hana) | 443 | No | Yes | No | No | No | No | | [Snowflake](/databases/snowflake) | 443 | No | No | No | No | No | No | | [SQLite](/databases/sqlite) | File | No | No | No | No | No | No | diff --git a/connections/ssl.mdx b/connections/ssl.mdx index 1eb790a..a73811d 100644 --- a/connections/ssl.mdx +++ b/connections/ssl.mdx @@ -26,7 +26,7 @@ The certificate rows appear only once **SSL Mode** is anything but Disabled. Eac | Field | When it appears | What it takes | |---|---|---| -| **Certificate**, under **CA Certificate** | Verify CA and Verify Identity only | The PEM or DER certificate the server's certificate chains to, for example `/path/to/ca-cert.pem`. Saving without it is blocked, except under Verify Identity on ClickHouse, Kafka, Presto, SAP HANA and Trino, which check the system trust store when it is empty | +| **Certificate**, under **CA Certificate** | Verify CA and Verify Identity only | The PEM or DER certificate the server's certificate chains to, for example `/path/to/ca-cert.pem`. Saving without it is blocked, except under Verify Identity on ClickHouse, Kafka, Presto, Qdrant, SAP HANA and Trino, which check the system trust store when it is empty | | **Client Certificate** | Any mode but Disabled | Your certificate, for a server that enforces mutual TLS. Optional otherwise | | **Client Key** | Any mode but Disabled | The matching private key. Required once a client certificate is set | | **Key Passphrase** | Once **Client Key** has a path, on drivers that support it (Cassandra, ScyllaDB and MongoDB) | The passphrase of an encrypted key. Stored in the Keychain | @@ -43,7 +43,7 @@ A new connection starts on the mode that matches the driver's own default, and s | MySQL, MariaDB | Preferred | TLS when the server offers it, plain when it does not, and a plain retry after an SSL handshake error. Auth and network errors are not retried | | SQL Server | Preferred | FreeTDS `encryption=request`: the login over TLS, the rest in plain TCP unless the server forces encryption | | Teradata | Disabled | Opens a TLS transport, retries on a plain socket if it fails to come up | -| MongoDB, Redis, Cassandra, ClickHouse, Elasticsearch, Typesense, SurrealDB, Weaviate | Disabled | Nothing. No fallback exists, so Preferred forces TLS exactly like Required | +| MongoDB, Redis, Cassandra, ClickHouse, Elasticsearch, Typesense, SurrealDB, Weaviate, Qdrant | Disabled | Nothing. No fallback exists, so Preferred forces TLS exactly like Required | | etcd | Disabled | Nothing. The driver never reads these fields. Set **TLS Mode** on the Options section instead, and see [etcd](/databases/etcd) | | Trino, Presto | Disabled | Sends every request over HTTPS with no fallback, again like Required | | SAP HANA | Verify Identity | Nothing. No fallback exists, so Preferred forces TLS exactly like Required | diff --git a/databases/index.mdx b/databases/index.mdx index ba17ca4..85d42a6 100644 --- a/databases/index.mdx +++ b/databases/index.mdx @@ -1,11 +1,11 @@ --- title: Supported Databases -description: All 44 engines TablePro connects to, their default ports, and which ones need a plugin +description: All 47 engines TablePro connects to, their default ports, and which ones need a plugin --- import DriverCounts from "/snippets/driver-counts.mdx"; -Forty-four engines, and every one of them is free to use. What differs between them is where the +Forty-seven engines, and every one of them is free to use. What differs between them is where the driver comes from, not what the license covers. @@ -46,6 +46,7 @@ driver comes from, not what the license covers. | [PGlite](/databases/pglite) | 5432 | Built-in | | [PostgreSQL](/databases/postgresql) | 5432 | Built-in | | [Presto](/databases/trino) | 8080 | Plugin | +| [Qdrant](/databases/qdrant) | 6333 | Plugin | | [Redis](/databases/redis) | 6379 | Built-in | | [SAP HANA](/databases/sap-hana) | 443 | Plugin | | [ScyllaDB](/databases/cassandra) | 9042 | Plugin | diff --git a/databases/qdrant.mdx b/databases/qdrant.mdx new file mode 100644 index 0000000..a13bc8d --- /dev/null +++ b/databases/qdrant.mdx @@ -0,0 +1,134 @@ +--- +title: Qdrant +description: Connect to Qdrant, browse collections as points, edit payloads and vectors by id, and run REST requests +--- + +import RegistryPlugin from "/snippets/registry-plugin.mdx"; + +Collections are tables, points are rows, and the point `id` is the primary key. The editor takes Qdrant REST requests, not SQL, so a vector search such as `POST /collections/docs/points/query` runs from the editor and its points and scores land in the grid. + + + +## Quick setup + +Click **New Connection…**, select **Qdrant**, enter host and port, paste an **API Key** if the node needs one, then click **Save & Connect**. + +There is no Database field. One connection reaches one Qdrant instance, and its collections are the objects. + +For a node on a private server, open the **Network** tab and set **Connect via** to **SSH Tunnel**, then set **Host** to the address Qdrant listens on as the SSH server sees it, usually `localhost`. See [SSH Tunneling](/connections/ssh-tunneling). + +## Connection settings + +| Field | Description | +|-------|-------------| +| **Host** | Node hostname. `localhost` for Docker, the cluster URL's host for Qdrant Cloud | +| **Port** | `6333` by default, the REST port. Qdrant Cloud also answers on `6333` | +| **API Key** | Sent as the `api-key` header. Leave it empty for a node started without `QDRANT__SERVICE__API_KEY` | + +Qdrant Cloud requires the API key and HTTPS. Set **SSL Mode** to **Verify Identity** with no CA certificate: the certificate is checked against the system trust store. + +## Browsing collections + +The sidebar lists collections from `GET /collections`. + +Every grid has `id` first and the vectors last. `id` is the primary key and read-only. A collection with one unnamed vector shows a `vector` column. A collection with named or sparse vectors shows one column per name, such as `vector.image`. A dense vector is a JSON number array, a multivector an array of them, and a sparse vector its `indices` and `values`. + +Payload columns are the top-level indexed fields, then the fields found in the first 100 points. Arrays and objects render as JSON in the cell. An index on a nested path such as `metadata.source` or `tags[]` is listed in the **Indexes** section of the **Structure** tab, and its value shows inside the `metadata` or `tags` column. A field that appears only in later points shows up in a query result but not as a grid column. + +Pages load through `POST /points/query` in id order, with the offset applied by Qdrant, so a page always shows the rows at that position now. A node older than 1.10 has no Query API, so its pages walk the point ids from the start. + +## Sorting + +Sort by one payload field that has an `integer`, `float` or `datetime` payload index. Points with a value in that field come first, in order. Points without one follow, by id. Every point stays reachable, and the row count still matches the collection. + +Sorting by `id` ascending is the default order. These sorts are refused with a message: + +- `id` descending, a vector, or more than one column. +- A field that holds a list in any point, since Qdrant would list that point once per value. + +## Filtering + +A payload index does not limit what a field holds, so a value matches every type it can be: `42` finds the text `"42"`, the number 42, and a list holding either. The index still speeds up the part of the filter of its own type. + +| Operators | What Qdrant runs | +|----------|------------------| +| equals, not equals | `match.value` for the text, plus a closed `range` when it is a number and a boolean match for `true`, `false`, `1` or `0` | +| greater than, greater or equal, less than, less or equal, between | `range`, on numbers, or on dates for any other text | +| in list, not in list | Equals for each value, collapsed to `match.any` when every value is plain text | +| contains, not contains | `match.text`, with or without a full-text index. It matches whole words and ignores case, so `world` finds `Hello World` but `lo Wo` finds nothing. A number, a boolean or a vector matches nothing | +| is NULL, is not NULL | `is_empty`: the key is missing, `null`, or an empty list | +| is empty, is not empty | `is_empty`, or the empty string on a text field | + +Not equals, not in list and not contains also match points the key is missing from. On `id`, equals and in list become `has_id`, and contains finds a whole id only. **Starts with**, **ends with** and **REGEX** have no Qdrant equivalent, and a filter Qdrant cannot run reports why instead of returning the whole collection. The raw filter row takes one Qdrant condition as JSON, such as `{"key": "city", "match": {"value": "Berlin"}}`. + +Filtered row counts come from `POST /points/count` with `exact` set. + +## Editing points + +Grid edits are REST requests keyed by `id`, shown in the review sheet before they run. + +| Change | Request | +|--------|---------| +| New row | `POST /collections/{name}/points` first, to check that no point has the id, then `PUT /collections/{name}/points` with an `update_filter` that matches no point, so an id taken in between is left alone. An empty `id` gets a new UUID. Vector cells you fill are stored with the point. Needs Qdrant 1.15 or later, since older nodes ignore the filter and overwrite the point | +| Edited payload cells | `POST /collections/{name}/points/payload`, which sets only the edited keys | +| Edited vector cells | `PUT /collections/{name}/points/vectors`. Setting a vector to NULL sends `POST /collections/{name}/points/vectors/delete` | +| Deleted row | `POST /collections/{name}/points/delete` | + +A cell is written back as the type its column holds: a `keyword` field stays a string even when it reads `42`, and a `bool` field takes `true`, `false`, `1` or `0`. A column whose points hold different types, such as text in one and a number in another, has the type `json`: its text shows in quotes, and a cell saves as the JSON it spells, so `"42"` stays text and `42` a number. A value that does not fit, such as `4.5` in an `integer` column or a vector that is not a number array, stops the save and names the column. **Truncate** deletes every point with an empty filter and keeps the collection. **Drop** removes the collection. + +## REST editor + +Type a request line, then a JSON body on the same line, on the lines under it, or starting on the line and going on under it: + +```http +POST /collections/docs/points/query +{"query": [0.1, 0.2, 0.3, 0.4], "limit": 10, "with_payload": true} +``` + +A response that lists points, from `query`, `scroll`, `search` or a retrieve by id, renders as a grid with `id`, then `score` when there is one, the payload fields, and any vectors returned. Anything else is shown as formatted JSON. + +Read-only mode allows `GET`, `POST /collections/{name}/facet`, and the read endpoints under `/points`: `query`, `scroll`, `search`, `count`, `recommend`, `discover`, their batch and group forms, and a retrieve by id. A `DELETE`, a path ending in `delete` or `clear`, and `points/batch` count as destructive. A snapshot request is flagged as reaching the server's disk. + +## SSL/TLS + +New connections start on **Disabled**, plain HTTP. Every other mode goes over HTTPS. + +| SSL Mode | What is checked | +|----------|-----------------| +| Preferred, Required (skip verify) | Nothing. A self-signed certificate is accepted | +| Verify CA | The certificate chains to the CA certificate you choose and is issued for a server. The hostname is not checked | +| Verify Identity | The certificate chains to the CA certificate, or to the system trust store when none is chosen, and names the host | + +For a node that enforces mutual TLS, choose a **Client Certificate** and its **Client Key**. See [SSL/TLS](/connections/ssl). + +## Limitations + +- SQL is not available. Use the REST editor. +- gRPC is not used. +- Payload indexes and collection settings are not edited from Structure. Send the REST request, such as `PUT /collections/{name}/index`. +- A field that holds a list in any point cannot be sorted. +- Adding rows from the grid needs Qdrant 1.15 or later. On an older node, upsert from the REST editor. +- The plugin is registry-only. It is not on iPhone or iPad. + +## Troubleshooting + +### Must provide an API key + +The node was started with an API key and **API Key** is empty. Paste the key and connect again. + +### Invalid API key or JWT + +The key was rejected. The health check reads `/collections`, which needs the key, so a revoked key also drops a live session. + +### No range index for `order_by` key + +The sorted column has no `integer`, `float` or `datetime` payload index. Create one with `PUT /collections/{name}/index`, or clear the sort. + +### A row with this key already exists + +A new row's `id` is already taken. Clear the `id` cell to get a new UUID, or edit the existing point instead. + +## Related + +- [Import & Export](/features/import-export) +- [Filtering](/features/filtering) diff --git a/docs.json b/docs.json index 8cea481..cbb9098 100644 --- a/docs.json +++ b/docs.json @@ -268,6 +268,7 @@ "databases/oracle", "databases/pglite", "databases/postgresql", + "databases/qdrant", "databases/redis", "databases/sap-hana", "databases/snowflake", diff --git a/index.mdx b/index.mdx index edeb2d1..be3e258 100644 --- a/index.mdx +++ b/index.mdx @@ -1,6 +1,6 @@ --- title: Introduction -description: Native macOS database client for MySQL, PostgreSQL, SQLite, MongoDB, Redis, and 39 more +description: Native macOS database client for MySQL, PostgreSQL, SQLite, MongoDB, Redis, and 42 more --- import DriverCounts from "/snippets/driver-counts.mdx"; diff --git a/snippets/driver-counts.mdx b/snippets/driver-counts.mdx index c8f8155..7bc9ec3 100644 --- a/snippets/driver-counts.mdx +++ b/snippets/driver-counts.mdx @@ -1,3 +1,3 @@ -Five drivers ship inside the app and cover twelve databases. Twenty-eight registry plugins cover the -other thirty-two and install on the first connection that needs one. See +Five drivers ship inside the app and cover twelve databases. Thirty-one registry plugins cover the +other thirty-five and install on the first connection that needs one. See [Plugins](/features/plugins).