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 Python 2

Considera 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 sobre ellos.

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

Puede proyectar subpropiedades indexadas de 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 la palabra clave 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 en las propiedades que se proyectan.

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

Ambas consultas son equivalentes y mostrará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 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 (=) 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 pueden volver a guardar en Datastore.

    Como la consulta devuelve resultados que solo se han rellenado parcialmente, no puedes volver a escribir 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, 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, 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.