Back to Blog

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.

CoomiaPublished on January 12, 20265 min read
Share this articleTwitter / X

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:

  1. Local deployment of coomia-dip (S12-01)
  2. Created Employee, Department, and Project ObjectTypes (S12-02)
Python
from ontology_sdk import OntoPlatform

platform = OntoPlatform(
    control_plane_url="localhost:50051",
    data_plane_url="localhost:50052"
)

#Creating Entities

#Create a Single Entity

Python
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

Python
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

Python
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:

Python
# 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

Python
emp = platform.objects.get(
    object_type="Employee",
    primary_key="EMP-000001"
)
print(f"Found: {emp.properties['name']} ({emp.properties['title']})")

#Conditional Query

Python
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

OperatorDescriptionExample
eqEquals{"status": {"eq": "active"}}
neqNot equals{"status": {"neq": "cancelled"}}
gtGreater than{"level": {"gt": 5}}
gteGreater or equal{"salary": {"gte": 30000}}
ltLess than{"level": {"lt": 10}}
lteLess or equal{"budget": {"lte": 100000}}
inIn list{"status": {"in": ["active", "planning"]}}
containsContains substring{"name": {"contains": "eng"}}
starts_withPrefix match{"email": {"starts_with": "alice"}}
is_nullIs null{"phone": {"is_null": True}}
betweenRange{"hire_date": {"between": ["2020-01-01", "2023-12-31"]}}
array_containsArray contains{"skills": {"array_contains": "python"}}

#Relationship Queries

Python
# 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

Python
platform.objects.update(
    object_type="Employee",
    primary_key="EMP-000001",
    properties={
        "salary": 130000.00,
        "level": 9
    }
)

#Transaction Support

Python
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

Python
# 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

Python
# 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

  1. Batch Operations: Prefer batch_create and batch_delete to reduce gRPC round trips
  2. Pagination: Use limit and offset for large result sets
  3. Selective Loading: Use select parameter to return only needed properties
  4. Indexing: Add indexes for frequently queried properties
  5. 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