Download notebook · View source · Open in Colab
Work with Blobs
A Blob stores binary data such as text files, PDFs, or audio, with metadata that ApertureDB can query. This example stores a recipe text file, retrieves and updates its metadata, and deletes it.
The examples require Python 3.10 or later, an ApertureDB connection profile or environment credentials, and permission to create, read, update, and delete objects. A unique documentation_run property separates the example data from other records. Cleanup uses that value, so keep the setup variables available until cleanup completes.
The server computes the read-only _mime_type MIME property from the blob bytes (for this example, text/plain). To override detection, pass mime_type directly in AddBlob; do not include _mime_type in AddBlob.properties.
Connect to ApertureDB
ApertureDB Cloud The SDK connects with an API key from your Cloud instance. Obtain the key from Connect > Generate API Key and add it to a .env file in this directory:
APERTUREDB_KEY=your_key_here
Local evaluation: follow the deployment guide, then configure your connection profile.
%pip install --quiet "aperturedb==0.5.1" "python-dotenv==1.1.1"
# Option A: ApertureDB Cloud
from dotenv import load_dotenv
load_dotenv() # loads APERTUREDB_KEY from .env into the environment
# For local evaluation, configure the profile from your versioned deployment package.
# https://docs.aperturedata.io/administration/deployment/DockerCompose
from aperturedb.CommonLibrary import create_connector
client = create_connector()
import uuid
documentation_run = uuid.uuid4().hex
ingredient_class = "CookbookIngredient_" + documentation_run
connection_class = "CookbookIngredientLink_" + documentation_run
def checked_query(query, input_blobs=None):
response, output_blobs = client.query(query, input_blobs or [])
assert isinstance(response, list) and len(response) == len(query), response
for command in response:
result = next(iter(command.values()))
assert result.get("status") == 0, response
return response, output_blobs
response, _ = checked_query([{"GetStatus": {}}])
client.print_last_response()
Create or Add a Recipe to ApertureDB
A recipe can be stored as a blob with metadata describing the dish. Short text can also be stored directly as an entity property. In this example, AddBlob receives the text file as a binary input alongside the JSON command.
The Python SDK loaders handle bulk ingestion.
# Download the sample file
from pathlib import Path
from urllib.request import urlopen
import hashlib
fixture_path = Path("data/baked_potato.txt")
fixture_path.parent.mkdir(exist_ok=True)
fixture_url = "https://raw.githubusercontent.com/aperture-data/Cookbook/e333f6c59070b9165033d9ddd5af852a6b9624ba/notebooks/simple/data/baked_potato.txt"
fixture_sha256 = "8197119cb3a5ee884d619b67459bd8ae1acc9abb965a9033446714b754bb220c"
if not fixture_path.exists() or hashlib.sha256(fixture_path.read_bytes()).hexdigest() != fixture_sha256:
with urlopen(fixture_url, timeout=60) as response:
fixture_path.write_bytes(response.read())
assert hashlib.sha256(fixture_path.read_bytes()).hexdigest() == fixture_sha256
print("Verified fixture:", fixture_path.name)
query = [{
"AddBlob": {
# Notice the missing "class" property since we are adding it as a blob (represented as _Blob in ApertureDB)
"properties": {
'documentation_run': documentation_run,
"type": "text", # since blobs can be of different types like audio, pdf, etc, we can make that explicit
"name": "baked_potato",
"id": 55,
"category": "sides",
"cuisine": "American",
"caption": "Special baked potatoes"
},
"if_not_found": ['all', ['$id', '==', 55], ['$documentation_run', '==', documentation_run]]
}
}]
# Read the image data as a binary blob
fd = open("data/baked_potato.txt", 'rb')
array = [ fd.read() ]
fd.close()
response, blobs = checked_query(query, array)
client.print_last_response()
Query blob by its metadata attributes
FindBlob selects the recipe by its metadata and returns its property values.
from aperturedb import NotebookHelpers as nh # Our helper package for image displays and other utilities
query = [{
"FindBlob": {
"constraints": ['all', ['all', ['$name', '>=', 'baked'], ['$type', '==', 'text']], ['$documentation_run', '==', documentation_run]],
"blobs": True, # This is set to False by default
"results": {
"all_properties": True
}
}
}]
response, blobs = checked_query(query)
client.print_last_response()
num_blobs = response[0]["FindBlob"]["returned"]
for count in range(num_blobs):
print(blobs[count].decode())
assert response[0]["FindBlob"]["returned"] == 1
assert len(blobs) == 1 and blobs[0] == fixture_path.read_bytes()
Update properties of the blob already in ApertureDB
UpdateBlob changes existing metadata values or adds properties to matching blobs.
query = [{
"UpdateBlob": {
"properties": {
"contributor": "Gavin" # property will get added if missing or the value will be updated
},
"constraints": ['all', ['$name', '==', 'baked_potato'], ['$documentation_run', '==', documentation_run]],
}
}]
response, blobs = checked_query(query)
client.print_last_response()
query = [{
"FindBlob": {
"constraints": ['all', ['$name', '==', 'baked_potato'], ['$documentation_run', '==', documentation_run]],
"results": {
"all_properties": True
}
}
}]
response, blobs = checked_query(query)
client.print_last_response()
assert response[0]["FindBlob"]["entities"][0]["contributor"] == "Gavin"
Delete the blob
DeleteBlob removes the recipe selected by its metadata and this run's identifier.
query = [{
"DeleteBlob": {
"constraints": ['all', ['$name', '==', 'baked_potato'], ['$documentation_run', '==', documentation_run]]
}
}]
response, blobs = checked_query(query)
client.print_last_response()
Verify deletion
After deletion, the same blob query returns no matches.
query = [{
"FindBlob": {
"constraints": ['all', ['$name', '==', 'baked_potato'], ['$documentation_run', '==', documentation_run]],
"results": {
"all_properties": True
}
}
}]
response, blobs = checked_query(query)
client.print_last_response()
assert response[0]["FindBlob"]["returned"] == 0
What's next?
- Bulk load blobs
- Add embeddings or relationships to ApertureDB