qlib-rs: A flexible in-memory database library
qlib-rs provides a simple yet powerful in-memory database based on an
Entity-Attribute-Value (EAV) model. It's designed for scenarios where you need to
manage structured but flexible data, with a focus on relationships between
entities.
Core Concepts
The database is built around a few key concepts:
- 
Entity: An
Entityis a unique object in the database, identified by anEntityId. Each entity has a type (e.g., "User", "Folder") and a unique ID. Entities are lightweight; they are primarily containers for fields. - 
Field: A
Fieldis a piece of data associated with an entity. It's defined by aFieldType(e.g., "Name", "Email") and holds aValue. - 
Value: The
Valueenum represents the actual data stored in a field. It can be a primitive type (Bool,Int,Float,String), a timestamp, binary data, or a reference to other entities (EntityReference,EntityList). - 
Schema: A
Schemadefines the structure for a givenEntityType. TheEntitySchemaspecifies which fields an entity of that type can have. Each field is further described by aFieldSchema, which defines its data type (via adefault_value), rank, and other constraints. 
The Store
The Store is the central component of qlib-rs. It's the in-memory database
that holds all entities, schemas, and their associated fields. All interactions
with the database, such as creating entities, reading fields, or writing values,
are performed through the Store.
Operations are batched into a Vec<Request> and processed by the Store::perform
method. A Request can be either a Read or a Write.
Database Structure
qlib-rs uses a structure similar to an Entity-Attribute-Value (EAV) model.
Instead of rigid tables, data is stored in a more flexible way:
schemas: A map fromEntityTypetoEntitySchema, defining the data model.entities: A map fromEntityTypeto a list of allEntityIds of that type.fields: The core data storage. It's a map where the key is anEntityIdand the value is another map fromFieldTypeto the actualFielddata (which includes theValue).
Relationships between entities are a key feature. They are not enforced by foreign
keys but are managed through special Value types:
Value::EntityReference: Represents a one-to-one or many-to-one relationship. For example, a "User" entity might have a "Manager" field of this type.Value::EntityList: Represents a one-to-many relationship. For example, a "Folder" entity would have a "Children" field of this type to list all the entities it contains.
The library provides helpers like create_entity which automatically manage
bidirectional parent-child relationships.
Indirection
Indirection is a powerful feature that allows you to traverse relationships
between entities in a single read or write request, without needing to perform
multiple queries. An indirection path is a string composed of field names and
list indices, separated by ->.
For example, consider a hierarchy: Root -> Folder -> User. To get the email of
a user named "admin" inside a "Users" folder, you might use an indirection path
like: "Children->0->Email".
Let's break down an example path: Parent->Children->0->Name
Parent: This resolves to theParentfield of the starting entity. This field is expected to be anEntityReference. The store follows this reference to the parent entity.Children: Now on the parent entity, it looks for theChildrenfield. This is expected to be anEntityList.0: This is an index into theEntityListfrom the previous step. It selects the first entity in the list.Name: Finally, it resolves theNamefield on the entity selected by the index.
This allows for complex data retrieval in a concise and efficient manner. If any
part of the path fails to resolve (e.g., an empty reference, an index out of
bounds), the operation will fail with a BadIndirection error.
Entity Inheritance in qlib-rs
The qlib-rs library supports an entity inheritance model similar to object-oriented programming. This allows you to define entity types that inherit fields and behavior from parent entity types.
How Inheritance Works
- 
Entity Schema Definition: When defining an entity schema, you can specify a parent entity type using the
inheritfield:let mut schema = new; - 
Field Inheritance: Child entity types automatically inherit all fields from their parent entity types. For example, if the "Object" type has "Name", "Parent", and "Children" fields, any entity type inheriting from "Object" will also have these fields.
 - 
Field Override: Child entity types can override fields defined by their parent types by defining fields with the same field type:
// Parent "Object" has a default Name field, but User can override it with different properties let name_schema = FieldSchema ; - 
Multi-level Inheritance: The system supports multiple levels of inheritance. For example, "Employee" could inherit from "User", which inherits from "Object".
 - 
Complete Schema Resolution: When working with entities, the library automatically resolves the complete schema by combining all inherited fields:
// This returns a schema with all inherited fields let complete_schema = store.get_complete_entity_schema?; 
Base Type
By convention, all entity types should inherit from the "Object" base type, which provides common fields:
Name: String type for naming the entityParent: EntityReference type to establish hierarchyChildren: EntityList type to track child entities
Benefits of Inheritance
- Code Reuse: Define common fields once and reuse them across multiple entity types
 - Consistency: Ensure consistent field structure across related entity types
 - Schema Evolution: Easily evolve your data model by adding fields to parent types
 
Example Usage
// Define a base Person type inheriting from Object
let mut person_schema = new;
// Add Person-specific fields
person_schema.fields.insert;
// Register the schema
store.set_entity_schema?;
// Define an Employee type inheriting from Person
let mut employee_schema = new;
// Add Employee-specific fields
employee_schema.fields.insert;
// Register the schema
store.set_entity_schema?;
// Now Employee entities will have:
// - Name, Parent, Children (from Object)
// - Age (from Person)
// - Department (from Employee)
Implementation Details
The library manages two versions of entity schemas:
EntitySchema<Single>: Represents the schema as defined, without resolving inheritanceEntitySchema<Complete>: Represents the fully resolved schema with all inherited fields
When querying or manipulating entities, the library uses the complete schema to ensure all inherited fields are available.