[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

SyntaxPopisPříklad
entity(params) { fields }Výběr entity — specifikujte název entity následovaný volitelnými parametry v závorkáchproducts(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, idParametry — 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 aliasAliases — použijte klíčové slovo “as” pro přejmenování polí nebo relacícategory as product_category
name:cs, description:enPřeložitelná pole — vyberte konkrétní jazykové varianty pomocí syntaxe fieldName:languageCodename: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

FunkcePopisPříklad
lower(field)Převede řetězec na malá písmenalower(name)
upper(field)Převede řetězec na velká písmenaupper(code)
length(field)Získá délku řetězcového polelength(description)
isnull(field)Zkontroluje, zda je pole NULLisnull(email)

Funkce data / času

FunkcePopisPříklad
year(field)Extrahuje rok z data/datetimeyear(created_at)
month(field)Extrahuje měsíc z data/datetimemonth(birth_date)
day(field)Extrahuje den z data/datetimeday(due_date)
week(field)Extrahuje číslo týdne z dataweek(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

FunkcePopisPří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čkaPopis
AuthorizationBearer token pro autentizaci (např. Bearer your-jwt-token)
x-organisation-idIdentifikátor organizace pro vymezení kontextu dotazu
x-archive-atVolitelné časové razítko pro dotazy k určitému bodu v čase. Viz [navigate:/devdoc/changeset
x-changeset-idVolitelné ID changeset pro dotazování na konkrétní verze dat. Viz [navigate:/devdoc/changeset
Accept-LanguagePreference 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 from a/nebo to k rozšíření rozsahu za hranice toho, co data obsahují.
  • Agregace @count a @sum jsou vyplněny 0 pro chybějící řádky.
  • Agregace @avg, @min, @max jsou vyplněny null pro chybějící řádky.

Podporované typy polí

PoleFormátPří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 roku20222024
month(field)Celé číslo 1–12112
day(field), week(field), quarter(field), dayofweek(field)Celá čísla
prosté pole celého číslaJaké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

PoleTypPopis
_deletedBooleanZda byl záznam odstraněn (true) nebo upraven (false). Vždy false pro aktuální řádek
_deletedAtDateTimeČasové razítko, kdy byl záznam odstraněn (null, pokud nebyl odstraněn nebo aktuální řádek)
_archiveValidSinceDateTimeZačátek období, kdy byla tato verze platná. NOW() pro aktuální řádek
_archiveValidToDateTimeKonec 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