SapixDBSapixDB/Docs
Home

Manual · Querying

Field Projection

Return only the fields you need. The select parameter strips every other field from each record's payload on the server before the response is sent — saving bandwidth, protecting sensitive data, and shrinking client-side parsing work.

Server-side strippingFields not listed in select are removed before the response leaves the agent process. They are never serialized into the HTTP body, so no data ever crosses the wire unnecessarily.

The select parameter

Add "select" to any supported query body. Its value is an array of field name strings. Only those fields will appear inside each record's payload object in the response.

JSON
{
  "type":   "scan",
  "limit":  50,
  "select": ["user_id", "plan", "created_at"]
}

With the query above, each returned record contains only user_id, plan, and created_at. All other fields stored in the record — email address, address, payment token, etc. — are absent from the response.

Basic projection

curl
curl -s -X POST http://localhost:7475/v1/agents/users/query \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer spx_root_YOUR_ROOT_KEY" \
  -d '{
    "type":   "scan",
    "limit":  50,
    "select": ["user_id", "plan", "created_at"]
  }' | python3 -m json.tool
Response (one record shown)
{
  "records": [
    {
      "content_hash":   "sha256:...",
      "timestamp_hlc":  109870428282265600,
      "payload": {
        "user_id":    "usr_01J...",
        "plan":       "pro",
        "created_at": "2026-06-01T09:00:00Z"
      }
    }
  ],
  "count": 50
}

The content_hash and timestamp_hlc envelope fields are always returned regardless of select — projection only affects payload.

Projection combined with filter

select and filter are fully orthogonal. The filter determines which records are returned; select determines which fields appear in each record. You can filter on a field that is not included in select.

curl
curl -s -X POST http://localhost:7475/v1/agents/users/query \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer spx_root_YOUR_ROOT_KEY" \
  -d '{
    "type":   "scan",
    "limit":  50,
    "select": ["user_id", "email"],
    "filter": {
      "field": "plan",
      "op":    "eq",
      "value": "enterprise"
    }
  }' | python3 -m json.tool

This returns only the user_id and email of enterprise users. The plan field is used to filter but is absent from the response payload because it was not listed in select.

Filtering on unselected fieldsFilters are evaluated against the full record before projection is applied. You can freely filter on any stored field even if it is excluded from select.

Query types that support select

Query typeSupports selectNotes
scanyesFull-table scan with optional filter and ordering
as_ofyesTime-travel snapshot at a specific HLC timestamp
time_rangeyesRecords written between two HLC timestamps
nl (natural language)yesNL query result payload is projected after resolution
distinctnoReturns value list, not record payloads
aggregatenoReturns computed values, not record payloads

Use case: privacy

A frontend dashboard might need to display a list of users with their plan tier, but must never expose email addresses or payment details. Rather than filtering in the browser (where a determined user could inspect the raw response), use projection to ensure sensitive fields never leave the server.

curl — safe for frontend
curl -s -X POST http://localhost:7475/v1/agents/users/query \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer spx_frontend_READ_KEY" \
  -d '{
    "type":   "scan",
    "limit":  100,
    "select": ["user_id", "name", "plan", "created_at"]
  }'
Combine with scoped API keysPair select with a read-only scoped API key (created via POST /v1/agents/:id/keys) to create a defense-in-depth boundary. The key restricts verbs; projection restricts fields. Even if the key is leaked, the attacker can only read the projected subset.

Use case: bandwidth reduction

Records that embed large blobs — base64-encoded images, long markdown documents, or pre-computed embedding vectors — can be kilobytes or megabytes each. When you only need metadata, projection eliminates that overhead entirely.

curl — skip the blob field
curl -s -X POST http://localhost:7475/v1/agents/documents/query \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer spx_root_YOUR_ROOT_KEY" \
  -d '{
    "type":   "scan",
    "limit":  200,
    "select": ["doc_id", "title", "author", "created_at", "word_count"]
  }'

The content and embedding fields (potentially several kilobytes each) never appear in the response, making the list endpoint practical even over low-bandwidth connections.

Use case: data minimization for AI agents

When your AI agent calls SapixDB to retrieve context, you often want it to see only the fields relevant to the current task. Projecting before sending to the model reduces prompt tokens and prevents the model from inadvertently storing or leaking data it has no need to see.

TypeScript — agent-safe read
const context = await fetch(
  "http://localhost:7475/v1/agents/customers/query",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${process.env.SAPIX_AGENT_KEY}`,
    },
    body: JSON.stringify({
      type:   "scan",
      limit:  20,
      select: ["customer_id", "tier", "open_tickets"],
      filter: { field: "assigned_agent", op: "eq", value: agentId },
    }),
  }
).then((r) => r.json());

// Pass context.records to the LLM — no PII included

What happens when a selected field is missing

If a record does not contain a field listed in select, that key is simply absent from the projected payload — no error is raised and no null placeholder is inserted. This mirrors how SapixDB treats schema-less records in general.

No error on missing fieldsRequesting a non-existent field in select is a no-op for that record, not an error. The response is still 200 OK; the field is just not present in that record's payload.