javascript

Что же такое RQL

  • вторник, 4 июля 2017 г. в 19:11:07
https://habrahabr.ru/company/odin_ingram_micro/blog/331774/
  • Разработка веб-сайтов
  • JavaScript
  • Блог компании Odin (Ingram Micro)


Представьте, что у вас есть хранилище данных с REST-интерфейсом. Пусть в нем хранится информация о книгах и вы хотите вывести список всех книг. Можно сделать метод «books», который будет возвращать нам список книг. Но при отображении списка обычно есть паджинация или ленивая подгрузка данных, а еще пользовать хочет фильтровать и сортировать данные. Когда мы добавляем поддержку мобильных устройств у нас появляется еще потребность как-то ограничить объем получаемых данных не передавая часть полей. Всю эту информацию должен уметь понимать почти любой метод получения списка объектов, т.к. списки отображаются с помощью специального виджета. И тут нам на помощь приходит Resource Query Language.

Resource Query Language (RQL) — это язык запросов, разработанный для использования в URI при работе с объекто-подобными структурами данных. С помощью RQL клиент может запрашивать у сервера список объектов соответствующих определенным правилам, т.е., по сути, это синтаксис, который описывает как запрашивать данные. Например, запрос выбирающий все книги авторства Перумова может быть записан как eq(author,Перумов) или в обычном формате URL: author=Перумов.

RQL можно рассматривать, в основном, как набор вложенных операторов, каждый из которых имеет множество аргументов. Он рассчитан на очень простую, но расширяемую грамматику, которая может быть использована в валидном URL запросе.

Как использовать RQL


RQL выражение содержит функции запроса или операторы сравнения, соединенные логическими операторами.

Типовые сценарии


Сначала разберем типовые сценарии. Для лучшего понимания будем сравнивать с аналогичными командами в MongoDB.

Пусть у нас есть список книг:

[
{ title: "Эльфийский клинок", year: 1993, series: "Кольцо тьмы" },
{ title: "Чёрное копьё", year: 1993, series: "Кольцо тьмы" },
{ title: "Адамант Хенны", year: 1995, series: "Кольцо тьмы" },
{ title: "Воин Великой Тьмы", year: 1995, series: "Летописи Хьёрварда" }
]

Выведем все книги из серии «Кольцо тьмы». В MongoDB мы бы сделали это так:

db.users.find({series: "Кольцо тьмы"})

в RQL это будет выглядеть так:

eq(series, "Кольцо тьмы")

или

series="Кольцо тьмы"

Такой запрос вернет нам три книги.

Теперь более сложный запрос: нам надо вывести все книги серии «Кольцо тьмы» изданные в 1995 году.

В MongoDB:

db.users.find({series: "Кольцо тьмы", year: 1995})

В RQL:

eq(series, "Кольцо тьмы"),eq(year, 1995)


Предыдущие запросы применялись к простым объектам. Но документы могут быть очень сложными по структуре. Например, добавим в нашу библиотеку новую книгу:

{ title: "Воин Великой Тьмы", year: 1995, series: "Летописи Хьёрварда", translations: { language: "English", title: "Godsdoom" } }


Здесь определяется вложенный объект с ключом translations. И чтобы найти все книги переведенные на английский язык, нам надо использовать точку.

В MongoDB:

db.users.find({"translations.language": "English"})

В RQL:

eq(translations.language, "English")

Со временем наша библиотека выросла. Список книг не помещается на экран и мы решили показывать его постранично.

В MongoDB мы можем получить второй десяток записей следующим образом:

db.users.find().skip(10).limit(10)

В RQL:

limit(10,10)

Но показывать постранично мало. Еще хочется сортировать данные.

В MongoDB это будет:

db.users.find().sort({title: 1})

В RQL:

sort(+title) 

Функции


Базовые функции стандарта:
Функция Описание Примеры
in (<propеrty>,<array-of-values>) Выбирает объекты, у которых значение
указанного свойства входит в заданный массив свойств.
in(name,(Silver,Gold))
out (<propеrty>,<array-of-values>) Выбирает объекты, у которых значение указанного свойства не входит в заданный массив свойств. out(name,(Platinum,Gold))
limit (<stаrt>,<numbеr>) Возвращает заданное количество
(number)
объектов начиная с определённой (start) позиции.
limit(0,2)
sort (<list of properties with + or — prefix>) Сортирует список объектов по заданным свойствам (количество свойств неограниченно). Сначала список сортируется по первому из заданных свойств, затем по второму, и так далее.
Порядок сортировки определяется префиксом: + — по возрастанию, — — по убыванию.
sort(+hardware.memory,-hardware.diskspace)
select (<list оf attributes>) Обрезает каждый объект до набора свойств, определенных в аргументах. elect(name,hardware.memory,user)
select(name,hardware.memory,user.fullName)
values(<prоperty>) Возвращает набор значений из указанного поля всех объектов values(ve.name)
count() Возвращает количество записей in(name,(Silver,Gold))&count()
max(<prоperty?>) Возвращает максимальное значение из указанного поля всех объектов max(ve.memory)
min(<prоperty?>) Возвращает минимальное значение из указанного поля всех объектов min(ve.memory)

Больше существующих операторов можно найти в официальной документации (http://www.persvr.org/rql/).

В технологии APS в RQL добавлена новая функция:
Функция Описание Примеры
like (<prоperty>, <pаttern>) Ищет заданный паттерн (pattern) в заданном свойстве (property). Эта
функция аналогична оператору SQL LIKE, хоть и использует символ * вместо %. Чтобы определить в паттерне сам символ *, он должен быть процентно-кодированным, то есть надо писать %2A вместо *, см. примеры. Кроме того, в паттерне можно использовать символ ?, обозначающий, что любой символ в этой позиции является валидным.
like(firstName,Jo*)
like(firstName,*ohn)
like(firstName,*oh*)
like(firstName,Joh?)

И еще три специфичные для APS функции:
Функция Описание Примеры
implementing (<basе-type>) Возвращает список объектов (ресурсы и типы), реализующих базовый тип и включающих в себя сам базовый тип. aps-standard.org/samples/user-mgmt/offer/1.0
composing (<dеrived-type>) Возвращает список типов, которые реализованы производным типом (derived type), включая сам производный тип. aps-standard.org/samples/user-mgmt/offer/1.0
linkedWith (<rеsource ID>) Возвращает список ресурсов, которые связаны тем ресурсом, чей ID указан в качестве аргумента. APS-контроллер ищет все ссылки на ресурсы, включая внутренние системные ссылки. Например, актор admin/owner/referrer, имеющий доступ к ресурсу, тоже будет считаться «связанным» ресурсом. linkedWith(220aa29a-4ff4-460b-963d-f4a3ba093a0a)

implementing(http://aps-standard.org/types/core/user/service/1.0), linkedWith(220aa29a-4ff4-460b-963d-f4a3ba093a0a)

Логические операторы


Логические операторы позволяют посредством булевой логики объединить две и больше функций запроса. Все стандартные логические операторы имеют короткие алиасы.
Оператор Алиас Примеры Значение
and (<quеry>,<quеry>,...) &
,
and (http://aps-standard.org/samples/user-mgmt/offer),like(name,*L*))

implementing(http://aps-standard.org/samples/user-mgmt/offer)&like(name,*L*))

implementing(http://aps-standard.org/samples/user-mgmt/offer),like(name,*L*))
Выбирает все предложения (offers), имена которых соответствуют нечувствительному к регистру паттерну *L*
or (<quеry>,<quеry>,...) |
;
implementing(http://aps-standard.org/samples/user-mgmt/offer1.0)&or(like(description,*free*),in(name,(Silver,Gold))) Выбирает все предложения (offers), описания (description) которых соответствуют паттерну *free*, а также те, чьё имя Silver или Gold.

В технологии APS в RQL также добавлен новый логический оператор отрицания:
Оператор Алиас Примеры Значение
not (<quеry>) implementing(http://aps-standard.org/samples/user-mgmt/offer),not(like(name,*free*)) Выбирает все предложения (offers), за исключением тех, чьё имя соответствует
Хабр и Гиктаймс — RQL паттерну *free*.

Примечание
  1. Оператор and является неявным RQL-оператором верхнего уровня. Например, выражение http://hosting.com?and(arg1,arg2,arg3) эквивалентно http://hosting.com?arg1,arg2,arg3.
  2. У оператора and приоритет выше, чем у or. Поэтому при использовании алиасов нужно заключать объединённые запросы в круглые скобки, тем самым определяя необходимый порядок обработки. Например, implementing(<typе>),(prop1=eq=1|prop2=ge=2).


Операторы сравнения


Оператор сравнения используется для фильтрации объектов посредством сравнения одного из их свойств с заданным значением.
Оператор Алиас Примеры Значение
eq (<propеrty>,<valuе>) =eq= eq(aps.status,aps:ready)
aps.status=eq=aps:ready
Выбирает все объекты, чей aps.status имеет значение aps:ready.
ne (<propеrty>,<valuе>) =ne= ne(aps.status,aps:ready)
aps.status=ne=aps:ready
Выбирает все объекты, чей aps.status имеет значение aps:ready.
gt (<propеrty>,<valuе>) =gt= implementing(http://aps-standard.org/samples/user-mgmt/offer),hardware.memory=gt=1024) Выбирает все предложения (offers), предоставляющие hardware.memory больше 1024.
ge (<propеrty>,<valuе>) =ge= implementing(http://aps-standard.org/samples/user-mgmt/offer),hardware.memory=ge=1024) Выбирает все предложения (offers), предоставляющие hardware.memory больше или равно 1024.
lt (<propеrty>,<valuе>) =lt= implementing(http://aps-standard.org/samples/user-mgmt/offer),hardware.CPU.number=lt=16) Выбирает все предложения (offers), предоставляющие hardware.CPU.number меньше 16.
le (<propеrty>,<valuе>) =le= implementing(http://aps-standard.org/samples/user-mgmt/offer),hardware.CPU.number=le=16) Выбирает все предложения (offers), предоставляющие hardware.CPU.number меньше или равно 16.

Строковые типы сравниваются в лексикографическом порядке.

Значения


Функции запросов и операторы сравнения могут содержать следующие значения:

  • Строковые (с использованием URL-кодирования)
  • Числа
  • Даты (в формате ISO UTC без двоеточия)
  • Булевы
  • Функции-значения (Value functions)

Функции-значения — это функции, возвращающие особые значения вроде null, true, false или пустое строковое значение. Все они применимы к определённым типам данных.
Функция-значение Применимые типы Описания Примеры
null() Любой тип Задаётся, если значение null name=eq=null()
true()
false()
Булевы Задаётся, если значение true или false disabled=eq=false()
empty() Строковые Задаётся, если строковое значение является пустым (не null, но не содержит никаких символов) addressPostal.extendedAddress=eq=empty()

Использование


Существует большое количество реализации парсеров RQL на различных языках программирования.

Реализация на JavaScript кроме парсера содержит еще и движок, который умеет применять RQL-запрос к массиву объектов.

Оригинальная реализация RQL на JavaScript есть в npmjs: https://www.npmjs.com/package/rql

Реализация с добавленной нами функциональностью также доступна через npm: https://www.npmjs.com/package/aps-rql