Query » Historie » Verze 7

Jakub Jirůtka, 2011-08-25 01:43
přestrukturizováno; význam RSQL změněn na RESTful Service Query Language

1 1 Jakub Jirůtka
h1. Vyhledávání
2 1 Jakub Jirůtka
3 2 Jakub Jirůtka
{{>toc}}
4 1 Jakub Jirůtka
5 2 Jakub Jirůtka
Komplexní podpora dotazování tvoří jednu ze základních funkcionalit databází. Oproti tomu většina běžných RESTových služeb nenabízí moc silné prostředky pro dotazování a omezuje se pouze na triviální předdefinované dotazy, příp. fulltextové vyhledávání. Ovšem mají-li webové služby IS(informačního systému) sloužit aplikacím jako přímý ^1^ zdroj dat, tak je komplexnější podpora vyhledávání prakticky nezbytná.
6 1 Jakub Jirůtka
7 1 Jakub Jirůtka
8 2 Jakub Jirůtka
h2. Předdefinované dotazy
9 1 Jakub Jirůtka
10 2 Jakub Jirůtka
Všechny zdroje s parametrem v URI(Uniform Resource Identifier) jsou v podstatě předdefinované vyhledávací dotazy. Kupříkladu "/units/18000":https://kosapi.fit.cvut.cz/api/3/units/18000/ vyhledá _organizační jednotku_ s kódem _18000_. Na pozadí dojde k vygenerování _SELECTu_ nad tabulkou nákladových středisek, kde kód střediska je rovný 18000. To je poměrně triviální dotaz. Trochu složitější se skrývá například za "/programmes/MI/courses":https://kosapi.fit.cvut.cz/api/3/programmes/MI/courses, který vyhledá všechny _předměty_ patřící pod _studijní program_ s kódem _MI_. Zde se vygeneruje polospojení nad programy, spojovou tabulkou a předměty, kde program má kód rovný MI.
11 1 Jakub Jirůtka
12 2 Jakub Jirůtka
Omezení takovýchto dotazů jsou zjevná. Co když potřebujeme například vyhledat všechny předměty, které se vyučují v zimním semestru, zajišťuje je Katedra softwarového inženýrství FIT a jejich název obsahuje slovo „prog“? Tady už potřebujeme nějaký dotazovací jazyk, který nám umožní kombinovat podmínky.
13 1 Jakub Jirůtka
14 2 Jakub Jirůtka
15 1 Jakub Jirůtka
h2. RSQL
16 1 Jakub Jirůtka
17 1 Jakub Jirůtka
*UPOZORNĚNÍ: Integrace RSQL ještě není úplně dokončená, takže pro některé zdroje a konkrétní atributy nemusí fungovat správně!*
18 1 Jakub Jirůtka
19 7 Jakub Jirůtka
RSQL(RESTful Service Query Language) je dotazovací jazyk a knihovna, jež jsem vyvinul pro KOSapi, která umožňuje vyhledávat záznamy (Atom Entry) podle jejich strukturovaných elementů (atributů) v Atom Content. Všechny zdroje KOSapi jsou koncipované tak, že Atom elementy využívají pouze pro metadata a vlastní data z KOSu jsou obsažená v Atom Content ve strukturované podobě (závisí na _Content-Type_, výchozí je XML(Extensible Markup Language)). RSQL(RESTful Service Query Language) dotazy se v KOSapi překládají na SQL(Structured Query Language) dotazy do KOSu.
20 1 Jakub Jirůtka
21 7 Jakub Jirůtka
Inspiroval jsem se "Feed Item Query Language":http://tools.ietf.org/html/draft-nottingham-atompub-fiql-00 (FIQL), což je IETF(Internet Engineering Task Force) návrh dotazovacího jazyka určeného (pouze) pro vyhledávání záznamů podle „metadat“ v Atom Entry. Syntaxe FIQL(Feed Item Query Language) je výhodná svým prvoplánovým určením pro zápis v URI(Uniform Resource Identifier), díky čemuž ji není potřeba zakódovávat. Na druhou stranu je tím poněkud neobvyklá a ne příliš intuitivní. Jelikož jsem si stejně musel napsat vlastní parser, rozhodl jsem se tuto syntaxi využít a rozšířit ji ještě o alternativní zápis.
22 1 Jakub Jirůtka
23 7 Jakub Jirůtka
Proč jsem vlastně vyvíjel vlastní řešení a nevyužil nějaké standardizované? Důvod je prostý, žádné takové kupodivu zatím neexistuje nebo jsem ho nenašel. Tedy kromě standardu "Open Data Protocol":http://www.odata.org, který mimo jiné zahrnuje komplexní podporu pro dotazování. Ovšem využití OData pro KOSapi jsem z několika důvodů zavrhl a vzhledem k tomu, že podpora vyhledávání je jeho „inherentní“ součástí, tak mi její _samostatné_ využití nepřišlo přínosné.
24 1 Jakub Jirůtka
25 7 Jakub Jirůtka
26 1 Jakub Jirůtka
h3. Struktura výrazu
27 1 Jakub Jirůtka
28 5 Jakub Jirůtka
Výraz se skládá z jednoho či více _kritérií_, které se spojují logickými (Booleovskými) operátory.
29 1 Jakub Jirůtka
30 1 Jakub Jirůtka
* _výraz_ = [ "(" ] ( _podmínka_ / _výraz_ ) [ _logický-operátor_ ( _podmínka_ / _výraz_ ) ] [ ")" ]
31 1 Jakub Jirůtka
* _podmínka_ = _selektor_ _operátor-porovnání_ _argument_
32 4 Jakub Jirůtka
* _logický-operátor_ = @";" / "&" / " and " / "," / "|" / " or "@
33 4 Jakub Jirůtka
* _operátor-porovnání_ = @"==" / "=" / "!=" / "=lt=" / "<" / "=le=" / "<=" / "=gt=" / ">" / "=ge=" / ">="@
34 5 Jakub Jirůtka
* _selektor_ = @["a"-"z","A"-"Z","_","0"-"9","-"] ("." ["a"-"z","A"-"Z","_","0"-"9","-"] )*@
35 4 Jakub Jirůtka
* _argument_ = @~["(",")",";",","," "]+ / "'" ~["'"]+ "'" / "\"" ~["\""]+ "\""@
36 1 Jakub Jirůtka
37 1 Jakub Jirůtka
38 1 Jakub Jirůtka
h3. Logické operátory
39 1 Jakub Jirůtka
40 3 Jakub Jirůtka
|_. Název	|_. FIQL		|_. Alternativní	|
41 3 Jakub Jirůtka
| AND		| @;@		| "@ and @"		|
42 3 Jakub Jirůtka
| OR		| @,@		| "@ or @"		|
43 1 Jakub Jirůtka
44 1 Jakub Jirůtka
Operátor AND má standardně přednost, tj. všechny operátory OR se vyhodnocují až po něm. Toto chování samozřejmě lze změnit pomocí uzávorkování výrazů.
45 1 Jakub Jirůtka
46 1 Jakub Jirůtka
47 1 Jakub Jirůtka
h3. Operátory porovnání
48 1 Jakub Jirůtka
49 1 Jakub Jirůtka
|_. Název			|_. FIQL		|_. Alternativní		|_. Platné datové typy							|
50 1 Jakub Jirůtka
| rovná se			| @==@		| @=@				| textový řetězec, číslo, datum, výčtový typ, XLink	|
51 1 Jakub Jirůtka
| nerovná se		        | @!=@		| @!=@				| textový řetězec, číslo, datum, výčtový typ, XLink	|
52 1 Jakub Jirůtka
| menší než			| @=lt=@	| @<@				| číslo, datum									|
53 1 Jakub Jirůtka
| menší nebo rovno	| @=le=@	| @<=@				| číslo, datum									|	
54 1 Jakub Jirůtka
| větší než			| @=gt=@	| @>@				| číslo, datum									|
55 1 Jakub Jirůtka
| větší nebo rovno	| @=ge=@	| @>=@				| číslo, datum									|
56 1 Jakub Jirůtka
57 7 Jakub Jirůtka
Porovnávání textových řetězců nezohledňuje velikost písmen (je _case insensitive_). Pokud je URL(Uniform Resource Locator) parametr [[URLParameters#multilang|multilang]] nastaven na @true@, tak zohledňuje texty v obou jazycích (neplatí pro [[Query#„Dereference“-vazeb-aka-JOIN|dereferencované]] atributy). V opačném případě vyhledává pouze ve zvoleném jazyce (podle Accept-Language, nebo  [[URLParameters#lang|lang]]).
58 1 Jakub Jirůtka
59 4 Jakub Jirůtka
Při porovnávání řetězců lze využít i _divoké karty_ a hledat pomocí nich i jen podle části řetězce. Způsob zápisu je stejný jako v SQL _LIKE_, pouze s tím rozdílem, že místo @%@ se zde používá @*@. Například podmínce @name=prog_am*@ vyhoví všechny předměty, jejichž název začíná na „prog“, následuje jeden libovolný znak, pak „am“ a cokoli (opět bez ohledu na velikost písmen).
60 1 Jakub Jirůtka
61 1 Jakub Jirůtka
V případě elementů, které reprezentují výčtový typ, je nutné jako argument uvádět _výčtový název_ (enum), nikoli jeho lokalizovaný popis.
62 1 Jakub Jirůtka
63 1 Jakub Jirůtka
Pakliže argument obsahuje mezery, kulaté závorky, čárku nebo dvojtečku, tak musí být uzavřen v uvozovkách (jednoduchých nebo dvojitých). V opačném případě jsou uvozovky nepovinné.
64 1 Jakub Jirůtka
65 5 Jakub Jirůtka
Argumentem pro XLink je identifikátor záznamu použitý v URI(Uniform Resource Identifier), což většinou bývá kód, nebo ID.
66 1 Jakub Jirůtka
67 1 Jakub Jirůtka
68 1 Jakub Jirůtka
h3. „Dereference“ vazeb (aka JOIN)
69 5 Jakub Jirůtka
70 5 Jakub Jirůtka
V dotazu je možné přistupovat i k atributům _odkazovaných_ zdrojů (na které vede XLink) pomocí tzv. „dereference“. Jinak řečeno umožňuje zápis podmínky s _implicitním_ spojením (_JOINem_) entit, mezi kterými existuje explicitní průchozí vazba (obdobně jako v HQL(Hibernate Query Language)). Vazbami se prochází pomocí tečkové notace a je možné i zanořování. Kupříkladu @unit.unitType==FACULTY@ vybere všechny záznamy, které jsou ve vztahu s organizační jednotkou _typu_ fakulta. Na pozadí dojde k vygenerování polospojení (_LEFT JOIN_) tabulky předmětů s tabulkou nákladových středisek a podmínky unitType=FACULTY.
71 5 Jakub Jirůtka
72 5 Jakub Jirůtka
Mějte prosím na paměti, že tyto dotazy mohou generovat velkou zátěž databáze. Jakmile bude KOSapi napojené přímo na KOS, bude tento problém dost citlivý. Používejte je proto obezřetně a vyhýbejte se zbytečně neefektivním dotazům. Z těchto důvodů jsem také omezil maximální počet implicitních _JOINů_ pro dotaz na 3.
73 5 Jakub Jirůtka
74 5 Jakub Jirůtka
75 1 Jakub Jirůtka
h3. Parametry
76 1 Jakub Jirůtka
77 7 Jakub Jirůtka
RSQL(RESTful Service Query Language) výraz se zapisuje do URL(Uniform Resource Locator) parametru [[URLParameters#query|query]] a je možné ho efektivně kombinovat s parametry [[URLParameters#startIndex|startIndex]], [[URLParameters#maxResults|maxResults]] a [[URLParameters#orderBy|orderBy]].
78 1 Jakub Jirůtka
79 1 Jakub Jirůtka
80 1 Jakub Jirůtka
h3. Příklady
81 1 Jakub Jirůtka
82 1 Jakub Jirůtka
* "<code>/courses?query=name==*prog*</code>":https://kosapi.fit.cvut.cz/api/3/courses?query=name=%2Aprog%2A - vrátí předměty, jejichž název obsahuje „prog“
83 1 Jakub Jirůtka
* "<code>/courses?query=name=='programování v*'</code>":https://kosapi.fit.cvut.cz/api/3/courses?query=name==%27programov%C3%A1n%C3%AD%20v*%27 - vrátí předměty, jejichž název začíná na „programování v“
84 1 Jakub Jirůtka
* "<code>/courses?query=credits>5</code>":https://kosapi.fit.cvut.cz/api/3/courses?query=credits%3E5 - vrátí předměty za více než 5 kreditů
85 4 Jakub Jirůtka
* "<code>/courses?query=season==WINTER;(completion==CLFD_CREDIT,completion==CREDIT)</code>":https://kosapi.fit.cvut.cz/api/3/courses?query=season==WINTER;%28completion==CLFD_CREDIT,completion==CREDIT%29 - vrátí předměty, které se vyučují v zimním semestru a jsou zakončené klasifikovaným zápočtem nebo zápočtem
86 4 Jakub Jirůtka
* "<code>/courses?query=department.unitType==FACULTY</code>":https://kosapi.fit.cvut.cz/api/3/courses?query=department.unitType==FACULTY - vrátí předměty, které zajišťuje přímo libovolná fakulta (tzn. organizační jednotka typu fakulta)
87 4 Jakub Jirůtka
* "/teachers?query=extern==true&orderBy=lastName&maxResults=50":https://kosapi.fit.cvut.cz/api/3/teachers?query=extern==true&orderBy=lastName&maxResults=50 - vrátí vyučující externisty, seřadí je podle příjmení a výstup omezí na max. 50 záznamů
88 1 Jakub Jirůtka
89 6 Jakub Jirůtka
90 1 Jakub Jirůtka
h3. Pár slov k implementaci
91 1 Jakub Jirůtka
92 7 Jakub Jirůtka
RSQL(RESTful Service Query Language) jsem vyvinul speciálně pro KOSapi, ale jeho návrh a implementace je dostatečně obecná i pro použití v jiných RESTových službách postavených nad relační databází. V dohledné době plánuji uvolnění zdrojových kódů pod open-source licencí.
93 6 Jakub Jirůtka
94 7 Jakub Jirůtka
Skládá se ze dvou navazujících knihoven. První je _RSQL-parser_, který provádí lexikální analýzu, parsování a sestavení objektové reprezentace zadaného RSQL(RESTful Service Query Language) výrazu. Součástí je lexikální specifikace a EBNF(Extended Backus–Naur Form) gramatika zapsaná v "JavaCC":http://javacc.java.net/, ze které je vygenerován vlastní parser.
95 6 Jakub Jirůtka
96 6 Jakub Jirůtka
Druhou knihovnou je _RSQL-hibernate_. Ta zajišťuje převod dotazu na "Hibernate Criteria Query":http://docs.jboss.org/hibernate/core/3.5/reference/en/html/querycriteria.html (objektová reprezentace HQL(Hibernate Query Language), resp. SQL(Structured Query Language) dotazu), z něhož se následně generuje SQL(Structured Query Language) dotaz do relační databáze. V tomto procesu hrají hlavní roli _RSQLCriteriaBuilder_, sada _CriterionBuilders_ a _Mapper_. _CriteriaBuilder_ prochází strom výrazu, generuje _Criterion_ pro logické výrazy (AND, OR) a deleguje _kritéria_ (porovnání) na odpovídající _CriterionBuilder_. Ty má připravené v kolekci, jíž iteruje dokud nenalezne takový, který umí obsloužit daný selektor a operátor. Kromě obecného _CriterionBuilder_ obsahuje například takový, který umí vytvořit _Criterion_ pro atribut vazby (i s NaturalID), multijazyčný text, selektor s implicitním JOINem, příp. speciální pro nestandardní entity. _Mapper_ zajišťuje mapování _selektorů_ (názvů v XML, příp. cest) na názvy příslušných _atributů_ v entitách. Většina odpovídá 1:1, ale v některých případech je nutné použít přemapování (např. multijazyčné texty).
97 2 Jakub Jirůtka
98 2 Jakub Jirůtka
__
99 2 Jakub Jirůtka
^1^ To znamená, že aplikace si nebudou uchovávat lokální kopii celé ani části databáze IS (cache se tím nevylučuje), ale budou je přímo získávat z webové služby.