Фильтрация и итерация в read view с помощью CRUD
В этом разделе приведены подробные примеры использования операций select и pairs для read view с помощью
модуля CRUD.
Для выполнения примеров из этого руководства требуется настроенный кластер с подготовленными данными. Чтобы подготовить стенд, используйте инструкцию из запускаемого Docker-примера из GitHub-репозитория: read_view.
Содержание:
Чтобы создать read view, во вкладке Terminal вызовите функцию crud.readview():
rv = crud.readview()
Метод read_view_object:select() позволяет фильтровать кортежи по условиям.
Каждое условие должно использовать имя поля или имя индекса.
Первое условие с именем индекса используется для итерации по спейсам.
Если условия с именами индексов отсутствуют, выполняется полное сканирование (Map-Reduce).
Остальные условия используются в качестве дополнительных фильтров.
Условие поиска для индексируемого поля должно быть размещено первым, чтобы избежать
полного сканирования.
Чтобы избежать длинных выборок, можно ограничить количество результатов с помощью параметра first.
В примере ниже получены первые 6 кортежей из спейса customers:
rv:select('customers', nil, { first = 6 })
Запрос возвращает метаданные, массив кортежей и ошибку:
---- metadata: [ { 'name': 'id', 'type': 'integer' }, { 'name': 'bucket_id', 'type': 'unsigned' },{ 'name': 'name', 'type': 'string' }, { 'name': 'surname', 'type': 'string' }, { 'name': 'age','type': 'number' } ]rows:- [ 1, 12477, 'Elizabeth', 'Bagnall', 12 ]- [ 2, 21401, 'Mary', 'Bowman', 46 ]- [ 3, 11804, 'David', 'Bradley', 33 ]- [ 4, 28161, 'William', 'Bridgens', 81 ]- [ 5, 1172, 'Jack', 'Brown', 35 ]- [ 6, 13064, 'William', 'Long', 25 ]- null...
В примере по индексу age_index выбраны первые 10 клиентов, возраст которых больше или равен 20.
Так как первое условие — индекс age_index, результат отсортирован по возрасту.
rv:select('customers', { { '>=', 'age_index', 20 } }, { first = 10 })---- metadata: [ { 'name': 'id', 'type': 'integer' }, { 'name': 'bucket_id', 'type': 'unsigned' },{ 'name': 'name', 'type': 'string' }, { 'name': 'surname', 'type': 'string' }, { 'name': 'age','type': 'number' } ]rows:- [ 40, 6292, 'Evelyn', 'Mishra', 20 ]- [ 12, 16624, 'Olivia', 'Kinsella', 22 ]- [ 25, 158, 'Ava', 'Fisher', 23 ]- [ 34, 9834, 'Amelia', 'Ahmed', 24 ]- [ 6, 13064, 'William', 'Long', 25 ]- [ 23, 28454, 'Mia', 'Morrison', 25 ]- [ 20, 3826, 'Abigail', 'Gauld', 26 ]- [ 29, 17582, 'Chloe', 'Walters', 27 ]- [ 14, 24056, 'Emily', 'Grimshaw', 28 ]- [ 38, 26474, 'Avery', 'Murray', 28 ]- null...
Здесь запросы по индексу age_index и полю age эквивалентны, в обоих случаях поиск будет осуществляться по индексу без полного сканирования:
rv:select('customers', { { '>=', 'age_index', 20 } }, { first = 10 })rv:select('customers', { { '>=', 'age', 20 } }, { first = 10 })
Если имена индекса и поля совпадают, поиск также идет по индексу.
В примере используется составной индекс full_name, состоящий из полей name и surname.
Запрос возвращает 10 первых клиентов, имя и фамилия которых совпадают с условием:
rv:select('customers', { { '==', 'full_name', { 'Thomas', 'Griffin' } } }, { first = 10 })---- metadata: [ { 'name': 'id', 'type': 'integer' }, { 'name': 'bucket_id', 'type': 'unsigned' },{ 'name': 'name', 'type': 'string' }, { 'name': 'surname', 'type': 'string' }, { 'name': 'age','type': 'number' } ]rows:- [ 13, 14925, 'Thomas', 'Griffin', 64 ]- null...
В запросе также можно использовать часть составного ключа:
rv:select('customers', { { '==', 'full_name', 'William' } }, { first = 10 })---- metadata: [ { 'name': 'id', 'type': 'integer' }, { 'name': 'bucket_id', 'type': 'unsigned' },{ 'name': 'name', 'type': 'string' }, { 'name': 'surname', 'type': 'string' }, { 'name': 'age','type': 'number' } ]rows:- [ 4, 28161, 'William', 'Bridgens', 81 ]- [ 6, 13064, 'William', 'Long', 25 ]- [ 22, 21655, 'William', 'Norman', 42 ]- null...
Если указать в запросе не индексируемое поле, будет выполнено полное сканирование (Map-Reduce).
В примере выбраны первые 10 покупателей с заданной фамилией:
rv:select('customers', { { '==', 'surname', 'Wilcox' } }, { first = 10 })---- metadata: [ { 'name': 'id', 'type': 'integer' }, { 'name': 'bucket_id', 'type': 'unsigned' },{ 'name': 'name', 'type': 'string' }, { 'name': 'surname', 'type': 'string' }, { 'name': 'age','type': 'number' } ]rows:- [ 30, 29239, 'Jacob', 'Wilcox', 49 ]- null...
Метод read_view_object:pairs() позволяет итерироваться по распределенному спейсу.
В примере записаны в таблицу в виде кортежа первые 4 записи:
tuples = {}for _, tuple in rv:pairs('customers', nil, { first = 4 }) dotable.insert(tuples, tuple)endtuples
Вывод выглядит так:
---- - [ 1, 12477, 'Elizabeth', 'Bagnall', 12 ]- [ 2, 21401, 'Mary', 'Bowman', 46 ]- [ 3, 11804, 'David', 'Bradley', 33 ]- [ 4, 28161, 'William', 'Bridgens', 81 ]...
Чтобы итерироваться по объектам или плоским кортежам, используйте параметр use_tomap со значением true.
В примере в таблицу записаны в виде объектов первые 4 покупателя:
objects = {}for _, obj in rv:pairs('customers', nil, { use_tomap = true, first = 4 }) dotable.insert(objects, obj)endobjects
Вывод выглядит так:
---- - bucket_id: 12477id: 1surname: Bagnallage: 12name: Elizabeth- bucket_id: 21401id: 2surname: Bowmanage: 46name: Mary- bucket_id: 11804id: 3surname: Bradleyage: 33name: David- bucket_id: 28161id: 4surname: Bridgensage: 81name: William...
Операция pairs совместима с библиотекой Lua Fun.
Примеры работы с основными функциями из этой библиотеки приведены ниже.
Функция filter() возвращает новый итератор, элементы которого удовлетворяют предикату. В примере функция-предикат возвращает элементы, у которых значение поля возраста делится на 2 без остатка.
objects = {}for _, obj in rv:pairs('customers', nil, { first = 4, use_tomap = true }):filter(function(x)return x.age % 2 == 0end) dotable.insert(objects, obj)endobjects
Вывод выглядит так:
---- - bucket_id: 12477id: 1surname: Bagnallage: 12name: Elizabeth- bucket_id: 21401id: 2surname: Bowmanage: 46name: Mary...
Функция reduce (foldl) уменьшает итератор слева направо:
age_sum = crud.pairs('customers', nil, { use_tomap = true }):reduce(function(acc, x)return acc + x.ageend, 0)age_sum
Вывод выглядит так:
---- 1441...
Функция map() возвращает новый итератор, к каждому элементу которого была применена функция.
objects = {}for _, obj in rv:pairs('customers', nil, { first = 4, use_tomap = true }):map(function(x)return { id = x.id, name = x.name, age = x.age * 2 }end) dotable.insert(objects, obj)endobjects
Вывод выглядит так:
---- - age: 24name: Elizabethid: 1- age: 92name: Maryid: 2- age: 66name: Davidid: 3- age: 162name: Williamid: 4...
Функция take() возвращает итератор с заданным количеством последовательностей:
tuples = {}for _, tuple in rv:pairs('customers', { { '>=', 'age', 25 } }):take(2) dotable.insert(tuples, tuple)endtuples
Вывод выглядит так:
---- - [ 6, 13064, 'William', 'Long', 25 ]- [ 23, 28454, 'Mia', 'Morrison', 25 ]...