Google Code disponible en: English - Español - 日本語 - 한국어 - Português - Pусский - 中文(简体) - 中文(繁體)
Cada consulta del almacén de datos utiliza un índice, una tabla que contiene los resultados de la consulta en el orden deseado. Una aplicación App Engine define sus índices en un archivo de configuración con el nombre datastore-indexes.xml. El servidor web de desarrollo genera automáticamente sugerencias a este archivo a medida que encuentra consultas que no disponen aún de índices configurados.
El mecanismo de consultas basado en índices admite los tipos de consultas más habituales, pero no admite determinados tipos de consultas procedentes de otras tecnologías de bases de datos a los que es posible que estés acostumbrado. A continuación, se describen las restricciones que se aplican a las consultas y sus explicaciones.
Una consulta recupera entidades del almacén de datos que cumplen un conjunto de condiciones. La consulta especifica un tipo de entidad, cero o más condiciones basadas en valores de propiedad de la entidad (a veces llamados "filtros") y cero o más descripciones de criterios de ordenación. Al ejecutar una consulta, se extraen todas las entidades del tipo concreto que cumplen todas las condiciones especificadas, ordenadas de la forma descrita.
JDO puede realizar consultas para entidades que cumplen determinados criterios. También puedes utilizar una instancia Extent de JDO para representar la colección de cada tipo de entidad (cada objeto almacenado de una clase).
JDO incluye un lenguaje de consulta para recuperar objetos que cumplen una serie de criterios. Este lenguaje, denominado JDOQL, hace referencia a los campos y clases de datos JDO e incluye una comprobación de tipos para los resultados y parámetros de la consulta. JDOQL es similar a SQL, aunque es más adecuado para bases de datos relacionadas con objetos, como, por ejemplo, el almacén de datos de App Engine. (El almacén de datos de App Engine no admite consultas SQL a través de la interfaz JDO).
El API de consulta admite varios estilos de ejecución. Puedes especificar una consulta completa en una cadena mediante la sintaxis de cadena JDOQL. También puedes especificar parte o la totalidad de la consulta mediante métodos de ejecución en el objeto de consulta.
A continuación se muestra un ejemplo sencillo de una consulta mediante el método de estilo de ejecución, con un filtro y un criterio de ordenación, y el uso de la sustitución de parámetros para el valor utilizado en el filtro. El método execute() del objeto de consulta se ejecuta, con los valores que se van a sustituir en la consulta, en el orden establecido.
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();
}
A continuación se muestra la misma consulta con la sintaxis de cadena:
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");
Puedes combinar estos estilos de definición de la consulta. Por ejemplo:
Query query = pm.newQuery(Employee.class,
"lastName == lastNameParam order by hireDate desc");
query.declareParameters("String lastNameParam");
List<Employee> results = (List<Employee>) query.execute("Smith");
Puedes volver a utilizar una única instancia Query, con diferentes valores sustituidos para los parámetros, mediante la ejecución repetitiva del método execute(). Cada ejecución realiza la consulta y devuelve los resultados como una colección.
La sintaxis de cadena JDOQL admite valores literales dentro de la cadena para valores de cadena y valores numéricos. Delimita las cadenas con comillas simples (') o dobles ("). Los demás tipos de valores deben utilizar la sustitución de parámetros. A continuación se muestra un ejemplo que utiliza un valor literal de cadena:
Query query = pm.newQuery(Employee.class,
"lastName == 'Smith' order by hireDate desc");
Un filtro especifica un nombre de campo, un operador y un valor. El valor debe ser proporcionado por la aplicación y no puede hacer referencia a otra propiedad o calcularse en función de otras propiedades. El operador puede ser cualquiera de los siguientes: < <= == >= >
Nota: la interfaz del almacén de datos Java no admite los operadores de filtro != y IN implementados en la interfaz del almacén de datos Python. (En la interfaz de Python, estos operadores están implementados en las bibliotecas de cliente como varias consultas del almacén de datos; no son funciones del propio almacén de datos).
El asunto de un filtro puede ser cualquier campo de objeto, incluida la clave principal y la entidad principal del grupo de entidades (consulta Transacciones).
Una entidad deberá coincidir con todos los filtros para ser un resultado. En la sintaxis de cadena JDOQL, se especifican varios filtros mediante la separación && ("and" lógico). No se admiten otras combinaciones lógicas de filtros, como, por ejemplo, "or" lógico o "not" lógico.
Debido a la forma en la que el almacén de datos de App Engine ejecuta las consultas, una única consulta no puede utilizar filtros de desigualdad (< <= >= >) en más de una propiedad. Se permiten varios filtros de desigualdad en la misma propiedad, como, por ejemplo, consultar un intervalo de valores. Consulta Restricciones aplicadas a las consultas.
query.setFilter("lastName == 'Smith' && hireDate > hireDateMinimum");
query.declareParameters("Date hireDateMinimum");
Un criterio de ordenación especifica una propiedad y una dirección, ya sea ascendente o descendente. Los resultados se devuelven ordenados conforme a los criterios de ordenación especificados. Si no se especifican criterios de ordenación para la consulta, los resultados se ordenarán por sus claves de entidad.
Debido a la forma en la que el almacén de datos de App Engine ejecuta las consultas, si una consulta especifica filtros de desigualdad en una propiedad y criterios de ordenación en otras propiedades, la propiedad con los filtros de desigualdad deberá ordenarse antes que el resto de propiedades. Consulta Restricciones aplicadas a las consultas.
query.setOrdering("hireDate desc, firstName asc");
Una consulta puede especificar un intervalo de resultados para devolverlos a la aplicación. El intervalo especifica el resultado del conjunto de resultados que debería devolverse primero y el que debería devolverse el último mediante índices numéricos que empiezan por cero para el primer resultado. Por ejemplo, un intervalo de 5, 10 devuelve los resultados 6º, 7º, 8º, 9º y 10º.
La desviación inicial afecta al rendimiento: el almacén de datos deberá recuperar y, a continuación, descartar todos los resultados antes de la desviación inicial. Por ejemplo, una consulta con un intervalo de 5, 10 extrae diez resultados del almacén de datos, a continuación, descarta los cinco primeros y devuelve los cinco restantes a la aplicación.
query.setRange(5, 10);
Una instancia Extent de JDO representa todos los objetos contenidos en el almacén de datos de una clase determinada.
Inicia una instancia Extent mediante el método getExtent() de PersistenceManager y transfiérelo a la clase de datos. La clase Extent implementa la interfaz Iterable para acceder a los resultados. Tras acceder a los resultados, ejecuta el método closeAll().
El siguiente ejemplo pasa por todos los objetos Employee del almacén de datos:
import java.util.Iterator;
import javax.jdo.Extent;
// ...
Extent extent = pm.getExtent(Employee.class, false);
for (Employee e : extent) {
// ...
}
extent.closeAll();
Una instancia Extent recupera los resultados por lotes y puede exceder el límite de 1.000 resultados que se aplica a las consultas.
El almacén de datos de App Engine mantiene un índice para cada consulta que una aplicación pretende realizar. A medida que la aplicación realiza cambios en las entidades del almacén de datos, el almacén actualiza los índices con los resultados correctos. Cuando la aplicación ejecuta una consulta, el almacén de datos extrae los resultados directamente del índice correspondiente.
Una aplicación tiene un índice para cada combinación de tipo, propiedad de filtro y operador y, además, un criterio de ordenación utilizado en una consulta. Ten en cuenta la consulta del ejemplo, especificada en JDOQL:
select from Person where lastName == "Smith"
&& height < 72
order by height desc
El índice de esta consulta es una tabla de claves para entidades del tipo Person, con columnas para los valores de las propiedades height, lastName y . El índice se clasifica por height en orden descendente.
Dos consultas con el mismo formato pero con distintos valores de filtro utilizan el mismo índice. Por ejemplo, la siguiente consulta utiliza el mismo índice que la consulta anterior:
select from Person where lastName == "Jones"
&& height < 64
order by height desc
El almacén de datos ejecuta una consulta mediante los siguientes pasos:
Cada tabla de índice contiene columnas para cada una de las propiedades utilizadas en un filtro o criterio de ordenación. Las filas se ordenan según los siguientes aspectos, en orden:
Esto coloca todos los resultados de cada posible consulta que utilice este índice en filas consecutivas de la tabla.
Este mecanismo admite una amplia gama de consultas y resulta apropiado para la mayoría de las aplicaciones. No obstante, no admite algunos tipos de consultas procedentes de otras tecnologías de bases de datos a los que es posible que estés acostumbrado.
Un índice sólo contiene entidades cuyas propiedades aparecen en el índice. Si una propiedad de una entidad no aparece en el índice, dicha entidad no aparecerá en el índice y nunca será el resultado de la consulta que utilice el índice.
Ten en cuenta que el almacén de datos de App Engine distingue entre una entidad que no tiene una propiedad y una entidad que tiene una propiedad pero cuyo valor es nulo (null). Si deseas que todas las entidades de un tipo sean un posible resultado de una solicitud, puedes utilizar una clase de datos JDO o JPA, que asigna siempre un valor para cada propiedad que corresponde a un campo de la clase.
Las propiedades con valores del tipo de valor texto largo (Text) o del tipo de valor binario largo (Blob) no se incluyen en índices, por lo que las consultas no las encuentran.
Dado que estos valores de propiedad no se indexan, una consulta con un filtro o criterio de ordenación relacionado con una propiedad nunca coincidirá con una entidad cuyo valor de propiedad sea Text o Blob. Las propiedades con estos valores se comportan como si la propiedad no estuviera establecida en relación con los criterios de ordenación y filtros de la consulta.
Cuando dos entidades tienen propiedades con el mismo nombre pero con diferente tipo de valor, el índice de propiedad ordena las entidades primero según el tipo de valor y, a continuación, según un orden adecuado al tipo. Por ejemplo, si dos entidades tienen una propiedad denominada "age", una con un valor entero y otra con un valor de cadena, la entidad con el valor entero aparecerá siempre antes que la entidad con el valor de cadena, cuando se hayan ordenado según la propiedad "Age", independientemente de los valores en sí.
Cabe destacar esto en el caso de los números enteros y de punto flotante, a los que el almacén de datos trata como tipos independientes. Una propiedad con valor entero 38 se ordena antes que una propiedad con valor de punto flotante 37.5, ya que todos los enteros se ordenan con anterioridad a los flotantes.
(Si estás utilizando JDO o JPA, esta situación no se presentará a menos que modifiques un tipo de campo sin actualizar las entidades existentes en el almacén de datos o que utilices el API de almacén de datos de nivel inferior o un API que no utilice Java).
App Engine genera índices para diversas consultas sencillas de forma predeterminada. Para otras consultas, la aplicación debe especificar los índices que necesita en un archivo de configuración con el nombre datastore-indexes.xml. Si la aplicación que se está ejecutando sobre App Engine intenta realizar una consulta para la que no existe un índice correspondiente (proporcionado de forma predeterminada o descrito en datastore-indexes.xml), la consulta fallará.
App Engine proporciona índices automáticos para los siguientes formatos de consultas:
Para el resto de formatos de consultas es necesario especificar sus índices en datastore-indexes.xml, incluidas:
El servidor web de desarrollo hace que la gestión de la configuración del índice sea más fácil. En lugar de fallar en la ejecución de una consulta que no dispone de índice y que lo necesita, el servidor web de desarrollo puede generar configuración para un índice que permitiría que la solicitud se realice con éxito. Si la prueba local de tu aplicación ejecuta cada consulta posible que vaya a realizar la aplicación (cada combinación de tipo, ancestros, filtro y criterio de ordenación), las entradas generadas representarán un conjunto completo de índices. Si tu prueba no ejecuta cada posible formato de consulta, puedes consultar y ajustar la configuración de índice antes de subir la aplicación.
Puedes definir índices de forma manual mediante un archivo de configuración, denominado datastore-indexes.xml, que se encuentra en el directorio WEB-INF/ del WAR de tu aplicación. Si tienes activada la configuración de índices automática (consulta la información que aparece a continuación), el servidor de desarrollo creará una configuración de índices, denominada datastore-indexes-auto.xml, en un directorio denominado WEB-INF/appengine-generated/ y utilizará ambos archivos para establecer el conjunto completo de índices.
Tomemos como ejemplo una vez más la siguiente consulta de muestra:
select from Person where lastName = 'Smith'
&& height < 72
order by height desc
La configuración del índice que necesita esta consulta aparecerá en datastore-indexes.xml como se muestra a continuación:
<?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>
Si el elemento <datastore-indexes> en datastore-indexes.xml tiene el atributo autoGenerate="true" (al igual que el ejemplo anterior) o si la aplicación no tiene un archivo datastore-indexes.xml, se activará la configuración de índices automática. Con la configuración de índices automática activada, si la aplicación realiza esta consulta en el servidor de desarrollo y el índice no está configurado, el servidor añadirá esta configuración de índices al archivo datastore-indexes-auto.xml.
Para obtener más información sobre datastore-indexes.xml y datastore-indexes-auto.xml, consulta Configuración de índices del almacén de datos Java.
Las claves de entidad pueden ser el asunto de un criterio de ordenación o de un filtro de consultas. En JDO, puedes consultar la clave de entidad mediante el campo de clave principal del objeto. El almacén de datos considera el valor de clave completo para esas consultas, incluida la ruta de la entidad principal de la entidad, el tipo y la cadena de nombre de clave asignada por la aplicación o la ID numérica asignada por el sistema.
Puesto que una clave de entidad es única para todas las entidades del sistema, las consultas de clave facilitan la recuperación de un determinado tipo de entidades en lotes, como, por ejemplo, para un volcado de lotes del contenido del almacén de datos. A diferencia de los intervalos JDOQL, esto funciona de forma eficaz para cualquier número de entidades.
Las claves se ordenan primero por ruta principal y, a continuación, por tipo y por nombre de clave o ID. Los nombres de clave y de tipos son cadenas y se ordenan por valor de byte. Las ID son enteras y se ordenan de forma numérica. Si las entidades con el mismo tipo y principal utilizan una mezcla de cadenas de nombre de clave e ID numéricas, las entidades con ID numéricas se consideran que tienen que ser inferiores a las cadenas de nombre de clave. La comparación de los elementos de la ruta principal se realiza de forma similar: por tipo (cadena), por nombre de clave (cadena) o ID (número).
Las consultas que implican claves utilizan índices del mismo modo que las consultas que implican propiedades, pero con una diferencia: a diferencia de una consulta con una propiedad, una consulta con un filtro de igualdad en la clave que también tenga filtros adicionales debe emplear un índice personalizado definido en el archivo de configuración del índice de la aplicación. Como con todas las consultas, el servidor web de desarrollo crea entradas de configuración adecuadas en este archivo cuando se prueba una consulta que requiere un índice personalizado.
La naturaleza del mecanismo de consultas basado en índices impone unas cuantas restricciones con respecto a lo que se puede hacer.
Una condición de filtro de consulta o un criterio de ordenación de una propiedad también implica la condición de que la entidad tenga un valor para la propiedad.
No es necesario que una entidad de almacén de datos tenga el mismo valor para una propiedad que otras entidades del mismo tipo. Un filtro sobre una propiedad sólo puede coincidir con una entidad con un valor para la propiedad. Las entidades sin un valor para una propiedad que se utilicen en un filtro o criterio de ordenación se omitirán del índice generado para la consulta.
No es posible realizar una consulta que solicite entidades que no tengan una propiedad determinada. Una alternativa es crear una propiedad fija (modelada) con un valor predeterminado de null y, a continuación, crear un filtro para las entidades con null como valor de propiedad.
Una consulta sólo puede utilizar filtros de desigualdad (<, <=, >=, >) en una única propiedad en todos sus filtros.
Por ejemplo, esta consulta está permitida:
select from Person where birthYear >= minBirthYearParam
&& birthYear <= maxBirthYearParam
No obstante, esta consulta no está permitida porque utiliza filtros de desigualdad en dos propiedades distintas de la misma consulta:
select from Person where birthYear >= minBirthYearParam
&& height >= minHeightParam // ERROR
Los filtros pueden combinar comparaciones de igualdad (==) para distintas propiedades de la misma consulta, incluidas consultas con una o más condiciones de desigualdad en una propiedad. Lo siguiente está permitido:
select from Person where lastName == lastNameParam
&& city == cityParam
&& birthYear >= minBirthYearParam
El mecanismo de consulta se basa en que todos los resultados para una consulta serán adyacentes entre sí en la tabla de índice para evitar tener que examinar la tabla entera buscando los resultados. Una sola tabla de índice no puede representar varios filtros de desigualdad en múltiples propiedades manteniendo a la vez todos los resultados consecutivos en la tabla.
Si una consulta tiene un filtro con una comparación de desigualdad y uno o varios criterios de ordenación, la consulta deberá incluir un criterio de ordenación para la desigualdad, que deberá aparecer antes que los criterios de ordenación aplicados a otras propiedades.
Esta consulta no es válida porque utiliza un filtro de desigualdad y no ordena por la propiedad filtrada:
select from Person where birthYear >= minBirthYearParam
order by lastName // ERROR
Igualmente, esta consulta no es válida porque no ordena por la propiedad filtrada antes de ordenar por otras propiedades:
select from Person where birthYear > minBirthYearParam
order by lastName, birthYear // ERROR
Esta consulta es válida:
select from Person where birthYear >= minBirthYearParam
order by birthYear, lastName
Para obtener todos los resultados que coinciden con un filtro de desigualdad, una consulta busca en la tabla de índice la primera fila coincidente y, a continuación, devuelve todos los resultados consecutivos hasta que encuentra una fila que no coincide. Para que las filas consecutivas representen el conjunto de resultados completo, las filas se deben ordenar por filtro de desigualdad antes que por cualquier otro criterio de ordenación.
Debido a la forma en que se indexan las propiedades con varios valores, el criterio de ordenación de estas propiedades es inusual:
Este criterio de ordenación tiene la consecuencia inusual de que [1, 9] aparece antes de [4, 5, 6, 7], tanto en orden ascendente como en orden descendente.
Una advertencia importante es la relacionada con aquellas consultas con un filtro de igualdad y un criterio de ordenación en una propiedad con múltiples valores. En estas consultas, se hace caso omiso del criterio de ordenación. En el caso de las propiedades con un único valor, se trata de una optimización sencilla. Cada resultado tendrá el mismo valor para la propiedad, por lo que no será necesario ordenar más los resultados.
No obstante, las propiedades con varios valores pueden tener valores adicionales. Dado que se ha hecho caso omiso del criterio de ordenación, es posible que los resultados de la consulta se devuelvan en un orden distinto al que se habría obtenido de haberse aplicado el criterio de ordenación. (La restauración del criterio de ordenación desechado resulta costosa y requiere índices adicionales; además, este uso es poco habitual, por lo que el diseñador de consultas lo excluye).
Como se ha descrito anteriormente, todas las propiedades (que no tienen un valor Text o Blob) de todas las entidades se añaden al menos a una tabla de índice, incluido un índice sencillo que se proporciona de forma predeterminada y los índices descritos en el archivo datastore-indexes.xml de la aplicación que hace referencia a la propiedad. En el caso de una entidad que tenga un valor para cada propiedad, App Engine almacena un valor de propiedad en su índice sencillo y una vez por cada ocasión en que se haga referencia a la propiedad en un índice personalizado. Todas las entradas de índice se deben actualizar cada vez que el valor de la propiedad cambie. Por tanto, cuantos más índices hagan referencia a la propiedad, más tiempo tardará en actualizarla.
Para evitar que la actualización de una propiedad tarde demasiado tiempo, el almacén de datos limita el número de entradas de índice que puede tener una única entidad. El límite es grande y la mayoría de las aplicaciones no lo perciben. No obstante, existen determinadas circunstancias en las que es posible encontrarse con el límite. Por ejemplo, una entidad con muchas propiedades de valor único puede exceder el límite de entradas de índice.
Las propiedades con varios valores almacenan cada valor en un índice como entradas independientes. Una entidad con una única propiedad que tenga muchos valores puede exceder el límite de entradas del índice.
Los índices personalizados que hacen referencia a varias propiedades con varios valores pueden aumentar mucho su tamaño con unos cuantos valores. Para registrar completamente dichas propiedades, la tabla de índice debe incluir una fila para cada permutación de los valores de cada propiedad del índice.
Por ejemplo, el siguiente índice (descrito en la sintaxis de datastore-indexes.xml) incluye las propiedades x y y de las entidades del tipo 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>
El siguiente código crea una entidad con dos valores para la propiedad x y dos valores para la propiedad y:
MyModel m = new MyModel();
m.setX(Arrays.asList("one", "two"));
m.setY(Arrays.asList("three", "four"));
pm.makePersistent(m);
Para representar con exactitud estos valores, el índice debe almacenar doce valores de propiedad: dos para los índices integrados en x y y y dos para cada una de las cuatro permutaciones de x y y en el índice personalizado. Con varios valores de propiedades con varios valores, esto puede significar que un índice debe almacenar varias entradas de índice para una única entidad. Puedes ejecutar un índice que haga referencia a varias propiedades con varios valores, un "índice de ampliación", porque puede aumentar de tamaño con tan sólo unos valores.
Si put() da lugar a un número de entradas de índice que excede el límite, la llamada generará una excepción. Si creas un índice nuevo que pueda contener un número de entradas de índice que exceda el límite para cualquier entidad durante su creación, las consultas relacionadas con el índice generarán un error y el índice aparecerá con el estado "Error" en la consola de administración.
Para evitar los índices de ampliación, evita aquellas consultas que necesiten un índice personalizado con una propiedad de lista. Como se ha descrito anteriormente, esto incluye consultas con varios criterios de ordenación, una mezcla de filtros de igualdad y desigualdad y filtros de ancestros.