Google Code предлагается на следующих языках: English – Español – 日本語 – 한국어 – Português – Pусский – 中文(简体) – 中文(繁體)
Каждый запрос к хранилищу данных использует индекс – таблицу, которая содержит результаты запроса, расположенные в необходимом порядке. Приложение App Engine определяет свои индексы в файле конфигурации datastore-indexes.xml. Веб-сервер разработки может автоматически создавать записи для этого файла, когда сталкивается с запросами, индексы для которых еще не настроены.
Механизм запросов, основанных на индексах, поддерживает наиболее распространенные типы запросов, но может не поддерживать некоторые типы запросов из других технологий баз данных, которые могут быть вам привычны. Ограничения, применяемые к запросам, вместе с соответствующими пояснениями перечислены ниже.
Запрос извлекает из хранилища данных объекты, удовлетворяющие заданному набору условий. В запросе указывается тип объекта, ноль или более условий, описывающих значения свойств объекта и иногда называемых "фильтрами", а также ноль или более описаний порядков сортировки. При выполнении запрос извлекает все объекты указанного типа, удовлетворяющие указанным условиям и отсортированные в заданном порядке.
JDO может выполнять запрашивать объекты, соответствующие определенным критериям. Также можно использовать Extent JDO для представления набора всех объектов определенного типа (все сохраненные объекты класса).
JDO включает язык запросов для получения объектов, соответствующих установленным критериям. Этот язык, называющийся JDOQL, использует поля и классы данных JDO напрямую и включает проверку типов для параметров и результатов запросов. JDOQL подобен SQL, но более подходит для объектно-ориентированных баз данных, таких как хранилище данных App Engine. (Хранилище данных App Engine не поддерживает запросы SQL с интерфейсом JDO.)
API запроса поддерживает несколько способов вызова. Можно указать весь запрос в строке, используя синтаксис строки JDOQL. Также можно указать некоторые или все части запроса путем вызова методов для объекта запроса.
Ниже приведен простой пример запроса, использующего вызов с помощью метода, с одним фильтром и одним порядком сортировки, а также подстановкой параметра для значения в фильтре. Метод execute() объекта Query вызывается со значениями для замены в запросе в порядке их объявления.
import java.util.List;
import javax.jdo.Query;
// ...
Query query = pm.newQuery(Employee.class);
query.setFilter("lastName == lastNameParam");
query.setOrdering("hireDate desc");
query.declareParameters("String lastNameParam");
try {
List<Employee> results = (List<Employee>) query.execute("Smith");
if (results.iterator().hasNext()) {
for (Employee e : results) {
// ...
}
} else {
// ... no results ...
}
} finally {
query.closeAll();
}
Ниже приведен этот же запроса, использующий синтаксис строки:
Query query = pm.newQuery("select from Employee " +
"where lastName == lastNameParam " +
"order by hireDate desc " +
"parameters String lastNameParam")
List<Employee> results = (List<Employee>) query.execute("Smith");
Можно сочетать эти способы определения запроса. Например:
Query query = pm.newQuery(Employee.class,
"lastName == lastNameParam order by hireDate desc");
query.declareParameters("String lastNameParam");
List<Employee> results = (List<Employee>) query.execute("Smith");
Можно повторно использовать один экземпляр Query с различными значениями, замененными на параметры путем вызова метода execute() несколько раз. Каждый вызов выполняет запрос и возвращает результаты в виде набора.
Синтаксис строки JDOQL поддерживает константы значений со строкой для строковых и числовых значений. Заключите строки в одинарные (') или двойные кавычки ("). Все остальные типы значений должны использовать подстановку параметров. Ниже приведен пример использования значения строкового литерала:
Query query = pm.newQuery(Employee.class,
"lastName == 'Smith' order by hireDate desc");
Фильтр указывает название поля, оператор и значение. Значение должно быть предоставлено приложением; оно не может ссылаться на другое свойство или быть вычислено через другие свойства. Оператор может быть одним из следующих:< <= == >= >
Примечание. Интерфейс хранилища данных Java не поддерживает операторы фильтра != и IN, реализованные в интерфейсе хранилища данных Python. (В интерфейсе Python эти операции реализованы в библиотеках на стороне клиента как несколько запросов к хранилищу данных; они не являются функциями самого хранилища.)
Объектом фильтра может быть любое поле объекта, включая первичный ключ и родителя группы объектов (см. Транзакции).
Чтобы являться результатом, объект должен соответствовать всем фильтрам. В синтаксисе строки JDOQL несколько фильтров разделяются знаком && (логическое "и"). Другие логические сочетания фильтров (логическое "или", "нет") не поддерживаются.
Из-за способа выполнения запросов хранилищем данных App Engine одиночный запрос не может использовать фильтры неравенства (< <= >= >) для нескольких свойств. Несколько фильтров неравенства для одного свойства (например, запрос диапазона значений) разрешены. См. раздел Ограничения, применяемые к запросам ниже.
query.setFilter("lastName == 'Smith' && hireDate > hireDateMinimum");
query.declareParameters("Date hireDateMinimum");
Порядок сортировки указывает свойство и направление, по возрастанию или по убыванию. Результаты возвращаются отсортированными в определенных порядках в порядке, в котором они были указаны. Если для запроса не указаны порядки сортировки, результаты сортируются по ключам объектов.
Из-за способа выполнения запросов хранилищем данных App Engine, если запрос указывает фильтры неравенства для свойства и порядки сортировки для других свойств, свойство, использованное с фильтрами неравенства, должно быть отсортировано до других свойств. См. раздел Ограничения, применяемые к запросам ниже.
query.setOrdering("hireDate desc, firstName asc");
Запрос может указывать диапазон результатов, которые должны быть возвращены приложению. Диапазон определяет первый и последний результаты в полном наборе результатов, которые должны быть возвращены, используя цифровые индексы, начиная с 0 для первого результата. Например, диапазон 5, 10 возвращает 6-й, 7-й, 8-й, 9-й и 10-й результаты.
Начальное смещение влияет на производительность: хранилище данных должно получить, а затем удалить все результаты до начального смещения. Например, запрос с диапазоном 5, 10 получает 10 результатов из хранилища данных, затем удаляет первые 5 и возвращает оставшиеся 5 результатов приложению.
query.setRange(5, 10);
Extent JDO представляет все объекты в хранилище данных определенного класса
Можно начать Extent используя метод getExtent() PersistenceManager, передавая ему класс данных. Класс Extent реализует итеративный интерфейс для получения результатов. После получения результатов вызывается метод closeAll().
В следующем примере просматриваются все объекты Employee в хранилище данных:
import java.util.Iterator;
import javax.jdo.Extent;
// ...
Extent extent = pm.getExtent(Employee.class, false);
for (Employee e : extent) {
// ...
}
extent.closeAll();
Extent получает результаты в пакетах и может превышать предел в 1000 результатов, применяемый для запросов.
Хранилище данных App Engine поддерживает индекс для каждого запроса, который собирается выполнить приложение. Как только приложение изменяет объекты хранилища данных, хранилище обновляет индексы, корректируя результаты. Когда приложение выполняет запрос, хранилище данных получает результаты напрямую из соответствующего индекса.
У приложения есть индекс для каждой комбинации типа, оператора фильтра, свойства фильтра и порядка сортировки, указанных в запросе. Рассмотрим запрос, написанный на JDOQL:
select from Person where lastName == "Smith"
&& height < 72
order by height desc
Индекс для этого запроса представляет собой таблицу ключей объектов типа Person, содержащую столбцы значений свойств height и lastName. Индекс отсортирован в убывающем порядке по свойству height.
Для двух запросов одинаковой формы, но с разными значениями фильтров используется один и тот же индекс. Например, для следующего запроса используется тот же индекс, что и для запроса выше:
select from Person where lastName == "Jones"
&& height < 64
order by height desc
Хранилище данных выполняет запрос следующим образом:
В таблице индекса содержатся столбцы для каждого свойства, используемого в фильтре или порядке сортировки. Строки отсортированы по следующим критериям в указанном порядке:
Это позволяет расположить результаты всевозможных запросов, использующих данный индекс, в последовательных строках таблицы.
Этот механизм поддерживает широкий спектр запросов и подходит для большинства приложений. Однако он не поддерживает некоторые типы запросов из других технологий баз данных, которые могут быть вам привычны.
В индексе содержатся только объекты, обладающие всеми свойствами, на которые он ссылается. Если у объекта нет свойства, на которое ссылается индекс, этот объект не появится в индексе и не сможет быть результатом запроса, использующего данный индекс.
Обратите внимание, что хранилище данных App Engine различает объекты, которые не обладают свойством, и объекты, которые обладают свойством с нулевым значением (null). Чтобы каждый объект определенного типа мог стать потенциальным результатом выполнения запроса, можно использовать класс данных JDO или JPA, который всегда присваивает значения всем свойствам, соответствующим полям класса.
Свойства, имеющие значения типа Text (большие текстовые значения) или Blob (большие двоичные значения), не включаются в индексы и не обнаруживаются запросами.
Вследствие того, что такие значения свойств не включаются в индекс, запрос, содержащий фильтр или порядок сортировки по некоторому свойству, никогда не вернет объект, у которого это свойство имеет значение типа Text или Blob. Свойства с такими значениями обрабатываются так, как если бы их значения не были заданы относительно фильтров и порядков сортировки запроса.
Если у двух объектов есть свойства с одинаковыми названиями, но разными типами значений, индекс этого свойства сортирует объекты сначала по типу значений, а затем в соответствующем каждому типу порядке. Например, если у двух объектов есть свойство "age", у одного с целочисленным значением, а у второго – со строковым, при сортировке по свойству "Age" объект с целочисленным значением всегда будет появляться прежде, чем объект со строковым, вне зависимости от самих значений.
Это особенно важно помнить при работе с целыми числами и числами с плавающей точкой, которые хранилище данных считает разными типами. Свойство с целым значением 38 после сортировки окажется раньше по порядку, чем значение с плавающей точкой 37.5, поскольку значения типа integer всегда выводятся раньше значений типа float.
(При работе с JDO или JPA такая ситуация может возникнуть только в том случае, если вы измените тип поля, не обновив существующие в хранилище данных объекты, или будете использовать низкоуровневый API хранилища данных либо API без поддержки Java.)
App Engine создает индексы для ряда простых запросов по умолчанию. Индексы, необходимые для других запросов, приложение должно указывать в файле конфигурации datastore-indexes.xml. Если приложение, работающее под управлением App Engine, пытается выполнить запрос, для которого отсутствует соответствующий индекс (заданный по умолчанию или указанный в файле datastore-indexes.xml), то этот запрос не будет выполнен.
App Engine автоматически создает индексы для следующих форм запросов:
Для других форм запросов индексы необходимо указывать в файле datastore-indexes.xml. К ним относятся:
Веб-сервер разработки облегчает управление конфигурацией индексов: вместо отказа выполнить запрос, для которого требуется отсутствующий индекс, он может создать конфигурацию индекса, которая позволит успешно выполнить запрос. Если во время локального тестирования приложения вызываются все запросы, которые может выполнять приложение (все комбинации типа, родителя, фильтра и порядка сортировки), созданные записи будут представлять собой полный набор индексов. Если во время тестирования были выполнены не все возможные формы запросов, перед загрузкой приложения просмотрите и настройте конфигурацию индексов.
Можно определить индексы вручную, используя файл конфигурации datastore-indexes.xml в каталоге WEB-INF/ WAR приложения. Если включена автоматическая конфигурация индексов (см. ниже), сервер разработки создает конфигурацию индекса в файле datastore-indexes-auto.xml в каталоге WEB-INF/appengine-generated/ и использует оба файла для определения полного набора индексов.
Рассмотрите еще раз следующий пример запроса:
select from Person where lastName = 'Smith'
&& height < 72
order by height desc
Конфигурация для индекса, необходимого для данного запроса, выглядит в datastore-indexes.xml следующим образом:
<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes
xmlns="http://appengine.google.com/ns/datastore-indexes/1.0"
autoGenerate="true">
<datastore-index kind="Person" ancestor="false">
<property name="lastName" direction="asc" />
<property name="height" direction="desc" />
</datastore-index>
</datastore-indexes>
Если элемент <datastore-indexes> в datastore-indexes.xml имеет атрибут autoGenerate="true" (как выше), или если в приложении отсутствует файл datastore-indexes.xml, включена автоматическая конфигурация индекса. Если при включенной автоматической конфигурации индекса приложение выполняет этот запрос на сервере разработки, и отсутствует конфигурация для индекса, сервер добавляет эту конфигурацию индекса в файл datastore-indexes-auto.xml.
Дополнительную информацию о datastore-indexes.xml и datastore-indexes-auto.xml см. в статье Конфигурация индекса хранилища данных Java.
Для ключей объектов может применяться фильтр запроса или порядок сортировки. В JDO ссылка на ключ объекта в запросе создается с помощью поля первичного ключа объекта. Хранилище данных получает полное значение ключа для таких запросов, включая путь родителя объекта, тип и назначенную приложением строку имени ключа или назначенный системой числовой идентификатор.
Поскольку ключ объекта уникален среди всех объектов в системе, запросы упрощают получение объектов определенного типа пакетами, например, пакетный дамп содержания хранилища данных. В отличие от диапазонов JDOQL это эффективно работает для любого количества объектов.
Упорядочивание ключей происходит в следующей очередности: по пути к родителю, по типу, а затем по идентификатору или названию ключа. Типы и названия ключей являются строками и упорядочиваются по байтовому значению. Идентификаторы являются целыми числами и упорядочиваются численно. Если объекты с одинаковым родителем и типом используют как строковые названия ключей, так и числовые идентификаторы, считается, что объекты с числовыми идентификаторами меньше, чем объекты со строковыми названиями ключей. Аналогично сравниваются и элементы пути к родителю: по типу (строка), затем по названию ключа (строка) или идентификатору (число).
Запросы, содержащие ключи, используют индексы так же, как запросы, содержащие свойства, с одним небольшим отличием: запрос с фильтром равенства по ключу, в котором есть и другие фильтры, должен использовать пользовательский индекс, определяемый в файле конфигурации индекса приложения. Как и для других запросов, при проверке запроса, которому требуется пользовательский индекс, веб-сервер разработки делает в этом файле соответствующие записи о конфигурации.
Природа механизма запросов с использованием индексов налагает на действия запросов ряд ограничений.
Условие фильтра запроса или порядок сортировки по свойству также требует, чтобы для данного свойства объекта было задано значение.
Объект хранилища данных необязательно должен иметь значение для свойства, для которого есть значения у других объектов того же типа. Фильтр по свойству применяется только к тем объектам, для которых значение этого свойства задано. Объекты, для которых значение свойства, используемого в фильтре или порядке сортировки, не задано, исключаются из индекса, создаваемого для запроса.
Выполнить запрос для объектов, у которых отсутствует заданное свойство, невозможно. Вариантом решения является создание фиксированного (смоделированного) свойства со значением по умолчанию null с последующей фильтрацией объектов со значением свойства null.
Запрос может использовать фильтры неравенства (<, <=, >=,>) только по одному свойству среди всех своих фильтров.
Например, следующий запрос допустим:
select from Person where birthYear >= minBirthYearParam
&& birthYear <= maxBirthYearParam
Однако следующий запрос недопустим, так как он использует фильтры неравенства по двум различным свойствам одновременно:
select from Person where birthYear >= minBirthYearParam
&& height >= minHeightParam // ERROR
Фильтры могут сочетать операторы равенства (==) для различных свойств в одном запросе, в том числе в запросах с одним или несколькими условиями неравенства для свойства. Вот пример допустимого запроса:
select from Person where lastName == lastNameParam
&& city == cityParam
&& birthYear >= minBirthYearParam
Механизм запросов предполагает, что все результаты запроса расположены в таблице индекса подряд, во избежание необходимости сканировать всю таблицу результатов. Одна таблица индекса не может содержать несколько фильтров неравенства по разным свойствам, одновременно обеспечивая расположение результатов подряд.
Если запрос содержит фильтр неравенства и один или несколько порядков сортировки, в нем также должна выполняться сортировка по свойству, используемому в неравенстве, причем перед сортировкой по другим свойствам.
Следующий запрос недопустим, так как он использует фильтр неравенства и не выполняет сортировку по используемому в этом фильтре свойству:
select from Person where birthYear >= minBirthYearParam
order by lastName // ERROR
Недопустим и следующий запрос, так как сортировка по используемому в фильтре свойству не выполняется перед сортировкой по другим свойствам:
select from Person where birthYear > minBirthYearParam
order by lastName, birthYear // ERROR
Следующий запрос допустим:
select from Person where birthYear >= minBirthYearParam
order by birthYear, lastName
Чтобы получить результаты, соответствующие фильтру неравенства, запрос сканирует таблицу индекса начиная с первой подходящей строки и возвращает результаты до тех пор, пока не найдет строку, не удовлетворяющую условиям фильтра. Чтобы полный набор результатов был представлен в таблице подряд, ее строки должны быть отсортированы по фильтру неравенства перед применением других порядков сортировки.
При индексации свойств, имеющих несколько значений, используется необычный порядок сортировки:
Такой порядок сортировки имеет необычное следствие: значения [1, 9] будут располагаться перед значениями [4, 5, 6, 7] и при сортировке по возрастанию, и по убыванию.
Следует сделать важное предупреждение относительно запросов, содержащих как фильтр равенства, так и порядок сортировки по свойству с несколькими значениями. В таких запросах порядок сортировки не учитывается. Для свойств с одним значением это обеспечивает простую оптимизацию. У всех результатов значение свойства будет одинаковым, поэтому необходимости дополнительно их сортировать нет.
Однако при наличии у свойства дополнительных значений могут возникать трудности. Так как указанная сортировка не применяется, результаты запроса могут возвращаться в другом порядке, чем необходимо. (Восстановление проигнорированного порядка сортировки требует лишних затрат и дополнительных индексов. Описанный случай возникает редко, поэтому планировщик запросов не рассматривает его.)
Как описано выше, каждое свойство (значение которого имеет тип, отличный от Text или Blob) каждого объекта добавляется по крайней мере в одну таблицу индекса, в том числе в простой индекс, задаваемый по умолчанию, и любые другие индексы, описанные в файле приложения datastore-indexes.xml и ссылающиеся на данное свойство. Для объекта, каждое свойство которого имеет только одно значение, App Engine сохраняет значение свойства в своем простом индексе единожды, а в пользовательском индексе столько раз, сколько существует обращений к данному свойству. Каждую из таких записей в индексах необходимо обновлять после каждого изменения значения свойства, поэтому чем больше индексов ссылаются на свойство, тем больше потребуется времени на его обновление.
Чтобы избежать длительного обновления объекта, хранилище данных ограничивает количество записей индекса, относящихся к одному объекту. Это ограничение достаточно велико, поэтому на работу большинства приложений оно не повлияет. Однако при некоторых обстоятельствах вы можете испытать действие данного ограничения. Например, ограничение на количество записей в индексе может превысить объект, имеющий очень много свойств с одним значением.
Каждое значение свойств, имеющих несколько значений, сохраняется в отдельной записи индекса. Объект, имеющий единственное свойство с очень большим количеством значений может превысить ограничение на количество записей в индексе.
Пользовательские индексы, ссылающиеся на несколько свойств с несколькими значениями, могут стать слишком большими очень быстро. Чтобы полностью записать такие свойства, таблица индекса должна содержать строку для каждой комбинации значений каждого свойства в индексе.
Например, следующий индекс (использован синтаксис datastore-indexes.xml) содержит свойства x и y объектов типа MyModel:
<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes>
<datastore-index kind="MyModel">
<property name="x" direction="asc" />
<property name="y" direction="asc" />
</datastore-index>
</datastore-indexes>
В следующем фрагменте кода создается объект с двумя значениями свойства x и двумя значениями свойства y:
MyModel m = new MyModel();
m.setX(Arrays.asList("one", "two"));
m.setY(Arrays.asList("three", "four"));
pm.makePersistent(m);
Чтобы точно представить эти значения, нужно сохранить в индексе 12 значений свойств: два для встроенных индексов по свойствам x и y и по два для каждой из четырех комбинаций свойств x и y в пользовательском индексе. Если свойство имеет много значений, это значит, что в индексе необходимо хранить слишком много записей для одного объекта. Индекс, который ссылается на несколько свойств с несколькими значениями, можно назвать "разрастающимся", так как он может стать очень большим при добавлении всего нескольких значений.
Если вызов метода put() приведет к превышению ограничения на количество записей, возникнет ошибка и будет вызвано исключение. Если при построении нового индекса количество записей в нем превысит заданное ограничение для какого-либо объекта, запросы по данному индексу не будут выполняться, а сам индекс появится в консоли администрирования с состоянием "Error".
Можно избежать разрастания индексов, не выполняя запросы, для которых необходим пользовательский индекс, использующий списковое свойство. Как описано выше, в эту категорию входят запросы с несколькими порядками сортировки, а также сочетаниями фильтров равенства, неравенства и родителей.