S3 Vectors: array filters, and building weed-vector without protoc

Filters now match array elements with $eq, a bare value and $in, with
$ne/$nin as their opposites (seaweedfs#12321), checked live on master
54777d7fb9. weed-vector builds with no system protoc from inside
seaweed-vector/ (seaweedfs#12317), checked in a clean rust:1.94.1-bookworm
container.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011UpvafvK8JpagF94MHWqwK
Chris LuandClaude Opus 5.5 committed 2026-11-01 19:04:58 -08:00
1 parent 014cfec364
commit 5ecd81c002
2 files changed
+12 -11

No files matched your search

+5 -6
@@ -27,17 +27,16 @@ SeaweedFS serves the [Amazon S3 Vectors](https://docs.aws.amazon.com/AmazonS3/la
## Getting weed-vector
`weed-vector` is not part of the release archives or the Docker images. Build it from source with Rust 1.94.1 or newer and `protoc`, which Lance's build scripts need:
No release or published Docker image includes `weed-vector` yet. Build it from source with Rust 1.94.1 or newer, running cargo inside `seaweed-vector/`. The workspace points Lance's build scripts at a bundled `protoc`, so no system `protoc` is needed:
```bash
sudo apt-get install -y protobuf-compiler # Debian/Ubuntu; provides protoc
git clone https://github.com/seaweedfs/seaweedfs.git
cd seaweedfs
cargo build --release --locked --manifest-path seaweed-vector/Cargo.toml -p weed-vector-server
# the binary is seaweed-vector/target/release/weed-vector
cd seaweedfs/seaweed-vector
cargo build --release --locked -p weed-vector-server
# the binary is target/release/weed-vector
```
Without `protoc` the build stops in `lance-encoding` with `Could not find protoc`.
Building from another directory with `--manifest-path` skips that setting. The build then needs `protoc` on `PATH` (or `PROTOC` set), or it stops in `lance-encoding` with `Could not find protoc`.
## Quick start
+7 -5
@@ -93,18 +93,20 @@ s3v.query_vectors(
| Filter | Matches |
|--------|---------|
| `{"genre": "scifi"}` | `genre` equals `"scifi"` |
| `{"genre": {"$eq": "scifi"}}`, `{"$ne": ...}` | equal / not equal |
| `{"genre": "scifi"}` | `genre` equals `"scifi"`, or is an array that holds `"scifi"` |
| `{"genre": {"$eq": "scifi"}}`, `{"$ne": ...}` | equal / not equal; for an array, holds / does not hold the value |
| `{"year": {"$gt": 1977}}`, `$gte`, `$lt`, `$lte` | numeric comparison; the operand must be a number (`$gt expects a number`) |
| `{"genre": {"$in": ["drama", "western"]}}`, `$nin` | equal to one of / none of the values |
| `{"genre": {"$in": ["drama", "western"]}}`, `$nin` | equal to (for an array, holds) one of / none of the values |
| `{"tags": {"$exists": true}}` | the key is present (or absent, with `false`) |
| `{"$and": [ ... ]}`, `{"$or": [ ... ]}` | all / any of the sub-filters |
| `{"genre": "scifi", "year": 2014}` | several keys in one object: all must match |
Behaviour to know:
* **Types are compared strictly.** `1`, `"1"`, `true` and `"true"` are four different values, for `$eq` as well as `$in` and `$nin`.
* **Only top-level scalar values match.** A filter does not look inside arrays: with `"tags": ["space", "classic"]`, neither `{"tags": "space"}` nor `{"tags": {"$in": ["space"]}}` matches; only `$exists` sees the key. A dotted path such as `{"info.lang": "fr"}` does not reach into a nested object either.
* **Arrays match by their elements.** With `"tags": ["space", "classic"]`, `{"tags": "space"}`, `{"tags": {"$eq": "classic"}}` and `{"tags": {"$in": ["prison", "space"]}}` all match. `$ne` and `$nin` are their opposites, so `{"tags": {"$ne": "space"}}` does not match. `$gt`, `$gte`, `$lt` and `$lte` do not look inside arrays: `{"scores": {"$gt": 2}}` does not match `"scores": [1, 2.5]`.
* **Types are compared strictly**, inside arrays too. `1`, `"1"`, `true` and `"true"` are four different values: `{"scores": 1}` matches `[1, 2.5]` but not `["1", "2.5"]`, and `{"flags": true}` matches `[true]` but not `["true"]`.
* **Nested objects are not searched.** A dotted path such as `{"info.lang": "fr"}` matches nothing, and `{"info": {"lang": "fr"}}` is rejected with `unsupported operator lang`.
* Array matching needs a gateway and a `weed-vector` of the same release. With an older `weed-vector`, a number or boolean can also match a string element with the same text.
* An unknown operator is rejected (`unsupported operator $regex`), and so is a filter on a non-filterable key.
## Errors