Описание клиентского языка запросов.

Пространство имен Query

В аддоне ember-flexberry-data экспортируется пространство имен Query, в котором содержатся классы для работы с языком запросов. Для импорта пространства имен Query необходимо использовать следующий код:

import { Query } from 'ember-flexberry-data';

В пространстве имен Query содержится следующий перечень классов:

  • BaseAdapter - базовый класс для адаптеров языка запросов, которые преобразуют объект запроса в запрос для конкретного типа бэкэнда.
  • BaseBuilder - базовый класс для построителя запросов.
  • Builder - класс для построения запросов (построитель запросов).
  • Condition - перечисление с логическими операциями для Query.ComplexPredicate.
  • FilterOperator - перечисление с операциями для Query.SimplePredicate.
  • IndexedDbAdapter - адаптер языка запросов для построения запросов к IndexedDB из объекта запроса.
  • JsAdapter - адаптер языка запросов для построения функций фильтраций JavaScript-массивов из объекта запроса.
  • OdataAdapter - адаптер языка запросов для построения запросов согласно спецификации OData из объекта запроса.
  • OrderByClause - класс для формирования order-by части запроса.
  • BasePredicate - базовый класс для классов предикатов.
  • SimplePredicate - класс для создания фильтра в запросе по значению атрибута и указанной операции.
  • ComplexPredicate - класс для создания фильтра в запросе из нескольких предикатов, объединенных логическими операциями.
  • StringPredicate - класс для создания фильтра в запросе по строковым полям.
  • DetailPredicate - класс для создания фильтра в запросе по детейловым объектам.
  • DatePredicate - класс для создания фильтра в запросе по полям с типом дата.
  • NotPredicate - класс для инверсии вложенного предиката.
  • TruePredicate - класс для создания фильтра с заведомо выполняемым условием.
  • FalsePredicate - класс для создания фильтра с заведомо невыполняемым условием.
  • IsOfPredicate - класс для создания фильтра в запросе по типу объекта данных.
  • InPredicate - класс для создания фильтра в запросе по значению атрибута, входящего в заданный список значени.
  • createPredicate - метод для создания предиката по заданным параметрам.

Пример использования классов из пространства имен:

let builder = new Query.Builder(store);

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

import { Query } from 'ember-flexberry-data';
const { Builder, FilterOperator } = Query;

let builder = new Builder(store, 'customer').where('firstName', FilterOperator.Eq, 'Vasya');

Query.Builder

Query.Builder - класс для построения запроса.

Конструктор

Конструктор Query.Builder может принимать 2 параметра: обязательный store, и modelName - наименование сущности запроса (наименование модели для вычитки).

let builder = new Query.Builder(store);

let builder = new Query.Builder(store, 'customer');

Вычитывание объекта по ключу

Метод byId принимает параметр строку или число в качестве ключа объекта (GUID необходимо передавать в нижнем регистре).

let builder = new Query.Builder(this.store)
    .from(modelName)
    .selectByProjection(projectionName)
    .byId('3087fbdc-273e-4bae-b440-071fd1eab1e0');

Наименование сущности

builder.from('employee');

Ограничение

Предикат - функция, возвращающая true/false. Предикат может быть:

  • Простым
  • Сложным
  • Строковым
  • Детейловым
builder.where(Query.SomePredicate);

или

let builder = new Query.Builder(store, 'customer').where('firstName', Query.FilterOperator.Eq, 'Vasya');

Для мастера

let builder = new Query.Builder(store, 'customer').where('manager', Query.FilterOperator.Eq, '3bcc4730-9cc1-4237-a843-c4b1de881d7c');

Для поля мастера

let builder = new Query.Builder(store, 'customer').where('manager.firstName', Query.FilterOperator.Eq, 'Vasya');

Сортировка

builder.orderBy('age desc, price asc');

По полю мастера:

builder.orderBy('creator.age desc, price asc');

Возвращение первых N записей

builder.top(N);

Пропуск N записей

builder.skip(N);

Подсчет количества результатов

builder.count();

Задание атрибутов для запроса

Проекция и задание атрибутов вычитки (select) - взаимоисключающие вещи, необходимо использовать одно из этого!

builder.select('id,age,name');

Задание проекции для запроса

Проекция и задание атрибутов вычитки(select) - взаимоисключающие вещи, необходимо использовать одно из этого!

builder.selectByProjection('EmployeeTestProjection');

Создание экземпляра запроса на основе заданных данных

builder.build();

Передача экземпляра в query

store.query(modelName, builder.build());

Предикаты

Query.SimplePredicate

Query.SimplePredicate - класс для простых предикатов для фильтрации поля по значению и указанному оператору.

Конструктор

Конструктор Query.SimplePredicate принимает 3 параметра: attributePath - путь атрибута, operator - оператор, value - значение.

let predicate = new Query.SimplePredicate('name', Query.FilterOperator.Eq, 'Vasya');

Получение свойств предиката

Получение пути атрибута:

predicate.attributePath

Получение оператора:

predicate.operator

Получение значения фильтрации:

predicate.value

Query.ComplexPredicate

Query.ComplexPredicate - класс для сложных предикатов, объединяющий несколько предикатов логическими условиями.

Конструктор

Конструктор Query.ComplexPredicate принимает 2 параметра: condition - логическое условия для предикатов, ...predicates - список предикатов для объединения.

let sp1 = new Query.SimplePredicate('name', Query.FilterOperator.Eq, 'Vasya');
let sp2 = new Query.SimplePredicate('surname', Query.FilterOperator.Eq, 'Ivanov');
let cp1 = new Query.ComplexPredicate(Query.Condition.Or, sp1, sp2);

Другой вариант:

let p1 = new Query.SimplePredicate('name', Query.FilterOperator.Eq, 'Vasya');
let p2 = new Query.SimplePredicate('surname', Query.FilterOperator.Eq, 'Ivanov');
let result = p1.or(p2);

Получение свойств предиката

Получение логического условия:

complexPredicate.condition

Получение списка предикатов:

complexPredicate.predicates

Добавление к существующему сложному предикату новых.

Возьмем cp1 из примера для конструктора. Добавление через and:

let sp3 = new Query.SimplePredicate('nationality', Query.FilterOperator.Eq, 'Russian');
let result = cp1.and(sp3);

Добавление через or:

let sp3 = new Query.SimplePredicate('nationality', Query.FilterOperator.Eq, 'Russian');
let result = cp1.or(sp3);

Query.StringPredicate

Query.StringPredicate - класс для построения фильтров по строковым полям.

Конструктор

Конструктор Query.StringPredicate принимает единственный параметр: attributePath - путь атрибута предиката.

Поиск по подстроке

Добавление значения, которое заданный в конструкторе атрибут должен содержать:

let sp1 = new Query.StringPredicate('country').contains('i');

Добавление значения, которое атрибут мастера должен содержать:

let sp1 = new Query.StringPredicate('country.name').contains('i');

Получение свойств предиката

Получение пути атрибута:

predicate.attributePath

Получение значения, которое должно содержаться в атрибуте:

predicate.containsValue

Query.DetailPredicate

Query.DetailPredicate - класс для построения фильтров по детейлам.

Конструктор

Конструктор Query.DetailPredicate принимает единственный параметр - наименование детейла.

let dp = new Query.DetailPredicate('detailName')

Добавление простого предиката для всех детейлов

dp.all(new Query.SimplePredicate('name', Query.FilterOperator.Eq, 'Tag1'));

или

let p = new Query.DetailPredicate('detailName').all('field', Query.FilterOperator.Eq, 'Value');

Для поля мастера

dp.all(new Query.SimplePredicate('creator.name', Query.FilterOperator.Eq, 'X'));

Добавление простого предиката для любого детейла

dp.any(new Query.SimplePredicate('name', Query.FilterOperator.Eq, 'Tag1'));

или

let p = new Query.DetailPredicate('detailName').any('field', Query.FilterOperator.Eq, 'Value');

Для поля мастера

dp.any(new Query.SimplePredicate('creator.name', Query.FilterOperator.Eq, 'X'));

Добавление сложного предиката для всех детейлов

let sp1 = new Query.SimplePredicate('name', Query.FilterOperator.Eq, 'Tag1');
let sp2 = new Query.SimplePredicate('name', Query.FilterOperator.Eq, 'Tag3');
let cp1 = new Query.ComplexPredicate(Query.Condition.Or, sp1, sp2);
let dp = new Query.DetailPredicate('tags').all(cp1);

Для поля мастера

let sp1 = new Query.SimplePredicate('creator.name', Query.FilterOperator.Eq, 'X');
let sp2 = new Query.SimplePredicate('creator.name', Query.FilterOperator.Eq, 'Z');
let cp1 = new Query.ComplexPredicate(Query.Condition.Or, sp1, sp2);
let dp = new Query.DetailPredicate('tags').all(cp1);

Добавление сложного предиката для любого детейла

let sp1 = new Query.SimplePredicate('name', Query.FilterOperator.Eq, 'Tag1');
let sp2 = new Query.SimplePredicate('name', Query.FilterOperator.Eq, 'Tag2');
let cp1 = new Query.ComplexPredicate(Query.Condition.Or, sp1, sp2);
let dp = new Query.DetailPredicate('tags').any(cp1);

Для поля мастера

let sp1 = new Query.SimplePredicate('creator.name', Query.FilterOperator.Eq, 'X');
let sp2 = new Query.SimplePredicate('creator.name', Query.FilterOperator.Eq, 'Y');
let cp1 = new Query.ComplexPredicate(Query.Condition.Or, sp1, sp2);
let dp = new Query.DetailPredicate('tags').any(cp1);

Query.DatePredicate

Query.DatePredicate - класс для предикатов для фильтрации поля с типом дата по значению и указанному оператору.

Конструктор

Конструктор Query.DatePredicate принимает 4 параметра: attributePath - путь атрибута, operator - оператор, value - значение и timeless - флаг указывающий нужно ли учитывать время при сравнении дат (если true, то время не учитывается).

let predicate = new Query.DatePredicate('birthday', Query.FilterOperator.Eq, '2018-02-06T11:00:00.000Z');
let predicate = new Query.DatePredicate('birthday', Query.FilterOperator.Eq, '2018-02-06', true);

Получение свойств предиката

Получение пути атрибута:

predicate.attributePath

Получение оператора:

predicate.operator

Получение значения фильтрации:

predicate.value

Получение флага учета времени:

predicate.timeless

Query.NotPredicate

Query.NotPredicate – класс для инверсии вложенного предиката.

Конструктор

Конструктор Query.NotPredicate принимает единственный параметр – другой предикат.

let np = new Query.NotPredicate(new Query.SimplePredicate('creator.name', Query.FilterOperator.Eq, 'X'));

Получение свойств предиката

Получение вложенного предиката:

let innerPredicate = np.predicate;

Query.IsOfPredicate

Конструктор

Конструктор Query.IsOfPredicate с двумя параметрами принимает строку с выражением, указывающим на свойство модели, и строку с именем модели.

let predicate = new Query.IsOFPredicate('manager', 'employee');

Конструктор Query.IsOfPredicate с одним параметром принимает строку с именем модели.

let predicate = new Query.IsOFPredicate('customer');

Есть метод упрощающий работу с этим предикатом.

let predicate = new Query.IsOFPredicate('man');
let builder = new Query.Builder(store, 'creator').where(predicate);

Равнозначное выражение без использования предиката.

let builder = new Query.Builder(store, 'creator').isOf('man');

Query.InPredicate

Query.InPredicate - класс для создания фильтра в запросе по значению атрибута, входящего в заданный список значени.

Конструктор

Конструктор Query.InPredicate принимает 2 параметра: attributePath - путь атрибута, valueArray - массив значений, для проверки вхождения в него значения атрибута.

let predicate = new Query.InPredicate('name', ['Vasya', 'Petia', 'Ivan']);

Получение свойств предиката

Получение пути атрибута:

predicate.attributePath

Получение массива значений:

predicate.valueArray

Логические операторы для сложных предикатов

Or

Query.Condition.Or

let cp1 = new Query.ComplexPredicate(Query.Condition.Or, sp1, sp2);

And

Query.Condition.And

let cp1 = new Query.ComplexPredicate(Query.Condition.And, sp1, sp2);

Операторы для фильтрации данных для простых предикатов

Равенство

Query.FilterOperator.Eq

let sp1 = new Query.SimplePredicate('name', Query.FilterOperator.Eq, 'Tag1');

Неравенство

Query.FilterOperator.Neq

let sp1 = new Query.SimplePredicate('name', Query.FilterOperator.Neq, 'Tag1');

Больше

Query.FilterOperator.Ge

let builder = new Query.QueryBuilder(store, modelName).where('age', Query.FilterOperator.Ge, 10);

Больше или равно

Query.FilterOperator.Geq

let builder = new Query.QueryBuilder(store, modelName).where('age', Query.FilterOperator.Geq, 11);

Меньше

Query.FilterOperator.Le

let builder = new Query.QueryBuilder(store, modelName).where('age', Query.FilterOperator.Le, 12);

Меньше или равно

Query.FilterOperator.Leq

let builder = new Query.QueryBuilder(store, modelName).where('age', Query.FilterOperator.Leq, 11);

Создание запроса для фильтрации перечислений

По названию и типу перечисления берется name и пишется в предикат:

let enumValues = Ember.getOwner(this).lookup('enum:' + filter.type);
let pattern = '';

for (let key in enumValues) {
    if (enumValues[key] === filter.pattern) {
        pattern = key;
        break;
    }
}

switch (filter.condition) {
    case 'Равно':
    return new SimplePredicate(filter.name, FilterOperator.Eq, pattern);
    case 'Не равно':
    return new SimplePredicate(filter.name, FilterOperator.Neq, pattern);
    // по умолчанию Равно
    default:
    return new SimplePredicate(filter.name, FilterOperator.Eq, pattern);
}

Пример комплексного запроса

let builder = new Query.QueryBuilder(store)
      .from('customer')
      .select('id,firstName,lastName,age')
      .where('firstName', Query.FilterOperator.Eq, 'Vasya')
      .top(50)
      .skip(100)
      .count();