Skip to content

Database Skill

Local NoSQL embedded database: schema design, CRUD operations, expiry, change callbacks, and pagination.

Invoke with: /database

Key APIs

c
/* Open / close */
Db *dbOpen(cchar *path, cchar *schema, int flags);
void dbClose(Db *db);

/* CRUD */
const DbItem *dbCreate(Db *db, cchar *model, Json *props, DbParams *params);
const DbItem *dbGet(Db *db, cchar *model, Json *props, DbParams *params);
const DbItem *dbFindOne(Db *db, cchar *model, Json *props, DbParams *params);
RList *dbFind(Db *db, cchar *model, Json *props, DbParams *params);
const DbItem *dbUpdate(Db *db, cchar *model, Json *props, DbParams *params);
int dbRemove(Db *db, cchar *model, Json *props, DbParams *params);

/* Field access */
cchar *dbField(const DbItem *item, cchar *fieldName);
int64 dbFieldNumber(const DbItem *item, cchar *fieldName);
bool dbFieldBool(const DbItem *item, cchar *fieldName);

CRUD Pattern

c
const DbItem *item;
RList         *items;
int           index;

/* Create */
dbCreate(db, "Sensor", DB_PROPS("id", "sensor1", "value", "23.5"), DB_PARAMS());

/* Read one */
item = dbGet(db, "Sensor", DB_PROPS("id", "sensor1"), DB_PARAMS());
if (item) {
    rInfo("sensor", "Value: %s", dbField(item, "value"));
}

/* Find all with limit */
items = dbFind(db, "Sensor", NULL, DB_PARAMS(.limit = 100));
for (ITERATE_ITEMS(items, item, index)) {
    rTrace("scan", "id=%s val=%s", dbField(item, "id"), dbField(item, "value"));
}
rFreeList(items);

/* Update */
dbUpdate(db, "Sensor", DB_PROPS("id", "sensor1", "value", "24.1"), DB_PARAMS());

/* Delete */
dbRemove(db, "Sensor", DB_PROPS("id", "sensor1"), DB_PARAMS());

Schema Definition

Each app has a schema.json5 in state/config/:

json5
{
    version: '1.0.0',
    models: {
        Sensor: {
            id:    { type: 'string', required: true },
            value: { type: 'string' },
            ts:    { type: 'date', ttl: 86400 }
        }
    }
}

Field types: string, number, boolean, date, object, array. The ttl property (seconds) enables automatic item expiration.

Change Callbacks

c
static void onSensorChange(void *arg, Db *db, DbModel *model, DbItem *item,
    DbParams *params, cchar *cmd, int events)
{
    cchar *value = dbField(item, "value");
    rInfo("sensor", "Change: %s %s = %s", cmd, dbField(item, "id"), value);
}

/* In ioStart(): register callback */
dbAddCallback(ioto->db, onSensorChange, "Sensor", NULL, DB_ON_CHANGE);

/* In ioStop(): remove callback */
dbRemoveCallback(ioto->db, onSensorChange, "Sensor", NULL);

Event Types

EventDescription
DB_ON_CHANGEFired immediately on create, update, or remove
DB_ON_COMMITFired when changes are committed to disk

Important Notes

  • dbField() returns a pointer into the item — invalid after updates or frees. Copy with sclone(dbField(item, "name")) when storing the value.
  • DB_PROPS() and DB_PARAMS() are convenience macros for building query args.
  • DB_PARAMS(.limit = N) limits result count; .upsert = 1 creates if not found.
  • Use ioto->db when running inside the Ioto agent.