# ApertureDB — Full AI Context Reference # For use with Claude Code, GitHub Copilot, Cursor, Codex, and other AI coding assistants. # Drop this file in your project root or paste it into your assistant's context window. # URL: https://docs.aperturedata.io/llms-full.txt > ApertureDB is a multimodal database combining vector search, a labeled property graph, > and native storage for images, video, blobs, and embeddings — all in one system with > ACID transactions and a JSON query API. --- ## Mental Model Every ApertureDB query is a JSON array of command objects. Each command runs in the same transaction. Binary data (images, videos, embeddings) is sent alongside as an ordered list of blobs — the N-th command that needs a blob uses the N-th blob in the list. ```python response, blobs = client.query(query_list, blob_list) ``` Commands chain with `_ref` integers: one command assigns `_ref: 1`, a later command uses `ref: 1` to operate on the result of the first. Status codes: `0` = success, `2` = already exists (not an error), negative = failure. --- ## Installation and Connection ```bash pip install aperturedb ``` ### Start ApertureDB locally ```bash # Linux docker run -p 55555:55555 -e ADB_MASTER_KEY=admin -e ADB_FORCE_SSL=false \ aperturedata/aperturedb-community # macOS (map to different host port if 55555 is taken) docker run -p 55554:55555 -e ADB_MASTER_KEY=admin -e ADB_FORCE_SSL=false \ aperturedata/aperturedb-community ``` Default TCP port: **55555**. Default admin username: **admin**. ### Configure the CLI ```bash # Local instance adb config create localdb --active --host localhost --port 55555 \ --username admin --password admin # ApertureDB Cloud — get API key from the Cloud portal → Connect → Generate API Key adb config create mycloud --active --key YOUR_APERTUREDB_KEY ``` ### Connect in Python ```python from aperturedb.CommonLibrary import create_connector client = create_connector() # uses active config or APERTUREDB_KEY env var ``` Configuration resolution order: 1. `APERTUREDB_KEY` environment variable (encoded key string) 2. `APERTUREDB_JSON` environment variable (raw JSON) 3. `APERTUREDB_CONFIG` environment variable (named config) 4. Active `adb` configuration ```python # Explicit options client = create_connector(name="localdb") # named config client = create_connector(key="YOUR_KEY") # encoded key string # Manual construction from aperturedb.Connector import Connector from aperturedb.Configuration import Configuration cfg = Configuration(host="localhost", port=55555, username="admin", password="admin", use_ssl=False) client = Connector(cfg) ``` --- ## Core Query Pattern ```python from aperturedb.CommonLibrary import create_connector, execute_query client = create_connector() query = [{"CommandName": {"param": "value"}}] blobs = [] # binary data aligned with commands that need blobs # Low-level: returns (response_list, blobs_list) response, out_blobs = client.query(query, blobs) # Higher-level: returns (result_code, response, blobs) # result_code: 0 = all ok, 1 = transaction error, 2 = partial failure result, response, out_blobs = execute_query(client, query, blobs) ``` Check status on a specific command: ```python status = response[0]["CommandName"]["status"] # 0 = ok, 2 = exists, <0 = error info = response[0]["CommandName"].get("info", "") ``` --- ## Entity Commands Entities are graph nodes with a class name and arbitrary properties. ### AddEntity ```python query = [{ "AddEntity": { "class": "Person", "properties": { "name": "Alice", "age": 30, "department": "engineering" }, "_ref": 1 # optional: other commands can reference this result } }] ``` ### FindEntity ```python query = [{ "FindEntity": { "class": "Person", "constraints": { "age": [">=", 25], "department": ["==", "engineering"] }, "results": { "all_properties": True # or "list": ["name", "age"] }, "limit": 10, "sort": [{"key": "age", "order": "ascending"}], "_ref": 1 } }] ``` Constraint operators: `==`, `!=`, `>`, `<`, `>=`, `<=`, `in`, `not_in` ### UpdateEntity / DeleteEntity ```python # Update matching entities query = [{ "UpdateEntity": { "class": "Person", "constraints": {"name": ["==", "Alice"]}, "properties": {"age": 31} } }] # Delete — response includes "count" query = [{"DeleteEntity": {"class": "Person", "constraints": {"name": ["==", "Alice"]}}}] ``` --- ## Connection Commands Connections are directed edges between any two objects with a class name and properties. ### AddConnection ```python # Connect entity at _ref 1 → entity at _ref 2 query = [ {"FindEntity": {"class": "Person", "constraints": {"name": ["==", "Alice"]}, "_ref": 1}}, {"FindEntity": {"class": "Project", "constraints": {"name": ["==", "Nexus"]}, "_ref": 2}}, {"AddConnection": { "class": "works_on", "ref1": 1, "ref2": 2, "properties": {"role": "lead", "since": "2024-01"} }} ] ``` ### FindConnection ```python query = [{ "FindConnection": { "class": "works_on", "constraints": {"role": ["==", "lead"]}, "results": {"all_properties": True} } }] ``` ### Inline connect on Add Any `Add*` command can create a connection to an existing object in one transaction: ```python query = [ {"FindEntity": {"class": "Session", "constraints": {"id": ["==", "s-001"]}, "_ref": 1}}, {"AddEntity": { "class": "Context", "properties": {"purpose": "support"}, "connect": {"ref": 1, "class": "nexus_session_context"} }} ] ``` --- ## Descriptor (Embedding) Commands Descriptors are float vectors stored in a named DescriptorSet (vector index). ### AddDescriptorSet ```python query = [{ "AddDescriptorSet": { "name": "text_embeddings", "dimensions": 1536, # must match your model output "engine": "HNSW", # HNSW (approximate) or FaissFlat (exact) "metric": "CS", # CS=cosine, L2=euclidean, IP=inner product "properties": { "embeddings_model": "text-embedding-3-small", # for UI semantic search "modality": "text" } } }] # status 0 = created, status 2 = already exists — both are fine ``` Engines: `HNSW`, `FaissFlat`, `FaissIVFFlat` Metrics: `CS` (cosine similarity), `L2` (Euclidean), `IP` (inner product) ### AddDescriptor Blob must be a 32-bit float array in binary: `np.array(vector, dtype="float32").tobytes()` Size in bytes = `dimensions * 4` ```python import numpy as np vector = np.array([0.1, 0.2, ...], dtype="float32") # dimensions must match set query = [{ "AddDescriptor": { "set": "text_embeddings", "label": "optional label", "properties": {"text": "the original text", "source": "doc-123"} } }] blobs = [vector.tobytes()] response, _ = client.query(query, blobs) ``` ### FindDescriptor — k-NN Search ```python query_vector = np.array([0.1, 0.2, ...], dtype="float32") query = [{ "FindDescriptor": { "set": "text_embeddings", "k_neighbors": 5, "distances": True, # include _distance in results "labels": True, # include _label in results "results": {"all_properties": True}, "constraints": {"source": ["==", "doc-123"]} # optional pre/post filter } }] blobs = [query_vector.tobytes()] response, _ = client.query(query, blobs) results = response[0]["FindDescriptor"]["entities"] for r in results: print(r["_distance"], r.get("text")) ``` --- ## Image Commands Images are stored as binary blobs with optional on-the-fly operations. ### AddImage ```python with open("photo.jpg", "rb") as f: image_bytes = f.read() query = [{ "AddImage": { "format": "jpg", # jpg, png, bmp, tiff "properties": {"filename": "photo.jpg", "camera": "lobby"}, "operations": [{"type": "resize", "width": 224, "height": 224}], "_ref": 1 } }] response, _ = client.query(query, [image_bytes]) # Or from URL — server fetches it query = [{"AddImage": {"url": "https://example.com/photo.jpg", "properties": {...}}}] response, _ = client.query(query) ``` ### FindImage ```python query = [{ "FindImage": { "constraints": {"camera": ["==", "lobby"]}, "operations": [{"type": "resize", "width": 64, "height": 64}], "as_format": "png", "blobs": True, # return image bytes in response "results": {"all_properties": True}, "_ref": 1 } }] response, blobs = client.query(query) # blobs[N] contains the image for the N-th returned image ``` ### Supported image operations ```python {"type": "resize", "width": 224, "height": 224} {"type": "rotate", "angle": 90, "resize": True} {"type": "threshold", "value": 128} {"type": "flip", "code": 0} # 0=vertical, 1=horizontal, -1=both {"type": "crop", "x": 10, "y": 10, "width": 100, "height": 100} ``` ### Image + Descriptor in one transaction Store an image and its embedding atomically, linked by a connection: ```python import numpy as np query = [ {"AddImage": { "properties": {"filename": "dog.jpg"}, "_ref": 1 }}, {"AddDescriptor": { "set": "image_embeddings", "properties": {"filename": "dog.jpg"}, "connect": {"ref": 1, "class": "has_embedding"} }} ] blobs = [image_bytes, embedding_bytes] response, _ = client.query(query, blobs) ``` --- ## Video Commands ### AddVideo ```python with open("clip.mp4", "rb") as f: video_bytes = f.read() query = [{ "AddVideo": { "container": "mp4", "codec": "h264", "properties": {"source": "camera_1", "recorded_at": "2024-01-15"}, "_ref": 1 } }] response, _ = client.query(query, [video_bytes]) ``` ### AddClip ```python query = [ {"FindVideo": {"constraints": {"source": ["==", "camera_1"]}, "_ref": 1}}, {"AddClip": { "ref": 1, "range_type": "time", # "time", "frame", or "fraction" "start": 0.0, "stop": 10.0, "_ref": 2 }} ] ``` --- ## Blob Commands For arbitrary binary data (PDFs, audio, documents, etc.). ```python with open("contract.pdf", "rb") as f: pdf_bytes = f.read() query = [{ "AddBlob": { "properties": { "document_type": "pdf", "filename": "contract.pdf", "session_id": "s-001" }, "_ref": 1 } }] response, _ = client.query(query, [pdf_bytes]) # Retrieve query = [{ "FindBlob": { "constraints": {"document_type": ["==", "pdf"]}, "blobs": True, "results": {"all_properties": True} } }] response, blobs = client.query(query) ``` --- ## Schema and Index Commands ```python # Create index for fast constraint lookups (run once at setup) query = [{ "CreateIndex": { "index_type": "entity", # "entity" or "connection" "class": "Person", "property_key": "name" } }] # status 0 = created, status 2 = already exists — both fine # Inspect schema query = [{"GetSchema": {"type": "entities"}}] # or "connections" or "both" response, _ = client.query(query) schema = response[0]["GetSchema"]["entities"] # Database status query = [{"GetStatus": {}}] response, _ = client.query(query) version = response[0]["GetStatus"]["version"] ``` --- ## Python SDK High-Level Wrappers For common operations, use the object wrappers instead of raw queries: ### Descriptors ```python from aperturedb.Descriptors import Descriptors import numpy as np descriptors = Descriptors(client) # k-NN search — returns list of matching entities with properties results = descriptors.find_similar( set="text_embeddings", vector=query_vector, k_neighbors=5, distances=True, results={"all_properties": True} ) # MMR search (diversity-aware) results = descriptors.find_similar_mmr( set="text_embeddings", vector=query_vector, k_neighbors=5, fetch_k=20, lambda_mult=0.5 # 0=max diversity, 1=max relevance ) ``` ### DataModels + generate_add_query For structured ingestion, define a Pydantic model and generate the query automatically: ```python from aperturedb.DataModels import DescriptorDataModel, DescriptorSetDataModel, ImageDataModel from aperturedb.Query import generate_add_query from aperturedb.CommonLibrary import execute_query # Step 1: create the descriptor set dset = DescriptorSetDataModel(name="clip_embeddings", dimensions=512) query, blobs, _ = generate_add_query(dset) execute_query(client, query, blobs) # Step 2: define a custom model for image + embedding class ImageEmbedding(ImageDataModel): embedding: DescriptorDataModel filename: str # Step 3: add image + embedding in one call record = ImageEmbedding( url="https://example.com/dog.jpg", embedding=DescriptorDataModel(vector=my_vector.tolist(), set=dset), filename="dog.jpg" ) query, blobs, _ = generate_add_query(record) execute_query(client, query, blobs) ``` ### ParallelLoader For high-throughput bulk ingestion across multiple workers: ```python from aperturedb.ParallelLoader import ParallelLoader loader = ParallelLoader(client) # loader automatically batches and distributes across workers # use with CSV parsers or custom generators ``` --- ## LangChain Integration ApertureDB ships a LangChain vector store: ```python from langchain_community.vectorstores import ApertureDB vectorstore = ApertureDB( embeddings=your_embedding_model, # any LangChain Embeddings descriptor_set="langchain_docs" ) # Add documents vectorstore.add_documents(docs) # Similarity search results = vectorstore.similarity_search("query text", k=5) # MMR search results = vectorstore.max_marginal_relevance_search("query text", k=5, fetch_k=20) # As retriever for RAG retriever = vectorstore.as_retriever(search_kwargs={"k": 5}) ``` --- ## LlamaIndex Integration ```python from llama_index.vector_stores.aperturedb import ApertureDBVectorStore from llama_index.core import VectorStoreIndex, StorageContext vector_store = ApertureDBVectorStore(descriptor_set="llamaindex_docs") storage_context = StorageContext.from_defaults(vector_store=vector_store) index = VectorStoreIndex.from_documents(docs, storage_context=storage_context) query_engine = index.as_query_engine() response = query_engine.query("What is ApertureDB?") ``` --- ## Complete Working Examples ### Store and search text embeddings (Cohere) ```python import cohere import numpy as np from aperturedb.CommonLibrary import create_connector, execute_query co = cohere.Client("YOUR_COHERE_API_KEY") client = create_connector() texts = ["ApertureDB stores multimodal data", "Vector search finds similar content"] # Generate embeddings resp = co.embed(texts=texts, model="embed-english-v3.0", input_type="search_document") embeddings = [np.array(e, dtype="float32") for e in resp.embeddings] # Create descriptor set (idempotent) execute_query(client, [{ "AddDescriptorSet": { "name": "text_search", "dimensions": 1024, "engine": "HNSW", "metric": "CS", "properties": {"embeddings_model": "embed-english-v3.0"} } }]) # Add descriptors for text, emb in zip(texts, embeddings): execute_query( client, [{"AddDescriptor": {"set": "text_search", "properties": {"text": text}}}], [emb.tobytes()] ) # Search query_emb = np.array( co.embed(texts=["multimodal database"], model="embed-english-v3.0", input_type="search_query").embeddings[0], dtype="float32" ) result, response, _ = execute_query( client, [{"FindDescriptor": {"set": "text_search", "k_neighbors": 2, "distances": True, "results": {"all_properties": True}}}], [query_emb.tobytes()] ) for r in response[0]["FindDescriptor"]["entities"]: print(r["_distance"], r["text"]) ``` ### Store image + embedding (CLIP) ```python import torch, clip from PIL import Image from aperturedb.DataModels import DescriptorDataModel, DescriptorSetDataModel, ImageDataModel from aperturedb.Query import generate_add_query from aperturedb.CommonLibrary import create_connector, execute_query client = create_connector() device = "cuda" if torch.cuda.is_available() else "cpu" model, preprocess = clip.load("ViT-B/32", device=device) # Create descriptor set dset = DescriptorSetDataModel(name="clip_images", dimensions=512) execute_query(client, *generate_add_query(dset)[:2]) class ImageRecord(ImageDataModel): embedding: DescriptorDataModel filename: str for path in ["dog.jpg", "cat.jpg"]: img = preprocess(Image.open(path)).unsqueeze(0).to(device) with torch.no_grad(): vector = model.encode_image(img)[0] record = ImageRecord( url=path, embedding=DescriptorDataModel(vector=vector.tolist(), set=dset), filename=path ) execute_query(client, *generate_add_query(record)[:2]) ``` ### Graph traversal: entities + connections ```python # Add two entities and link them query = [ {"AddEntity": {"class": "Customer", "properties": {"id": "C-001", "name": "Alice"}, "_ref": 1}}, {"AddEntity": {"class": "Order", "properties": {"id": "O-001", "total": 149.99}, "_ref": 2}}, {"AddConnection": {"class": "placed", "ref1": 1, "ref2": 2, "properties": {"date": "2024-01-15"}}} ] client.query(query) # Find all orders placed by Alice query = [ {"FindEntity": {"class": "Customer", "constraints": {"name": ["==", "Alice"]}, "_ref": 1}}, {"FindEntity": { "class": "Order", "is_connected_to": {"ref": 1, "connection_class": "placed", "direction": "in"}, "results": {"all_properties": True} }} ] response, _ = client.query(query) orders = response[1]["FindEntity"]["entities"] ``` --- ## Common Gotchas - **status 2 is not an error.** `AddDescriptorSet`, `CreateIndex`, and `AddEntity` with `if_not_found` all return status 2 when the object already exists. Treat 0 and 2 as success. - **Blob order matters.** Blobs are positional. The N-th blob in your list is consumed by the N-th command that needs one. If two commands need blobs, send two blobs. - **Descriptors are binary float32.** Always use `np.array(v, dtype="float32").tobytes()`. The dimension count must exactly match the DescriptorSet's `dimensions` field. - **`_ref` is transaction-scoped.** Reference integers only live within a single query list. They are not persistent IDs. - **CreateIndex is idempotent.** Safe to call every deployment — status 2 = already indexed. - **`connect` vs `AddConnection`.** Use `connect` inside an `Add*` command to link atomically in the same transaction. Use `AddConnection` to link existing objects (found with `_ref`). - **`if_not_found` on AddEntity.** Pass `"if_not_found": "skip"` to make entity creation idempotent — creates if absent, skips if a matching entity exists. --- ## Reference: Key Environment Variables | Variable | Purpose | |----------|---------| | `APERTUREDB_KEY` | Encoded connection string (highest priority) | | `APERTUREDB_JSON` | Raw JSON config object | | `APERTUREDB_CONFIG` | Named config to use | | `ADB_MASTER_KEY` | Master password for new ApertureDB instances | | `ADB_FORCE_SSL` | Set to `false` for local non-TLS connections | --- ## Reference: Status Codes | Code | Meaning | |------|---------| | `0` | Success | | `2` | Already exists (not an error — treat as success for idempotent ops) | | `-1` | Transaction-level error | | `-2` | Command-level error | --- ## Reference: Quick Command Cheatsheet | Command | Purpose | |---------|---------| | `AddEntity` / `FindEntity` / `UpdateEntity` / `DeleteEntity` | Graph nodes | | `AddConnection` / `FindConnection` / `DeleteConnection` | Graph edges | | `AddDescriptorSet` | Create a vector index | | `AddDescriptor` / `FindDescriptor` / `DeleteDescriptor` | Embeddings and k-NN search | | `AddImage` / `FindImage` / `UpdateImage` / `DeleteImage` | Image storage + retrieval | | `AddVideo` / `FindVideo` / `AddClip` / `FindClip` | Video and clip storage | | `AddBlob` / `FindBlob` / `DeleteBlob` | Binary documents | | `AddBoundingBox` / `FindBoundingBox` | Region annotations | | `CreateIndex` / `RemoveIndex` | Property indexes | | `GetSchema` | Inspect entity/connection classes | | `GetStatus` | Database health and version | --- ## Further Reading - Docs: https://docs.aperturedata.io - Python SDK: https://docs.aperturedata.io/category/aperturedb-python-sdk - Query reference: https://docs.aperturedata.io/category/aperturedb-query-language - LangChain integration: https://docs.aperturedata.io/Integrations/langchain_howto - LlamaIndex integration: https://docs.aperturedata.io/Integrations/llamaindex_howto - ApertureDB Cloud: https://cloud.aperturedata.io - GitHub: https://github.com/aperture-data/aperturedb-python