[try:/devdoc/test-query|Testovací dotazy|play|right]
Přehled
Náš dotazovací jazyk je inspirován GraphQL, ale je navržen pro SQL back-endy a adaptaci REST API. Používá známou, vnořenou a expresivní syntaxi, která eliminuje problém N+1 pomocí inteligentního dávkování a optimalizace dotazů.
card: Inspirováno GraphQL
Známá vnořená syntaxe s výběrem entit, specifikací polí a podporou parametrů
card: Kompatibilní s REST
Může překládat dotazy na REST endpointy pro kompatibilitu s existujícími službami
card: Optimalizováno pro výkon
Eliminuje N+1 SQL problémy pomocí automatického dávkování a optimalizace dotazů
Příklady dotazů
Základní dotaz
Získání všech polí z entity [badge:Základní|info|right]
products {
id
name
price
description
}
Dotaz s parametry
Použití parametrů pro filtrování a stránkování [badge:Parametry|info|right]
products(
where: "status = 'active'",
limit: 10,
orderBy: [name@asc]
) {
id
name
price
}
Vnořené relace
Získání souvisejících entit v jednom dotazu [badge:Relace|info|right]
products {
id
name
category {
id
name
}
variants(limit: 10) {
id
price
}
}
Dotaz s agregacemi
Zahrnutí agregačních funkcí do dotazu [badge:Agregace|info|right]
products(
where: "price > 100"
) {
id
name
@count as total_products
@sum(price) as total_price
}
Funkce polí
Aplikace funkcí na pole pro transformace [badge:Funkce|info|right]
categories() {
name,
length(name) as name_length,
isnull(description) as has_no_description,
upper(code) as code_upper
}
Datumové funkce
Extrahování komponent data/času z polí [badge:Funkce|info|right]
orders() {
id,
order_date,
year(order_date) as order_year,
month(order_date) as order_month,
quarter(order_date) as order_quarter,
dayofweek(order_date) as weekday
}
Funkce časového rozsahu
Získání přesných hranic časového období pro analýzy [badge:Funkce|info|right]
analytics() {
id,
event_date,
startOfYear(event_date) as year_start,
endOfYear(event_date) as year_end,
startOfMonth(event_date) as month_start,
endOfMonth(event_date) as month_end,
startOfHour(event_date) as hour_start,
endOfHour(event_date) as hour_end
}
GROUP BY s funkcemi
Použití funkcí v agregačním seskupování [badge:Agregace|info|right]
sales(
@count as total_sales,
@sum(amount) as total_revenue,
@groupBy(year(sale_date), month(sale_date))
) { * }
Vyplnění mezer pro grafy časových řad
Vyplnění chybějících období ve seskupených výsledcích pro spojité grafy [badge:Agregace|info|right]
orders(
@count as order_count,
@sum(total) as monthly_revenue,
@groupBy(yearmonth(created_at)),
@fillGaps(yearmonth(created_at))
) { }
S explicitním rozsahem dat (rozšíření za hranice dat):
orders(
@count as order_count,
@sum(total) as monthly_revenue,
@groupBy(yearmonth(created_at)),
@fillGaps(yearmonth(created_at), from: "2024-01", to: "2024-12")
) { }
Komplexní dotaz
Kombinace více funkcí v jednom dotazu [badge:Pokročilé|info|right]
orders(limit: 10) {
id
total
user {
id
name
}
items {
id
quantity
product {
id
name
price
}
}
}
Přeložitelná pole
Dotazování na vícejazyčná pole s výběrem specifickým pro jazyk [badge:Pokročilé|info|right]
products(limit: 10) {
id
code
name // Vrátí {"en": "English", "cs": "Czech"}
name:cs // Vrátí pouze český překlad
name:en // Vrátí pouze anglický překlad
description:cs as czech_desc
category {
name:cs
name:en
}
}
Dotaz na historická data
Dotazování na historické verze z archivních tabulek v kombinaci s aktuálním řádkem [badge:Pokročilé|info|right]
products(
@history
where: "id = 123"
) {
id
name
price
_deleted
_deletedAt
_archiveValidSince
_archiveValidTo
}
Historický dotaz k určitému bodu v čase
Dotazování na data, jak existovala v konkrétním časovém okamžiku, plus aktuální řádek [badge:Pokročilé|info|right]
products(
@history(from: "2025-01-01T00:00:00")
where: "price > 100"
limit: 10
) {
id
name
price
_archiveValidSince
_archiveValidTo
}
Syntaktická pravidla
| Syntax | Popis | Příklad |
|---|---|---|
entity(params) { fields } | Výběr entity — specifikujte název entity následovaný volitelnými parametry v závorkách | products(where: "active = true") |
{ id, name } nebo { * } | Výběr polí — vypište pole ve složených závorkách, použijte * pro všechna pole | { id, name, price } nebo { * } |
lower(), upper(), year(), startOfYear(), isnull(), atd. | Funkce polí — aplikujte funkce na pole: lower(), upper(), length(), year(), month(), startOfYear(), endOfMonth(), isnull(), atd. | lower(name) as name_lower, startOfYear(created_at) |
where, limit, offset, orderBy, id | Parametry — použijte parametry where, limit, offset, orderBy a id. Výchozí limit je 1000 řádků. Použijte limit: 0 pro neomezené výsledky. | where: "status = 'active'", limit: 10 |
relation { fields } | Relace — vnořte související entity s vlastním výběrem polí | category { id, name } |
@count, @sum(field), @avg(field) | Agregace — použijte předponu @ pro agregační funkce | @count, @sum(price), @avg(rating) |
@fillGaps(field) nebo @fillGaps(field, from: x, to: y) | Vyplnění mezer — vyplňte chybějící období ve seskupených výsledcích časových řad (použijte s @groupBy). Automaticky detekuje rozsah nebo přijímá explicitní hranice from/to | @fillGaps(yearmonth(createdAt)) nebo @fillGaps(month, from: 1, to: 12) |
[field@asc], [field@desc] | Order By — pole párů pole@směr pro řazení | [created_at@desc, name@asc] |
field as alias | Aliases — použijte klíčové slovo “as” pro přejmenování polí nebo relací | category as product_category |
name:cs, description:en | Přeložitelná pole — vyberte konkrétní jazykové varianty pomocí syntaxe fieldName:languageCode | name:cs, description:en (podporované: cs, en, de, sk, pl, hu, fr, es) |
@history nebo @history(from: "date") | Historické dotazy — použijte direktivu @history k dotazování na historická data z archivních tabulek v kombinaci s aktuálním řádkem. Volitelně specifikujte bod v čase pomocí parametru from | @history nebo @history(from: "2025-01-01T00:00:00") |
Funkce polí
Transformujte a analyzujte hodnoty polí přímo ve vašich dotazech
Funkce polí vám umožňují aplikovat transformace a kontroly na pole v klauzuli select. Funkce lze použít samostatně nebo s aliasy pomocí klíčového slova as.
Řetězcové funkce
| Funkce | Popis | Příklad |
|---|---|---|
lower(field) | Převede řetězec na malá písmena | lower(name) |
upper(field) | Převede řetězec na velká písmena | upper(code) |
length(field) | Získá délku řetězcového pole | length(description) |
isnull(field) | Zkontroluje, zda je pole NULL | isnull(email) |
Funkce data / času
| Funkce | Popis | Příklad |
|---|---|---|
year(field) | Extrahuje rok z data/datetime | year(created_at) |
month(field) | Extrahuje měsíc z data/datetime | month(birth_date) |
day(field) | Extrahuje den z data/datetime | day(due_date) |
week(field) | Extrahuje číslo týdne z data | week(created_at) |
quarter(field) | Extrahuje čtvrtletí z data (1-4) | quarter(order_date) |
dayofweek(field) | Extrahuje den v týdnu (0-6, neděle=0) | dayofweek(event_date) |
yearmonth(field) | Získá řetězec rok-měsíc (RRRR-MM) | yearmonth(created_at) |
yearmonthday(field) | Získá řetězec rok-měsíc-den (RRRR-MM-DD) | yearmonthday(created_at) |
yearweek(field) | Získá řetězec rok-týden (RRRR-TT) | yearweek(start_date) |
yearquarter(field) | Získá řetězec rok-čtvrtletí (RRRR-QN) | yearquarter(fiscal_date) |
Funkce časového rozsahu
| Funkce | Popis | Příklad |
|---|---|---|
startOfYear(field) | Získá začátek roku (RRRR-01-01 00:00:00) | startOfYear(created_at) |
endOfYear(field) | Získá konec roku (RRRR-12-31 23:59:59.999) | endOfYear(created_at) |
startOfMonth(field) | Získá začátek měsíce (RRRR-MM-01 00:00:00) | startOfMonth(created_at) |
endOfMonth(field) | Získá konec měsíce (RRRR-MM-31 23:59:59.999) | endOfMonth(created_at) |
startOfDay(field) | Získá začátek dne (RRRR-MM-DD 00:00:00) | startOfDay(created_at) |
endOfDay(field) | Získá konec dne (RRRR-MM-DD 23:59:59.999) | endOfDay(created_at) |
startOfHour(field) | Získá začátek hodiny (RRRR-MM-DD HH:00:00) | startOfHour(created_at) |
endOfHour(field) | Získá konec hodiny (RRRR-MM-DD HH:59:59.999) | endOfHour(created_at) |
Příklady použití
Řetězcové transformační funkce:
users() {
name,
lower(name) as name_lower,
upper(code) as code_upper,
length(description) as desc_length
}
Funkce extrakce data/času:
orders() {
id,
order_date,
year(order_date) as order_year,
month(order_date) as order_month,
dayofweek(order_date) as weekday
}
Smíšené použití funkcí:
products() {
name,
isnull(description) as has_no_description,
length(name) as name_length,
yearmonth(created_at) as creation_month
}
Funkce časového rozsahu pro analýzu časového období:
analytics() {
id,
event_date,
startOfYear(event_date) as year_start,
endOfYear(event_date) as year_end,
startOfMonth(event_date) as month_start,
endOfMonth(event_date) as month_end,
startOfDay(event_date) as day_start,
endOfDay(event_date) as day_end,
startOfHour(event_date) as hour_start,
endOfHour(event_date) as hour_end
}
Funkce v agregacích GROUP BY:
orders(
@count as order_count,
@sum(total) as total_revenue,
@groupBy(year(created_at), month(created_at))
) { * }
// Funkce časového rozsahu v GROUP BY
events(
@count as event_count,
@sum(value) as total_value,
@groupBy(startOfYear(event_date), startOfMonth(event_date))
) { * }
**Poznámky**
- Funkce mohou být aliasovány pomocí klíčového slova `as`
- Všechny názvy funkcí nerozlišují malá a velká písmena
- Řetězcové funkce fungují s jakýmkoli polem, které lze převést na řetězec
- Funkce data/času fungují s typy polí DATE a DATETIME
- Funkce časového rozsahu vracejí přesná časová razítka pomocí operací DATE_TRUNC a INTERVAL v PostgreSQL
- Funkce lze použít jak ve výběru polí, tak v agregacích @groupBy
- `dayofweek()` vrací 0-6, kde 0=neděle, 6=sobota
- `quarter()` vrací 1-4 pro Q1-Q4
- `yearmonth()` vrací formát 'RRRR-MM' (např. '2023-12')
- `yearweek()` vrací formát 'RRRR-TT' (např. '2023-52')
- `yearquarter()` vrací formát 'RRRR-QN' (např. '2023-Q4')
- Funkce časového rozsahu poskytují mikrosekundovou přesnost pro přesné hranice časového období
Pokročilé funkce
Funkce polí
Aplikujte transformace a kontroly přímo v dotazech
lower(name), year(created_at), length(description)
Dotazy na více ID
Získávání podle jednoho nebo více ID pomocí polí a smíšených typů
products(id: ['id1', "uuid", 123]) { * }
Hluboké vnořování
Přístup k hluboce vnořeným relacím bez snížení výkonu
orders { user { profile { preferences } } }
Podpora komentářů
Přidejte komentáře do svých dotazů pro dokumentaci
// Jednořádkový komentář
/* Víceřádkový komentář */
products(limit: 10) {
id
name // inline comment
}
Přeložitelná pole
Přeložitelná (vícejazyčná) pole lze dotazovat několika způsoby:
Bez jazykové přípony:
products { name, description }
Vrátí všechny vyplněné jazyky jako objekt JSON: {"en": "English", "cs": "Czech"}
S jazykovou příponou:
products {
name:cs, // Pouze čeština
name:en, // Pouze angličtina
name:de // Pouze němčina
}
Vrátí pouze konkrétní jazyk jako řetězec. Podporuje: cs, en, de, sk, pl, hu, fr, es
S aliasy:
products {
name:cs as czech_name,
name:en as english_name
}
Použijte aliasy k přejmenování polí s jazykovými příponami
**Výběr jazyka:**
- Jazyk se vybírá pomocí hlavičky HTTP Accept-Language.
- Všechna přeložitelná pole ve výsledcích dotazu ve výchozím nastavení vracejí všechny vyplněné jazyky (bez jazykové přípony).
- Použijte syntaxi fieldName:languageCode pro získání pouze konkrétního jazyka.
- Jazyková přípona funguje jak ve filtrech WHERE, tak ve polích SELECT.
- Pokud požadovaný jazyk není k dispozici, pole může být prázdné nebo null.
- Příklad: `Accept-Language: en-US, cs-CZ;q=0.9`
Vyhledávání a externí ID
Můžete odkazovat na entity pomocí vyhledávacích identifikátorů namísto číselných/UUID ID. To je užitečné zejména při práci s daty z externích systémů, jako je ABRA Flexi/FlexiBee.
Standardní vyhledávání
Použijte identifikátory založené na polích, jako jsou code:, ean: nebo guid:
Použití vyhledávání kódu:
products(id: "code:ABC123") { id, name, code }
Použití vyhledávání v klauzuli WHERE:
products(where: "category = 'code:ELEC'") { id, name }
Více ID s vyhledáváním:
products(id: ["code:ABC", "ean:123456", "uuid-value"]) { * }
Vyhledávání externích ID
Externí ID (ext:) vám umožňují odkazovat na entity pomocí identifikátorů z externích systémů. Tato ID jsou uložena ve vyhrazené tabulce extIds a podporují vícečástkové klíče s dvojtečkami.
Použití externího ID v dotazu:
products(id: "ext:SYSTEM:PRODUCT:12345") { id, name, code }
Použití externího ID v klauzuli WHERE:
products(where: "category = 'ext:SYSTEM:CATEGORY:ELEC'") { id, name }
Smíšené identifikátory (standardní + externí):
products(id: ["code:ABC", "ext:SYSTEM:PRODUCT:789"]) { * }
Externí ID v relačních polích:
orders(where: "customer = 'ext:CRM:CUSTOMER:C001'") {
id
customer {
id
name
}
}
Vícečástková externí ID (s dvojtečkami):
products(id: "ext:SYSTEM:NAMESPACE:TYPE:ID:123") { id, name }
- Externí ID se automaticky vytvářejí při importu dat s identifikátory `ext:`
- Stejná entita může mít více externích ID z různých systémů
- Externí ID fungují všude, kde fungují běžná ID (dotazy, klauzule WHERE, relační pole)
- Vše za `ext:` je považováno za externí identifikátor
- Externí ID nerozlišují malá a velká písmena při porovnávání
- Uloženo ve vyhrazené tabulce `extIds` s poli: code, related_id, related_entity
API Endpoint: /api/query
Endpoint /api/query přijímá požadavky POST s poli dotazů a vrací streamované JSON odpovědi pro optimální výkon. Více dotazů je zpracováváno paralelně a výsledky jsou odesílány, jakmile jsou k dispozici.
Požadované hlavičky
| Hlavička | Popis |
|---|---|
Authorization | Bearer token pro autentizaci (např. Bearer your-jwt-token) |
x-organisation-id | Identifikátor organizace pro vymezení kontextu dotazu |
x-archive-at | Volitelné časové razítko pro dotazy k určitému bodu v čase. Viz [navigate:/devdoc/changeset |
x-changeset-id | Volitelné ID changeset pro dotazování na konkrétní verze dat. Viz [navigate:/devdoc/changeset |
Accept-Language | Preference jazyka pro přeložitelná pole (např. en-US, cs-CZ) |
[navigate:/apiKeys|Správa API klíčů|key]
Formát požadavku
POST /api/query
Content-Type: application/json
[
{
"requestId": "products",
"query": "products(where: \"category.name = ${categoryName}\", limit: 5) { id, name, price }",
"params": {"categoryName": "Electronics"}
},
{
"requestId": "categories",
"query": "categories { id, name }",
"params": {}
}
]
Úspěšná odpověď
[
{
"requestId": "products",
"success": true,
"data": [
{"id": 1, "name": "Product 1", "price": 100},
{"id": 2, "name": "Product 2", "price": 150}
]
}
]
Chybná odpověď
[
{
"requestId": "products",
"success": false,
"error": "Invalid where clause syntax",
"errorCode": "QUERY_ERROR"
}
]
Odpovědi jsou streamovány po částech pro optimální výkon. Více dotazů je zpracováváno paralelně a výsledky mohou dorazit v jiném pořadí, než bylo požadováno. Použijte částečný JSON parser, který dokáže zpracovat neúplný JSON při zpracování streamu odpovědí.
Vyplňování mezer pro časové řady (@fillGaps)
Vyplňte chybějící období ve seskupených výsledcích pro plynulé, spojité grafy
Při vytváření grafů nebo sestav v průběhu času mají seskupené výsledky často mezery — měsíce, týdny nebo roky, kde neexistují žádná data. @fillGaps automaticky vkládá řádky pro tyto chybějící hodnoty, takže výsledná sada je spojitá.
Použijte @fillGaps společně s @groupBy na stejném poli. Běží jako krok následného zpracování a neupravuje vaše data.
Jak to funguje
- Chybějící intervaly jsou detekovány porovnáním hodnot seskupeného pole s očekávaným rozsahem.
- Automatická detekce rozsahu: ve výchozím nastavení rozsah zahrnuje nejmenší až největší hodnotu ve skutečných datech.
- Explicitní rozsah: použijte
froma/nebotok rozšíření rozsahu za hranice toho, co data obsahují. - Agregace
@counta@sumjsou vyplněny0pro chybějící řádky. - Agregace
@avg,@min,@maxjsou vyplněnynullpro chybějící řádky.
Podporované typy polí
| Pole | Formát | Příklad |
|---|---|---|
yearmonth(field) | Řetězce RRRR-MM | "2024-01" … "2024-12" |
yearmonthday(field) | Řetězce RRRR-MM-DD | "2024-01-01" … "2024-01-31" |
yearweek(field) | Řetězce RRRR-TT | "2024-01" … "2024-52" |
year(field) | Celé číslo roku | 2022 … 2024 |
month(field) | Celé číslo 1–12 | 1 … 12 |
day(field), week(field), quarter(field), dayofweek(field) | Celá čísla | |
| prosté pole celého čísla | Jakékoli celé číslo | @groupBy(month), @fillGaps(month) |
Měsíční trend s automaticky detekovaným rozsahem
orders(
@count as order_count,
@sum(total) as monthly_revenue,
@groupBy(yearmonth(created_at)),
@fillGaps(yearmonth(created_at))
) { }
Měsíce bez objednávek získají order_count=0 a monthly_revenue=0
Celoroční dashboard (explicitní rozsah)
orders(
@count as order_count,
@avg(total) as avg_order_value,
@groupBy(yearmonth(created_at)),
@fillGaps(yearmonth(created_at), from: "2024-01", to: "2024-12")
) { }
Vždy vrátí přesně 12 řádků, leden–prosinec 2024
Týdenní aktivita s nulovými metrikami pro prázdné týdny
events(
@count as event_count,
@avg(duration) as avg_duration,
@groupBy(yearweek(event_date)),
@fillGaps(yearweek(event_date))
) { }
Prázdné týdny získají event_count=0 a avg_duration=null
Seskupování celočíselných polí
products(
@count as product_count,
@groupBy(month),
@fillGaps(month, from: 1, to: 12)
) { }
- `@fillGaps` musí být použito společně s `@groupBy` na stejném poli
- Automatická detekce vyplňuje od minimální po maximální hodnotu přítomnou v datech
- Můžete zadat pouze `from`, pouze `to` nebo obojí pro jemné doladění
- Číselné hodnoty `from`/`to` jsou psány bez uvozovek; řetězcové hodnoty (yearmonth, yearweek) používají dvojité uvozovky
- Direktiva ovlivňuje pouze sadu výsledků; neupravuje data ani nespouští další SQL dotazy
Historické dotazy (@history)
Dotazování na historická data z archivních tabulek v kombinaci s aktuálním řádkem
Direktiva @history vám umožňuje dotazovat se na historická data z archivních tabulek. Pokud entity podporují archivaci, všechny změny záznamů jsou uchovávány ve speciálních archivních tabulkách, které udržují kompletní historii úprav a odstranění. Dotaz vrací jak historické verze, tak aktuální stav záznamů.
Jak to funguje
Direktiva @history používá SQL UNION ALL ke kombinaci:
- Řádky archivní tabulky — historické verze z
{entity}_archive - Aktuální řádek tabulky — aktuální stav z
{entity}
Výsledky jsou seřazeny podle _archiveValidTo DESC NULLS LAST, což znamená, že historické verze se zobrazují jako první (od nejnovější po nejstarší) a aktuální řádek se zobrazuje jako poslední.
Pole specifická pro archiv
| Pole | Typ | Popis |
|---|---|---|
_deleted | Boolean | Zda byl záznam odstraněn (true) nebo upraven (false). Vždy false pro aktuální řádek |
_deletedAt | DateTime | Časové razítko, kdy byl záznam odstraněn (null, pokud nebyl odstraněn nebo aktuální řádek) |
_archiveValidSince | DateTime | Začátek období, kdy byla tato verze platná. NOW() pro aktuální řádek |
_archiveValidTo | DateTime | Konec období, kdy byla tato verze platná. NULL pro aktuální řádek |
Dotaz na všechny historické verze plus aktuální řádek
products(@history) {
id
name
price
_deleted
_deletedAt
_archiveValidSince
_archiveValidTo
}
Dotaz k určitému bodu v čase (data k určitému datu)
products(
@history(from: "2025-01-01T00:00:00")
where: "price > 100"
) {
id
name
price
_archiveValidSince
_archiveValidTo
}
Najděte všechny změny konkrétního produktu
products(
@history
where: "id = 123"
) {
id
name
price
_archiveValidSince
_archiveValidTo
}
Najděte odstraněné záznamy
products(
@history
where: "_deleted = true"
) {
id
name
_deletedAt
_archiveValidSince
_archiveValidTo
}
Běžné případy použití
card: Auditní stopa
Sledujte všechny změny citlivých dat pro zajištění souladu
card: Cestování časem
Zobrazte data, jak existovala v kterémkoli bodě v minulosti
card: Analýza změn
Analyzujte vzorce v úpravách dat v průběhu času
card: Obnova odstraněných záznamů
Najděte a zkontrolujte odstraněné záznamy vedle aktuálních záznamů
- Ne všechny entity podporují archivaci. Pokud použijete @history na nepodporované entitě, obdržíte chybu
- Dotaz vždy zahrnuje aktuální stav záznamů jako poslední řádek(řádky) v sadě výsledků
- Archivní tabulky mohou obsahovat mnoho verzí, proto použijte příslušné klauzule where a parametry limit
- Časová razítka by měla být uvedena ve formátu ISO 8601 (např. "2025-01-01T12:00:00")
- Pro aktuální řádek je _archiveValidTo NULL a _deleted je vždy false
Filtrování pomocí klauzulí WHERE
Parametr where používá náš výkonný systém filtrů, který podporuje různé operátory, logické podmínky a filtrování vztahů. Seznamte se se všemi dostupnými operátory a prohlédněte si praktické příklady.
products(where: "price > 100 and category.name = 'Electronics