Your First Data: Writing and Querying Entities
In the previous tutorial, we created ObjectTypes and RelationTypes to define our business model. But models are just the skeleton — data is the flesh. In this tutorial, you will learn how to write entity data, create relationship instances, execute queries, and perform batch data operations in coomia-dip.
“Series: S12 Developer Tutorials · Article 3 | Level: Beginner | Reading Time: 15 min
Your First Data: Writing and Querying Entities
#Introduction
In the previous tutorial, we created ObjectTypes and RelationTypes to define our business model. But models are just the skeleton — data is the flesh. In this tutorial, you will learn how to write entity data, create relationship instances, execute queries, and perform batch data operations in coomia-dip.
The coomia-dip Data Layer is built on Apache Iceberg and Nessie, providing enterprise-grade features like ACID transactions, time-travel queries, and branch management. All data operations communicate with the Data Layer via gRPC, and the Python SDK provides a friendly wrapper.
#Prerequisites
Ensure you have completed the previous two tutorials:
- Local deployment of coomia-dip (S12-01)
- Created Employee, Department, and Project ObjectTypes (S12-02)
from ontology_sdk import OntoPlatform
platform = OntoPlatform(
control_plane_url="localhost:50051",
data_plane_url="localhost:50052"
)
#Creating Entities
#Create a Single Entity
employee = platform.objects.create(
object_type="Employee",
properties={
"employee_id": "EMP-000001",
"name": "Alice Chen",
"email": "alice@example.com",
"title": "Senior Engineer",
"level": 8,
"hire_date": "2020-03-15",
"salary": 120000.00,
"is_active": True,
"skills": ["python", "java", "kubernetes"]
}
)
print(f"Created: {employee.rid}")
print(f" Name: {employee.properties['name']}")
Each created entity receives a globally unique RID (Resource Identifier) in the format ri.ontology.object.<type>.<uuid>.
#Batch Create Entities
employees_data = [
{
"employee_id": "EMP-000002",
"name": "Bob Li",
"email": "bob@example.com",
"title": "Product Manager",
"level": 7,
"hire_date": "2021-06-01",
"salary": 110000.00,
"skills": ["product-design", "data-analysis"]
},
{
"employee_id": "EMP-000003",
"name": "Carol Wang",
"email": "carol@example.com",
"title": "Data Engineer",
"level": 6,
"hire_date": "2022-01-10",
"salary": 95000.00,
"skills": ["spark", "flink", "sql"]
},
]
results = platform.objects.batch_create(
object_type="Employee",
items=employees_data
)
print(f"Batch created: {len(results)} records")
for r in results:
print(f" {r.properties['name']} -> {r.rid}")
#Create Department and Project Data
engineering = platform.objects.create(
object_type="Department",
properties={
"dept_id": "DEPT-001",
"name": "Engineering",
"code": "ENG",
"budget": 5000000.00
}
)
project_alpha = platform.objects.create(
object_type="Project",
properties={
"project_id": "PRJ-001",
"name": "Alpha Platform",
"status": "in_progress",
"priority": "high",
"budget": 1000000.00,
"start_date": "2024-01-01",
"end_date": "2024-12-31",
"tags": ["platform", "core"]
}
)
#Creating Relationship Instances
With entity data in place, we can create relationships to connect them:
# Alice belongs to Engineering
platform.relations.create(
relation_type="belongs_to",
source_rid=employee.rid,
target_rid=engineering.rid,
properties={
"joined_at": "2020-03-15",
"role_in_dept": "Tech Lead"
}
)
# Alice participates in Alpha Project
platform.relations.create(
relation_type="participates_in",
source_rid=employee.rid,
target_rid=project_alpha.rid,
properties={
"role": "owner",
"allocation_pct": 60
}
)
# Engineering owns Alpha Project
platform.relations.create(
relation_type="owns_project",
source_rid=engineering.rid,
target_rid=project_alpha.rid
)
#Querying Entities
#Query by Primary Key
emp = platform.objects.get(
object_type="Employee",
primary_key="EMP-000001"
)
print(f"Found: {emp.properties['name']} ({emp.properties['title']})")
#Conditional Query
results = platform.objects.search(
object_type="Employee",
filter={
"is_active": {"eq": True},
"level": {"gte": 7},
"title": {"contains": "Engineer"}
},
sort=[{"field": "level", "order": "desc"}],
limit=10
)
print(f"Results: {len(results)} records")
for emp in results:
print(f" {emp.properties['name']} - L{emp.properties['level']} {emp.properties['title']}")
#Filter Operators
| Operator | Description | Example |
|---|---|---|
eq | Equals | {"status": {"eq": "active"}} |
neq | Not equals | {"status": {"neq": "cancelled"}} |
gt | Greater than | {"level": {"gt": 5}} |
gte | Greater or equal | {"salary": {"gte": 30000}} |
lt | Less than | {"level": {"lt": 10}} |
lte | Less or equal | {"budget": {"lte": 100000}} |
in | In list | {"status": {"in": ["active", "planning"]}} |
contains | Contains substring | {"name": {"contains": "eng"}} |
starts_with | Prefix match | {"email": {"starts_with": "alice"}} |
is_null | Is null | {"phone": {"is_null": True}} |
between | Range | {"hire_date": {"between": ["2020-01-01", "2023-12-31"]}} |
array_contains | Array contains | {"skills": {"array_contains": "python"}} |
#Relationship Queries
# Find Alice's department
depts = platform.relations.get_targets(
relation_type="belongs_to",
source_rid=employee.rid
)
# Find all employees in Engineering
members = platform.relations.get_sources(
relation_type="belongs_to",
target_rid=engineering.rid
)
# Find all projects Alice participates in
projects = platform.relations.get_targets(
relation_type="participates_in",
source_rid=employee.rid
)
#Updating Entities
platform.objects.update(
object_type="Employee",
primary_key="EMP-000001",
properties={
"salary": 130000.00,
"level": 9
}
)
#Transaction Support
with platform.transaction() as tx:
new_emp = tx.objects.create(
object_type="Employee",
properties={
"employee_id": "EMP-000005",
"name": "David Sun",
"email": "david@example.com",
"hire_date": "2024-03-01"
}
)
tx.relations.create(
relation_type="belongs_to",
source_rid=new_emp.rid,
target_rid=engineering.rid,
properties={"joined_at": "2024-03-01"}
)
tx.commit()
#Time Travel Queries
# Query data at a specific point in time
historical = platform.objects.search(
object_type="Employee",
filter={"employee_id": {"eq": "EMP-000001"}},
as_of="2024-01-01T00:00:00Z"
)
# View change history
changelog = platform.objects.get_changelog(
object_type="Employee",
primary_key="EMP-000001"
)
for entry in changelog:
print(f" {entry.timestamp}: {entry.change_type}")
#Data Import/Export
# Import from CSV
result = platform.data.import_csv(
object_type="Employee",
file_path="employees.csv",
column_mapping={
"ID": "employee_id",
"Name": "name",
"Email": "email",
"Hire Date": "hire_date"
},
batch_size=1000
)
print(f"Imported: {result.success_count} success, {result.error_count} errors")
# Export to JSON
platform.data.export_json(
object_type="Employee",
output_path="employees_export.json",
filter={"is_active": {"eq": True}}
)
#Performance Tips
- Batch Operations: Prefer
batch_createandbatch_deleteto reduce gRPC round trips - Pagination: Use
limitandoffsetfor large result sets - Selective Loading: Use
selectparameter to return only needed properties - Indexing: Add indexes for frequently queried properties
- Transaction Grouping: Group related operations in a single transaction
#Summary
In this tutorial, we covered the complete data operation lifecycle in coomia-dip: creating entities and relationships, querying with filters and relationship traversal, updating and deleting entities, transaction support, time travel queries, and data import/export. These operations form the foundation of coomia-dip data management.
This is Article 3 in the coomia-dip Developer Tutorial series. Repository: https://github.com/coomia-dip/coomia-dip↗ | License: Apache License 2.0