Consultas de proyectos

La mayoría de las consultas de Datastore devuelven entidades enteras 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 menores que si recuperaras 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, de acuerdo 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

Veamos el siguiente modelo:

class Article(ndb.Model):
    title = ndb.StringProperty()
    author = ndb.StringProperty()
    tags = ndb.StringProperty(repeated=True)

Para especificar una proyección, haz lo siguiente:

def print_author_tags():
    query = Article.query()
    articles = query.fetch(20, projection=[Article.author, Article.tags])
    for article in articles:
        print(article.author)
        print(article.tags)
        # article.title will raise a ndb.UnprojectedPropertyError

Los resultados de estas consultas se gestionan de la misma forma que los de una consulta de entidad estándar; por ejemplo, iterando por los resultados.

for article in articles:
        print(article.author)
        print(article.tags)
        # article.title will raise a ndb.UnprojectedPropertyError

Puedes proyectar subpropiedades indexadas desde una propiedad estructurada. Para obtener solo la propiedad city de la propiedad estructurada address de un contacto, puedes usar una proyección como la siguiente:

class Address(ndb.Model):
    type = ndb.StringProperty()  # E.g., 'home', 'work'
    street = ndb.StringProperty()
    city = ndb.StringProperty()
...
class Contact(ndb.Model):
    name = ndb.StringProperty()
    addresses = ndb.StructuredProperty(Address, repeated=True)
...
Contact.query().fetch(projection=["name", "addresses.city"])
Contact.query().fetch(projection=[Contact.name, Contact.addresses.city])

Agrupación (experimental)

Las consultas de proyección pueden usar el método distinct 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 para las propiedades que se están proyectando.

Article.query(projection=[Article.author], group_by=[Article.author])
Article.query(projection=[Article.author], distinct=True)

Ambas consultas son equivalentes y producirán el nombre de cada autor solo una vez.

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 para 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 (=) o de pertenencia (IN) no se pueden proyectar.

    Por ejemplo,

    SELECT A FROM kind WHERE B = 1
    

    es válida (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), pero

    SELECT A FROM kind WHERE A = 1
    

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

  • Los resultados que devuelve una consulta de proyección no pueden guardarse de nuevo en Datastore.

    Como la consulta devuelve resultados que solo se rellenan parcialmente, no puedes escribirlos de nuevo en Datastore.

Proyecciones y propiedades con varios valores

Si proyectas 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 tienes una entidad del 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 con varios valores sin valores, no se incluirán entradas en el índice y no se devolverán resultados para esa entidad desde una consulta de proyección que incluya esa propiedad.

Índices para 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 que necesitas en el archivo de configuración de índices,index.yaml, 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, incluso cuando no todas 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, incluso cuando C no sea obligatoria, solo se necesitará 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 esta:

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

que requiere que 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á crear 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 requerido, ya que las propiedades proyectadas A y B ya se habían incluido en la consulta.