Query » Historie » Verze 10

Jakub Jirůtka, 2011-11-17 22:34
absolutní URL nahrazeny relativními

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 9 Jakub Jirůtka
RESTful Service Query Language (RSQL) 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 8 Jakub Jirůtka
h3. Gramatika a sémantika
27 1 Jakub Jirůtka
28 9 Jakub Jirůtka
RSQL(RESTful Service Query Language) 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 8 Jakub Jirůtka
 expression = [ "(" ],
31 8 Jakub Jirůtka
             ( constraint | expression ),
32 8 Jakub Jirůtka
             [ logical-operator, ( constraint | expression ) ],
33 8 Jakub Jirůtka
             [ ")" ];
34 1 Jakub Jirůtka
35 8 Jakub Jirůtka
Logické operátory jsou:
36 8 Jakub Jirůtka
* AND : "@;@" podle FIQL, nebo alternativní "@ and @"
37 8 Jakub Jirůtka
* OR : "@,@" podle FIQL, nebo alternativní "@ or @"
38 1 Jakub Jirůtka
39 8 Jakub Jirůtka
 logical-operator = ";" | " and " | "," | " or ";
40 1 Jakub Jirůtka
41 8 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í lze samozřejmě změnit pomocí uzávorkování výrazů.
42 3 Jakub Jirůtka
43 1 Jakub Jirůtka
44 8 Jakub Jirůtka
Kritérium se skládá ze selektoru, který identifikuje element v Atom Content, operátoru porovnání a argumentu.
45 1 Jakub Jirůtka
46 8 Jakub Jirůtka
 constraint = selector, comparison-operator, argument;
47 1 Jakub Jirůtka
48 8 Jakub Jirůtka
Operátory porovnání jsou:
49 8 Jakub Jirůtka
50 1 Jakub Jirůtka
|_. Název			|_. FIQL		|_. Alternativní		|_. Platné datové typy							|
51 1 Jakub Jirůtka
| rovná se			| @==@		| @=@				| textový řetězec, číslo, datum, výčtový typ, XLink	|
52 1 Jakub Jirůtka
| nerovná se		        | @!=@		| @!=@				| textový řetězec, číslo, datum, výčtový typ, XLink	|
53 1 Jakub Jirůtka
| menší než			| @=lt=@	| @<@				| číslo, datum									|
54 8 Jakub Jirůtka
| menší nebo rovno	| @=le=@	| @<=@				| číslo, datum									|
55 1 Jakub Jirůtka
| větší než			| @=gt=@	| @>@				| číslo, datum									|
56 1 Jakub Jirůtka
| větší nebo rovno	| @=ge=@	| @>=@				| číslo, datum									|
57 1 Jakub Jirůtka
58 8 Jakub Jirůtka
 comparison-operator = "==" | "=" | "!=" | "=lt=" | "<" | "=le=" | "<=" | "=gt=" | ">" | "=ge=" | ">=";
59 8 Jakub Jirůtka
60 8 Jakub Jirůtka
Selektor odpovídá názvu elementu v Atom Content nebo jeho relativní cestě, pakliže je zanořený. Může také obsahovat [[Query#„Dereference“-vazeb-aka-JOIN|„dereferenci“]] XLink vazby pomocí tečkové notace.
61 8 Jakub Jirůtka
62 8 Jakub Jirůtka
 selector   = identifier, { ("/" | "."), identifier };
63 8 Jakub Jirůtka
identifier = ? ["a"-"z","A"-"Z","_","0"-"9","-"]+ ?
64 8 Jakub Jirůtka
65 8 Jakub Jirůtka
Argumenty mohou být dvojího typu. Libovolná sekvence znaků uzavřená mezi jednoduché či dvojité uvozovky, nebo sekvence znaků bez mezer, kulatých závorek, čárek a středníků.
66 8 Jakub Jirůtka
67 8 Jakub Jirůtka
 argument    = arg_ws | arg_sq | arg_dq;
68 8 Jakub Jirůtka
argument-ws = ? ( ~["(", ")", ";", ",", " "] )+ ?;
69 8 Jakub Jirůtka
argument-sq = ? "'" ~["'"]+ "'" ?;
70 8 Jakub Jirůtka
argument-dq = ? "\"" ~["\""]+ "\"" ?;
71 8 Jakub Jirůtka
72 1 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]]).
73 1 Jakub Jirůtka
74 1 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).
75 5 Jakub Jirůtka
76 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.
77 1 Jakub Jirůtka
78 1 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.
79 5 Jakub Jirůtka
80 5 Jakub Jirůtka
h3. „Dereference“ vazeb (aka JOIN)
81 5 Jakub Jirůtka
82 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.
83 5 Jakub Jirůtka
84 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.
85 1 Jakub Jirůtka
86 1 Jakub Jirůtka
87 1 Jakub Jirůtka
h3. Parametry
88 1 Jakub Jirůtka
89 1 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]].
90 1 Jakub Jirůtka
91 7 Jakub Jirůtka
92 1 Jakub Jirůtka
h3. Příklady
93 1 Jakub Jirůtka
94 10 Jakub Jirůtka
* "<code>/courses?query=name==*prog*</code>":/api/3/courses?query=name=%2Aprog%2A - vrátí předměty, jejichž název obsahuje „prog“
95 10 Jakub Jirůtka
* "<code>/courses?query=name=='programování v*'</code>":/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“
96 10 Jakub Jirůtka
* "<code>/courses?query=credits>5</code>":/api/3/courses?query=credits%3E5 - vrátí předměty za více než 5 kreditů
97 10 Jakub Jirůtka
* "<code>/courses?query=season==WINTER;(completion==CLFD_CREDIT,completion==CREDIT)</code>":/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
98 10 Jakub Jirůtka
* "<code>/courses?query=department.unitType==FACULTY</code>":/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)
99 10 Jakub Jirůtka
* "<code>/teachers?query=extern==true&orderBy=lastName&maxResults=50</code>":/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ů
100 4 Jakub Jirůtka
101 4 Jakub Jirůtka
102 1 Jakub Jirůtka
h3. Pár slov k implementaci
103 6 Jakub Jirůtka
104 8 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í. Skládá se ze dvou navazujících knihoven. 
105 1 Jakub Jirůtka
106 8 Jakub Jirůtka
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 gramatika zapsaná v "JavaCC":http://javacc.java.net/, ze které je vygenerován vlastní parser.
107 6 Jakub Jirůtka
108 7 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).
109 8 Jakub Jirůtka
110 8 Jakub Jirůtka
Obě knihovny jsem uvolnil pod licencí LGPL a umístil na GitHub - "RSQL-parser":https://github.com/jirutka/rsql-parser a "RSQL-hibernate":https://github.com/jirutka/rsql-hibernate.
111 2 Jakub Jirůtka
112 2 Jakub Jirůtka
__
113 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.