Back to capabilities
v0.4.0

tinysystems/database-module

View Source

Container Image

europe-west2-docker.pkg.dev/tinyplatform/tinysystems/database-module-v0:0.4.0

Components 7 available

P

postgres_exec

Executes INSERT/UPDATE/DELETE against Postgres with positional parameters. Connection pool is cached per DSN across calls.

P

postgres_query

Runs SELECT against Postgres and returns rows as a list of objects keyed by column name. Configure expected row shape in settings so downstream edges can navigate the result. Connection pool is cached per DSN.

R

redis_dedup

Atomic 'first seen' check via SET NX EX. Routes new IDs to the New port and duplicates to the Seen port. Redis client is cached per URL across calls.

R

redis_get

Reads a key from Redis. Emits found=false with empty value when the key does not exist; the error port is reserved for actual Redis failures.

R

redis_set

Sets a key in Redis. Optional TTL via ttlSeconds (0 = no expiry). Optional NX (set only if key does not already exist).

V

vector_search

kNN over a pgvector column. Returns the top-K nearest rows by configurable distance metric, with a normalised score in [0,1]. Optional JSONB metadata filter restricts the search. Pair with vector_upsert to build a RAG store.

V

vector_upsert

Writes an embedding row into a pgvector table. INSERT ... ON CONFLICT (id) DO UPDATE — existing rows are overwritten. The table, id column, embedding column, and optional metadata column are configurable; the embedding extension must be installed and the table must already exist.

Release Notes

Tiny Systems Database Module

Postgres and Redis components for Tiny Systems flows.

Components

Name Purpose
postgres_exec Run INSERT/UPDATE/DELETE with positional parameters; emits rowsAffected.
postgres_query Run SELECT; emits rows[] keyed by column name with a configurable row shape.
vector_upsert Write an embedding row into a pgvector table; INSERT ... ON CONFLICT (id) DO UPDATE. Pair with embedding-module to build a RAG store.
vector_search kNN over a pgvector column; returns top-K rows with normalised similarity scores and an optional JSONB metadata filter. Cosine / L2 / inner-product.
redis_dedup Atomic "first seen" check via SET NX EX; routes new IDs to out_new and duplicates to out_seen.
redis_set Set a key, with optional TTL and NX.
redis_get Get a key, returns found=false for missing keys without raising an error.

All components take their connection string (dsn for Postgres, url for Redis) per message, so a single deployed module can talk to many databases. Connections are pooled by DSN/URL across calls.

Patterns

Dedup-then-route

ticker → http_request(api) → json_decode → array_split → redis_dedup
                                                            ├── out_new  → … process and store ──→ postgres_exec
                                                            └── out_seen → drop

Use redis_dedup for "have I already processed this ID?" checks. keyPrefix + id form the composite key. TTL determines how long Redis remembers — set it longer than the polling cycle plus margin.

Insert with parameters

redis_dedup:out_new → postgres_exec
                       sql: INSERT INTO matched_posts (id, source, title, score) VALUES ($1, $2, $3, $4)
                       params: ["{{$.id}}", "reddit", "{{$.context.title}}", "{{$.context.score}}"]

Query with configurable row shape

In postgres_query settings, define the expected row shape:

{
  "row": { "id": "abc", "title": "title", "score": 0 }
}

Downstream edges can then navigate $.rows[0].title, $.count, etc.

RAG store: embed → upsert → search → chat

The two vector_* components assume the pgvector extension is installed and the target table already exists. A minimal schema for 384-dim embeddings (BGE-small):

CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE memories (
  id        TEXT PRIMARY KEY,
  embedding VECTOR(384),
  metadata  JSONB
);

CREATE INDEX ON memories USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);

Ingest flow — convert text to a vector and store it:

signal → embedding(embed_text) → vector_upsert
                                    table: memories
                                    id:    "{{$.context.docId}}"
                                    embedding: "{{$.embedding}}"
                                    metadata: { source: "{{$.context.source}}", text: "{{$.context.text}}" }

Query flow — embed the question, retrieve the closest rows, hand them to the chat model:

signal → embedding(query) → vector_search
                              table: memories
                              topK:  5
                              metric: cosine
                              metadataFilter: { source: "docs" }   # optional
        → llm_chat
              messages: [{ role: "user", content: "Context: {{$.results}}\n\nQuestion: {{$.context.question}}" }]

Distance metrics: cosine (default — best for semantic search), l2 (Euclidean), ip (negative inner product, useful when embeddings are already normalised and you want pure dot-product ranking). The score is normalised to [0, 1] regardless of metric so flows can threshold without knowing which metric was chosen.

Run Locally

go run cmd/main.go run \
  --name=tiny-systems/database-module-v0 \
  --namespace=tinysystems \
  --version=0.1.0

License

MIT for this module's source. Depends on Tiny Systems Module SDK (BSL 1.1).