Dynamic Query
Dynamic Query
Section titled “Dynamic Query”Übersicht
Section titled “Übersicht”Der Namespace /api/v1/dynamic/query stellt Endpunkte bereit, um flexible SQL-ähnliche Abfragen über JSON zu definieren. Die Engine übersetzt Request-Payloads in SQLAlchemy-Queries und unterstützt unter anderem:
SELECTWHEREJOINGROUP BYORDER BY- Subqueries in Filtern
Verfügbare Endpunkte:
| Methode | Pfad |
|---|---|
GET | /api/v1/dynamic/query/tables |
GET | /api/v1/dynamic/query/attributes |
POST | /api/v1/dynamic/query |
POST | /api/v1/dynamic/query/bulk |
POST | /api/v1/dynamic/query/default-configuration |
Alle Dynamic-Query-Endpunkte benötigen Authentifizierung. Standardmäßig ist ein effektiver Tenant-Kontext erforderlich. Tabellen und Attribute werden nach READ-Berechtigung gefiltert.
Modellnamen und Registry-Keys
Section titled “Modellnamen und Registry-Keys”query_model und target_model müssen Keys sein, die GET /api/v1/dynamic/query/tables zurückgibt. Der aktuelle Registry-Aufbau verwendet Tabellennamen aus __tablename__; verlasse dich nicht darauf, dass Singular-, Plural- oder Klassenname-Varianten zusätzlich funktionieren.
Beispiele für tabellenbasierte Keys:
usersagentsagent_tools
Grundstruktur der Anfrage
Section titled “Grundstruktur der Anfrage”Eine Dynamic-Query-Anfrage enthält dieses Schema:
| Feld | Typ | Beschreibung |
|---|---|---|
query_model | String | Pflichtfeld. Key aus /tables. |
query_attributes | Liste | Optional. Liste aus Strings oder [expression, alias]-Paaren. |
query_filter | Objekt | Optional. Filterbaum mit Operatoren. |
order_by | Liste aus Strings | Optional. -field sortiert absteigend. |
group_by | Liste aus Strings | Optional. Gruppierungsfelder. |
joins | Liste | Optional. Join-Definitionen. |
page | Objekt | Pflichtfeld bei POST /dynamic/query und bei jedem Eintrag in POST /dynamic/query/bulk. |
Join-Definitionen haben dieses Format:
{ "target_model": "agent_tools", "on": "id=agent_id", "isouter": false}on kann ein String oder null sein. isouter ist ein Boolean.
curl https://api.livoi.de/api/v1/dynamic/query \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_TOKEN' \ --data '{ "query_model": "users", "query_attributes": ["id", "email"], "page": { "number": 1, "size": 10 }}'Schema und Metadaten
Section titled “Schema und Metadaten”Verfügbare Tabellen auflisten
Section titled “Verfügbare Tabellen auflisten”curl 'https://api.livoi.de/api/v1/dynamic/query/tables?include_attributes=true' \ --header 'Authorization: Bearer YOUR_SECRET_TOKEN'Beispielantwort:
{ "tables": { "users": ["id", "email", "created_at"], "agents": ["id", "name", "tenant_id"], "agent_tools": ["id", "agent_id", "tool_id"] }}Attribute eines Modells abrufen
Section titled “Attribute eines Modells abrufen”curl 'https://api.livoi.de/api/v1/dynamic/query/attributes?query_model=users' \ --header 'Authorization: Bearer YOUR_SECRET_TOKEN'Beispielantwort:
{ "attributes": [ "content_type", "created_at", "file_name", "id", "size", "tags", "title", "updated_at", "uri" ]}SELECT
Section titled “SELECT”{ "query_model": "users", "joins": [ { "target_model": "agents", "on": "id=user_id", "isouter": true } ], "query_attributes": [ "id", "email", "agents.name" ], "page": { "number": 1, "size": 10 }}{ "query_model": "agents", "query_attributes": [ ["name", "agent_name"], ["tenant_id", "tenant"] ], "page": { "number": 1, "size": 10 }}{ "query_model": "agents", "query_attributes": [ "tenant_id", ["COUNT(id)", "agent_count"] ], "group_by": ["tenant_id"], "page": { "number": 1, "size": 10 }}WHERE-Filter
Section titled “WHERE-Filter”Filterfelder verwenden dieses Format:
field__operatorFelder ohne Operator sind ungültig. Statt permission_level muss zum Beispiel permission_level__eq verwendet werden.
Unterstützte Operatoren:
| Suffix | SQL | Wert |
|---|---|---|
__eq | = | Einzelwert |
__ne | != | Einzelwert |
__lt | < | Einzelwert |
__lte | <= | Einzelwert |
__gt | > | Einzelwert |
__gte | >= | Einzelwert |
__like | LIKE | String |
__ilike | ILIKE | String |
__in | IN | Liste |
__not_in | NOT IN | Liste |
__isnull | IS NULL / IS NOT NULL | Boolean |
in und not_in verlangen Listen. isnull erwartet einen booleschen Wert. Filter können mit and und or verschachtelt werden.
{ "query_model": "users", "query_filter": { "and": [ { "permission_level__in": ["ADMIN", "OWNER"] }, { "last_login__gte": "2024-01-01T00:00:00Z" }, { "deleted_at__isnull": true } ] }, "page": { "number": 1, "size": 10 }}Subqueries
Section titled “Subqueries”{ "id__in": { "subquery": { "query_model": "agent_tools", "query_attributes": ["agent_id"], "query_filter": { "tool_id__eq": "tool_123" } } }}Subquery-Vergleiche sind für normale Operatoren scalar mit limit(1). Für in und not_in wird die Subquery als Mengenvergleich genutzt.
- target_model
Der Ziel-Key muss aus
/api/v1/dynamic/query/tablesstammen. - ON-Clause
Das Format ist
left_field=right_field, zum Beispielid=agent_id. - Join-Typ
”isouter”: trueerzeugt einenLEFT OUTER JOIN.
{ "query_model": "agents", "joins": [ { "target_model": "agent_tools", "on": "id=agent_id", "isouter": true } ], "query_attributes": [ "id", "name", ["ARRAY_AGG(agent_tools.tool_id)", "tool_ids"] ], "group_by": ["id", "name"], "page": { "number": 1, "size": 10 }}ORDER BY
Section titled “ORDER BY”{ "query_model": "users", "order_by": [ "permission_level", "-last_login" ], "page": { "number": 1, "size": 10 }}field: aufsteigend-field: absteigend
GROUP BY
Section titled “GROUP BY”{ "query_model": "agents", "query_attributes": [ "tenant_id", ["COUNT(id)", "agent_count"] ], "group_by": ["tenant_id"], "page": { "number": 1, "size": 10 }}Bulk-Queries
Section titled “Bulk-Queries”curl https://api.livoi.de/api/v1/dynamic/query/bulk \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_SECRET_TOKEN'Beispiel-Body:
{ "queries": [ { "query_model": "users", "query_attributes": ["id", "email"], "page": { "number": 1, "size": 10 } }, { "query_model": "agents", "query_attributes": [ "tenant_id", ["COUNT(id)", "agent_count"] ], "group_by": ["tenant_id"], "page": { "number": 1, "size": 10 } } ]}Default-Konfiguration
Section titled “Default-Konfiguration”POST /api/v1/dynamic/query/default-configurationFilter:
referencefunction
Grenzen
Section titled “Grenzen”| Bereich | Limit |
|---|---|
query_attributes | 500 |
| Bulk-Queries | 100 |
| Filter-Branches | 250 |
| Filter-Depth | 25 |
| Filter-Nodes | 100 |
Filter-Werte für in / not_in | 100 |
group_by | 100 |
joins | 50 |
order_by | 100 |
page.size | 1000 |
Fehlerbehandlung
Section titled “Fehlerbehandlung”{ "status_code": "403", "status_message": "Forbidden", "errors": [ { "info": "Missing permission for agents.read." } ]}{ "status_code": "404", "status_message": "Not Found", "errors": [ { "info": "Unknown query_model: users_archive." } ]}{ "status_code": "422", "status_message": "Unprocessable Entity", "errors": [ { "info": "Validation failed.", "type": "ValidationError", "cause": [ { "details": "Subquery group_by is not supported", "location": ["body", "query_filter"], "code": "value_error" } ] } ]}{ "status_code": "500", "status_message": "Internal Server Error", "errors": [ { "info": "An unexpected error occurred." } ]}