User Guide
Everything you need to install, configure, and build with Montycat — from getting started to governance, advanced operations, and security.
Pointers
Concept of pointers
Pointers in Montycat are the equivalent of foreign keys (FK) in relational databases but with additional flexibility:
- Always many-to-many: a pointer can link records across multiple keyspaces without embedding entire documents.
- Natively resolved: Montycat can automatically dereference pointers at query time when requested.
- Data consistency: pointers help maintain relationships between entities stored in different keyspaces, ensuring linked data remains consistent.
Pointers allow you to model relationships between keyspaces without duplicating data, while leaving you in control of whether or not to resolve them on retrieval.
Example Workflow with Pointers
- Create and insert a record in the target keyspace (e.g., Departments).
- Store the returned key as a pointer when inserting into another keyspace (e.g., Employees).
- Retrieve the record:
- Without pointer resolution Montycat returns only original record.
- With pointer enabled Montycat automatically dereferences and embeds the related record(s).
This gives you the flexibility to either fetch lightweight references for performance, or resolve full relationships when you need rich, connected data.
In the current version, pointers can be established only between the same type of keyspaces — from in-memory to in-memory or from persistent to persistent.
Pointers within in-memory keyspaces cannot be established either if it was set to expire or an original key has was set to expire and will be removed by timer at some point of time.
Performance considerations: pointer resolution adds overhead. In highly concurrent environments with a large number of simultaneous requests, resolving deep or complex pointer chains can slow down performance.
Note: Pointers can be established only between the same type of keyspaces — from in-memory to in-memory or from persistent to persistent. Pointers within in-memory keyspaces cannot be established either if it was set to expire or an original key has was set to expire and will be removed by timer at some point of time.
Important: Always consider the lifecycle of your keys when working with pointers.
Pointers extraction: You can either extract pointer value or pointer metadata.
from montycat import Schema, Pointer
from keyspaces import Employees, Departments
class EmployeesSchema(Schema):
username: string
location: string
age: number
department: Pointer
class DepartmentSchema(Schema):
name: string
employees: int
department = DepartmentSchema(
name='IT',
employees=20
).serialize()
res1 = await Departments.insert_value(department)
# {"status": True, "payload": "128345335674100726154823618391811195396", "error": None}
employee = EmployeesSchema(
username="Name",
location="Location",
age=21,
department=Pointer(Departments, '128345335674100726154823618391811195396'), # pass foreign keyspace and previously returned key
).serialize()
res2 = await Employees.insert_value(employee)
# {"status": True, "payload": "128222336824100726154851618391811195396", "error": None}
res3 = await Employees.get_value(key="128222336824100726154851618391811195396")
# {"status": True, "payload": {"username": "Name", "location": "Location", "age": 21}, "error": None}
res4 = await Employees.get_value(key="128222336824100726154851618391811195396", with_pointers=True)
# {
# "status": True,
# "payload": {"username": "Name", "location": "Location", "age": 21,
# "department": {"name": "IT", "employees": 20}
# },
# "error": None
# }
res5 = await Employees.get_value(key="128222336824100726154851618391811195396", with_pointers_metadata=True)
# {
# "status": True,
# "payload": {"username": "Name", "location": "Location", "age": 21,
# "department": {
# "__key__": "128345335674100726154823618391811195396",
# "__keyspace__": "Departments"
# },
# },
# "error": None
# }Update pointers
You can easily update pointers:
from keyspaces import Employees, Departments #keyspaces.py
res1 = await Employees.update_value(
key="128222336824100726154851618391811195396",
age=34,
department=Pointer(Departments, "128345335333100726154823618391811195454"), # the different key passed
)
# {"status": True, "payload": None, "error": None}
# if no updates:
# {"status": False, "payload": {"no_changes": "128222336824100726154851618391811195396"}, "error": None}Lookup using pointers
You can lookup keys and values by pointers as well:
from keyspaces import Employees #keyspaces.py
from montycat import Pointer
res1 = await Employees.lookup_values_where(
age = 21,
username = "Name",
department = Pointer(Departments, "128345335333100726154823618391811195454"),
)
# {"status": True, "payload": [{"username": "Name", "location": "Location", "age": 21}], "error": None}
res2 = await Employees.lookup_keys_where(
age = 21,
username = "Name",
department = Pointer(Departments, "128345335333100726154823618391811195454"),
)
# {"status": True, "payload": ["128222336824100726154851618391811195396"], "error": None}Depending keys
You can list all dependent keys and keyspaces - that is, all keys related to a specific key within a particular keyspace.
from keyspaces import Departments #keyspaces.py
res1 = await Departments.list_all_depending_keys(key = "128345445574100726154823618391234595396")
res2 = await Departments.list_all_depending_keys(custom_key = "myCustomKey")
# {"status": True, "payload": {"Employees": ["128222336824100726154851618391811193333"]}, "error": None}