• Официальный сайт SDK
  • Сайт с примерами кода

Запросы и индексы

Каждый запрос к хранилищу использует индексы - таблицу, содержащую результаты для запроса в заданном порядке. Приложение App Engine может определить свои индексы с помощью конфигурационного файла index.yaml. Сервер разработки автоматически добавляет в него предположения о новых требуемых индексах на основе данных запросов, которые он обрабатывает. У разработчика имеется возможность дополнительно задать другие индексы или изменить параметры до того, как он загрузит приложение на сервер.

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

Введение в запросы

Запрос выполняет загрузку объектов из хранилища, которые удовлетворяют определенному набору условий. В запросе указывается тип объекта, ноль или несколько условий, основанных на значениях свойств объектов (иногда называемых "фильтры") и ноль или несколько условий сортировки. После запуска запроса он возвращает все объекты заданного типа, которые удовлетворяют заданным условиям и отсортированы в требуемом порядке.

Интерфейс Datastore API предлагает два варианта подготовки и выполнения запросов: интерфейс Query, который использует методы для подготовки запроса, и интерфейс GqlQuery, который использует похожий на SQL язык запросов, названный GQL, для формирования строки описания запроса. Эти интерфейсы подробнее описаны в разделе Создание, получение и удаление данных: Получение объектов с помощью запроса и в соответствующих разделах документации.

CmNsYXNzIFBlcnNvbihkYi5Nb2RlbCk6CmZpcnN0X25hbWUgPSBkYi5TdHJpbmdQcm9wZXJ0eSgpCmxhc3RfbmFtZSA9IGRiLlN0cmluZ1Byb3BlcnR5KCkKY2l0eSA9IGRiLlN0cmluZ1Byb3BlcnR5KCkKYmlydGhfeWVhciA9IGRiLkludGVnZXJQcm9wZXJ0eSgpCmhlaWdodCA9IGRiLkludGVnZXJQcm9wZXJ0eSgpCiMg0JjQvdGC0LXRgNGE0LXQudGBIFF1ZXJ5INC/0L7QtNCz0L7RgtCw0LLQu9C40LLQsNC10YIg0LfQsNC/0YDQvtGBINGBINC/0L7QvNC+0YnRjNGOINC80LXRgtC+0LTQvtCyINGN0LrQt9C10LzQv9C70Y/RgNCwLgpxID0gUGVyc29uLmFsbCgpCnEuZmlsdGVyKCJsYXN0X25hbWUgPSIsICJTbWl0aCIpCnEuZmlsdGVyKCJoZWlnaHQgJmx0OyIsIDcyKQpxLm9yZGVyKCItaGVpZ2h0IikKIyDQmNC90YLQtdGA0YTQtdC50YEgR3FsUXVlcnkg0L/QvtC00LPQvtGC0LDQstC70LjQstCw0LXRgiDQt9Cw0L/RgNC+0YEg0YEg0LjRgdC/0L7Qu9GM0LfQvtCy0LDQvdC40LXQvCDQstGL0YDQsNC20LXQvdC40Y8g0L3QsCDRj9C30YvQutC1IEdRTC4KcSA9IGRiLkdxbFF1ZXJ5KCJTRUxFQ1QgKiBGUk9NIFBlcnNvbiAiICsKIldIRVJFIGxhc3RfbmFtZSA9IDoxIEFORCBoZWlnaHQgJmx0OyA6MiAiICsKIk9SREVSIEJZIGhlaWdodCBERVNDIiwKIlNtaXRoIiwgNzIpCiMg0JfQsNC/0YDQvtGBINC90LUg0LHRg9C00LXRgiDQstGL0L/QvtC70L3QtdC9INC00L4g0YLQtdGFINC/0L7RgCwg0L/QvtC60LAg0L/RgNC40LvQvtC20LXQvdC40LUg0L/QtdGA0LLRi9C5INGA0LDQtyDQvdC1INC+0LHRgNCw0YLQuNGC0YHRjyDQuiDQv9C10YDQtdC80LXQvdC90L7QuSByZXN1bHRzLgpyZXN1bHRzID0gcS5mZXRjaCg1KQpmb3IgcCBpbiByZXN1bHRzOgpwcmludCAiJXMgJXMsICVkIGluY2hlcyB0YWxsIiAlIChwLmZpcnN0X25hbWUsIHAubGFzdF9uYW1lLCBwLmhlaWdodCkK=

Введение в индексы

Хранилище платформы App Engine ведет индексы для каждого запроса, какой может выполнить приложение. После того, как приложение производит модификацию объектов, хранилище обновляет информацию в своих индексах. Когда приложение выполняет запрос, хранилище получает его результаты напрямую из соответствующего индекса.

Приложение имеет отдельный индекс для каждой комбинации значений типа, фильтра свойств и порядка сортировки, задаваемых в запросе. Рассмотрим пример запроса, приведенного выше:

ClNFTEVDVCAqIEZST00gUGVyc29uIFdIRVJFIGxhc3RfbmFtZSA9ICJTbWl0aCIKQU5EIGhlaWdodCAmbHQ7IDcyCk9SREVSIEJZIGhlaWdodCBERVNDCg===

Индексом для этого запроса будет таблица ключей объекта типа Person с колонками значений свойств height и last_name. Индекс будет отсортирован по значению height в убывающем порядке.

Два запроса одной формы, но с разными значениями параметров будут использовать один и тот же индекс. К примеру, следующий запрос использует тот же индекс, что и предыдущий:

ClNFTEVDVCAqIEZST00gUGVyc29uIFdIRVJFIGxhc3RfbmFtZSA9ICJKb25lcyIKQU5EIGhlaWdodCAmbHQ7IDYzCk9SREVSIEJZIGhlaWdodCBERVNDCg===

Хранилище выполняет запрос по следующим шагам:

  1. Определяет соответствующий запросу индекс на основе значений типа объекта, параметров фильтра, его условий и порядка сортировки.
  2. Выполняет сканирование индексов с первого объекта, который удовлетворяет условиям фильтра запроса.
  3. Продолжает сканирование индекса, возвращая каждый объект, пока не доберется до неудовлетворяющих фильтру объектов или конца индекса.

Индексная таблица содержит колонки значений для каждого свойства, используемого в фильтре или порядке сортировки. Строки сортируются по следующим принципам по порядку:

  • предки объекта
  • значения свойств, используемые в равенствах или условиях IN фильтров
  • значения свойств, используемые в фильтрах неравенств
  • значения свойств, используемые в порядке сортировок

Примечание: Для создания индексов фильтры условий IN обрабатываются также как и условия =, а условия != обрабатываются как все прочие условия неравенств.

Это обобщает в одну таблицу результаты для всех возможных запросов, которые будут использовать этот индекс.

Такой алгоритм поддерживает широкий спектр запросов и подходит для работы большинства приложений. Однако, некоторые виды запросов, которые доступны для традиционных баз данных, не могут быть осуществимы. Для дополнительной информации, смотрите раздел Ограничения в запросах по тексту ниже.

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

db.GqlQuery("SELECT * FROM MyModel WHERE prop >= :1 AND prop < :2", "abc", "abc" + "xEFxBFxBD")

Запрос выберет все объекты типа MyModel, содержащие в свойстве prop строку, которая начинается с символов abc. Массив байт "xEFxBFxBD" представляет собой максимально возможные значения символов в кодировке юникод. После того, как значения свойств объектов будут отсортированы в индексе по этому условию, удовлетворяющие ему значения будут входить в диапазон строк с заданным префиксом.

Определение индексов в index.yaml

Платформа App Engine по умолчанию автоматически создает несколько простых индексов. Для других запросов приложение должно явно указать, какие индексы будут ему необходимы, в конфигурационном файле index.yaml. Если приложение при работе под App Engine попытается выполнить запрос, которому не будет сопоставлен соответствующий индекс (созданный по умолчанию или описанный в index.yaml), запрос не будет произведен.

App Engine автоматически генерирует индексы для следующих форм запросов:

  • запросы, использующие только условия равенства, IN и фильтры предков
  • запросы, использующие только условия неравенства (которые могут быть заданы только для одного свойства)
  • запросы только с сортировкой в порядке возрастания

Все прочие формы запросов требуют явного указания их индексов в файле index.yaml, включая:

  • запросы с сортировкой в порядке убывания
  • запросы с несколькими сортировками
  • запросы, содержащие один или несколько неравенств со одними свойствами и один или несколько равенств или IN с другими свойствами
  • запросы с фильтрами неравенств и предками объектов

Отладочный сервер разработки (dev_appserver.py) выполняет самостоятельное изменение файла index.yaml: Вместо аварийного завершения запроса, который не имеет соответствующий индекс, он добавляет его определение в конфигурационный файл и продолжает его исполнение.

Если при локальном тестировании приложения вы проведете выполнение всех возможных запросов (все комбинации типа объекта, его предков, фильтров и сортировок), то все необходимые приложению индексы уже будут созданы. Если в процессе тестирования нет возможности провести все возможные запросы, вы можете вручную добавить определения необходимых индексов до публикации приложения на сервере.

Подсказка: Если сервер dev_appserver.py будет запущен с параметром --require_indexes, возможность автоматического добавления индексов в файл index.yaml будет отключена и запросы, которые требуют индекс и не найдут его, выдадут ошибку. Тестирование приложения с этим параметром позволяет проверить, что все необходимые индексы уже определены.

В файле index.yaml описывается каждая индексная таблица, включая тип объекта, его свойства, используемые в фильтрах и сортировках, и будет или нет в запросе задействовано указание предка объекта (метод Query.ancestor() или выражение GQL ANCESTOR IS). Свойства перечисляются в порядке, в котором они будут отсортированы: сначала свойства, используемые в операциях сравнения и условиях IN, затем свойства, используемые в фильтрах неравенств, и напоследок, направления сортировки.

Рассмотрим еще раз пример запроса:

ClNFTEVDVCAqIEZST00gUGVyc29uIFdIRVJFIGxhc3RfbmFtZSA9ICJTbWl0aCIKQU5EIGhlaWdodCAmbHQ7IDcyCk9SREVSIEJZIGhlaWdodCBERVNDCg===

Если приложение выполняет только этот запрос (и возможно другие запросы, отличающимися от этого различными параметрами для "Smith" и 72), файл index.yaml будет выглядеть так:

CmluZGV4ZXM6Ci0ga2luZDogUGVyc29uCnByb3BlcnRpZXM6Ci0gbmFtZTogbGFzdF9uYW1lCi0gbmFtZTogaGVpZ2h0CmRpcmVjdGlvbjogZGVzYwo==

Когда изменяется какой-либо объект или создается новый, каждый соответствующий ему индекс будет обновлен. Количество индексов влияет на время, которое занимает создание или обновление каждого объекта.

Для дополнительной информации о синтаксисе файла index.yaml обратитесь к разделу Настройка индексов.

Ограничения в запросах

Характер реализации механизма работы с индексами накладывает некоторые ограничения на возможности запросов.

Фильтрация или сортировка по свойству требует того, чтобы свойство существовало

Задание условия для фильтра запроса или порядка сортировки по какому-то свойству объекта подразумевает, что это значение в объекте будет существовать.

Хранилище не требует того, чтобы в каждом объекте одного типа были заданы значения во всех свойствах. Таким образом, фильтр задающий условие по параметру, вернет только те объекты, значения свойств которых установлены. Объекты без заданных значений свойств, по которым производится выборка, будут отсутствовать в индексе, используемом для выполнения запроса.

Нет возможности делать выборки только тех объектов, свойства которых не существуют

Не существует способа осуществлять запросы объектов по условию, где будут отсутствовать заданное свойство. Для обхода этого ограничения можно создать статическое свойство в модели со значением по умолчанию None и затем определить фильтр для объектов со значением условия None.

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

Запрос может содержать условия неравенств (<, <=, >=,> и !=) только для одного свойства, указанного в фильтре запроса.

Пример допустимого запроса на языке GQL:

ClNFTEVDVCAqIEZST00gUGVyc29uIFdIRVJFIGJpcnRoX3llYXIgJmd0Oz0gOm1pbgpBTkQgYmlydGhfeWVhciAmbHQ7PSA6bWF4Cg===

Однако, следующий запрос не допускается, так как он использует условие неравенств в двух различных свойствах одного запроса:

ClNFTEVDVCAqIEZST00gUGVyc29uIFdIRVJFIGJpcnRoX3llYXIgJmd0Oz0gOm1pbl95ZWFyCkFORCBoZWlnaHQgJmd0Oz0gOm1pbl9oZWlnaHQgIyDQntCo0JjQkdCa0JAK=

Фильтры могут комбинировать условия равенства (=) с несколькими различными свойствами в одном запросе, включая одно или несколько условий неравенства для одного из свойств. Допустимое выражение:

ClNFTEVDVCAqIEZST00gUGVyc29uIFdIRVJFIGxhc3RfbmFtZSA9IDpsYXN0X25hbWUKQU5EIGNpdHkgPSA6Y2l0eQpBTkQgYmlydGhfeWVhciAmZ3Q7PSA6bWluX3llYXIK=

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

При использовании условий неравенств со свойствами, порядок сортировки должен быть указан в первую очередь по ним

Если запрос содержит фильтр с условием неравенства свойства и задает порядок сортировки по одному или нескольким значениям, то в порядке сортировки должно быть задано это используемое в условии свойство еще до того, как будут определены остальные значения сортировки.

Этот GQL запрос не является допустимым, так как использует условие неравенства и не определяет порядок сортировки по используемому в фильтре полю:

ClNFTEVDVCAqIEZST00gUGVyc29uIFdIRVJFIGJpcnRoX3llYXIgJmd0Oz0gOm1pbl95ZWFyCk9SREVSIEJZIGxhc3RfbmFtZSAjINCe0KjQmNCR0JrQkAo==

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

ClNFTEVDVCAqIEZST00gUGVyc29uIFdIRVJFIGJpcnRoX3llYXIgJmd0Oz0gOm1pbl95ZWFyCk9SREVSIEJZIGxhc3RfbmFtZSwgYmlydGhfeWVhciAjINCe0KjQmNCR0JrQkAo==

Этот запрос является верным:

ClNFTEVDVCAqIEZST00gUGVyc29uIFdIRVJFIGJpcnRoX3llYXIgJmd0Oz0gOm1pbl95ZWFyCk9SREVSIEJZIGJpcnRoX3llYXIsIGxhc3RfbmFtZQo==

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