Consultas de proyección

La mayoría de las consultas de Datastore devuelven entidades completas como resultados, pero a menudo una aplicación solo está interesada en algunas de las propiedades de la entidad. Las consultas de proyección te permiten consultar en Datastore solo las propiedades específicas de una entidad que necesitas, con una latencia y un coste inferiores a los de recuperar la entidad completa.

Las consultas de proyección son similares a las consultas de SQL con el siguiente formato:

SELECT name, email, phone FROM CUSTOMER

Puedes usar todas las funciones de filtrado y ordenación disponibles para las consultas de entidades estándar, con las limitaciones que se describen a continuación. La consulta devuelve resultados abreviados con solo las propiedades especificadas (name, email y phone en el ejemplo) rellenadas con valores. El resto de las propiedades no tienen datos.

Usar consultas de proyección en Java 8

Para crear una consulta de proyección, crea un objeto Query y añádele propiedades con el método addProjection():

private void addGuestbookProjections(Query query) {
  query.addProjection(new PropertyProjection("content", String.class));
  query.addProjection(new PropertyProjection("date", Date.class));
}

El tipo que especifiques para cada propiedad debe coincidir con el que usaste cuando definiste la propiedad por primera vez con Entity.setProperty(). En el siguiente ejemplo se muestra cómo procesar los resultados de la consulta. Para ello, se itera por la lista de entidades devueltas y se convierte cada valor de propiedad al tipo esperado:

private void printGuestbookEntries(DatastoreService datastore, Query query, PrintWriter out) {
  List<Entity> guests = datastore.prepare(query).asList(FetchOptions.Builder.withLimit(5));
  for (Entity guest : guests) {
    String content = (String) guest.getProperty("content");
    Date stamp = (Date) guest.getProperty("date");
    out.printf("Message %s posted on %s.\n", content, stamp.toString());
  }
}

Agrupación (experimental)

Las consultas de proyección pueden usar el método setDistinct() para asegurarse de que solo se devuelvan resultados completamente únicos en un conjunto de resultados. Solo se devolverá el primer resultado de las entidades que tengan los mismos valores en las propiedades que se proyectan.

Query q = new Query("TestKind");
q.addProjection(new PropertyProjection("A", String.class));
q.addProjection(new PropertyProjection("B", Long.class));
q.setDistinct(true);
q.setFilter(Query.FilterOperator.LESS_THAN.of("B", 1L));
q.addSort("B", Query.SortDirection.DESCENDING);
q.addSort("A");

Limitaciones de las proyecciones

Las consultas de proyección están sujetas a las siguientes limitaciones:

  • Solo se pueden proyectar las propiedades indexadas.

    No se admite la proyección en propiedades que no estén indexadas, ya sea de forma explícita o implícita. Las cadenas de texto largas (Text) y las cadenas de bytes largas (Blob) no se indexan.

  • La misma propiedad no se puede proyectar más de una vez.

  • Las propiedades a las que se hace referencia en un filtro de igualdad (EQUAL) o de pertenencia (IN) no se pueden proyectar.

    Por ejemplo,

    SELECT A FROM kind WHERE B = 1
    

    es válido (la propiedad proyectada no se usa en el filtro de igualdad), al igual que

    SELECT A FROM kind WHERE A > 1
    

    no es un filtro de igualdad, sino

    SELECT A FROM kind WHERE A = 1
    

    (propiedad proyectada usada en el filtro de igualdad) no lo es.

  • Los resultados devueltos por una consulta de proyección no se deben guardar en Datastore.

    Como la consulta devuelve resultados que solo se han rellenado parcialmente, no debes volver a escribirlos en Datastore.

Proyecciones y propiedades de varios valores

Si proyecta una propiedad con varios valores, no se rellenarán todos los valores de esa propiedad. En su lugar, se devolverá una entidad independiente por cada combinación única de valores proyectados que coincida con la consulta. Por ejemplo, supongamos que tiene una entidad de tipo Foo con dos propiedades de varios valores, A y B:

entity = Foo(A=[1, 1, 2, 3], B=['x', 'y', 'x'])

A continuación, la consulta de proyección

SELECT A, B FROM Foo WHERE A < 3

devolverá cuatro entidades con las siguientes combinaciones de valores:

A = 1, B = 'x'
A = 1, B = 'y'
A = 2, B = 'x'
A = 2, B = 'y'

Ten en cuenta que, si una entidad tiene una propiedad de varios valores sin valores, no se incluirá ninguna entrada en el índice y no se devolverá ningún resultado para esa entidad en una consulta de proyección que incluya esa propiedad.

Índices de proyecciones

Las consultas de proyección requieren que todas las propiedades especificadas en la proyección se incluyan en un índice de Datastore. El servidor de desarrollo de App Engine genera automáticamente los índices necesarios en el archivo de configuración de índices, datastore-indexes-auto.xml, que se sube con tu aplicación.

Una forma de minimizar el número de índices necesarios es proyectar las mismas propiedades de forma coherente, aunque no siempre sean necesarias. Por ejemplo, estas consultas requieren dos índices independientes:

SELECT A, B FROM Kind
SELECT A, B, C FROM Kind

Sin embargo, si siempre proyectas las propiedades A, B y C, aunque C no sea obligatoria, solo necesitarás un índice.

Para convertir una consulta en una consulta de proyección, puede que tengas que crear un índice si las propiedades de la proyección no están incluidas en otra parte de la consulta. Por ejemplo, supongamos que tienes una consulta como la siguiente:

SELECT * FROM Kind WHERE A > 1 ORDER BY A, B

que requiere el índice

Index(Kind, A, B)

Convertir esto en cualquiera de las consultas de proyección

SELECT C FROM Kind WHERE A > 1 ORDER BY A, B
SELECT A, B, C FROM Kind WHERE A > 1 ORDER BY A, B

introduce una nueva propiedad (C) y, por lo tanto, requerirá la creación de un nuevo índice Index(Kind, A, B, C). Ten en cuenta que la consulta de proyección

SELECT A, B FROM Kind WHERE A > 1 ORDER BY A, B

no cambiaría el índice necesario, ya que las propiedades proyectadas A y B ya se incluían en la consulta.