Consultas al almacén de datos

Nota: Se recomienda encarecidamente a los desarrolladores que creen aplicaciones nuevas que usen la biblioteca de cliente de NDB, que ofrece varias ventajas en comparación con esta biblioteca de cliente, como el almacenamiento automático en caché de entidades mediante la API Memcache. Si actualmente usas la biblioteca de cliente de DB anterior, consulta la guía de migración de DB a NDB.

Una consulta de Datastore obtiene entidades de Cloud Datastore que cumplen un conjunto de condiciones especificado.

Una consulta típica incluye lo siguiente:

  • Un tipo de entidad al que se aplica la consulta
  • Filtros opcionales basados en los valores de las propiedades, las claves y los antecesores de las entidades
  • Opcional: ordenar pedidos para secuenciar los resultados
Cuando se ejecuta, una consulta obtiene todas las entidades del tipo indicado que cumplen todos los filtros especificados, ordenadas según el orden indicado. Las consultas se ejecutan como de solo lectura.

En esta página se describe la estructura y los tipos de consultas que se usan en App Engine para obtener datos de Cloud Datastore.

Filtros

Los filtros de una consulta definen las restricciones de las propiedades, las claves y los ancestros de las entidades que se van a recuperar.

Filtros de propiedad

Un filtro de propiedad especifica

  • Nombre de una propiedad
  • Un operador de comparación
  • Un valor de propiedad
Por ejemplo:

q = Person.all()
q.filter("height <=", max_height)

La aplicación debe proporcionar el valor de la propiedad. No puede hacer referencia a otras propiedades ni calcularse en función de ellas. Una entidad cumple las condiciones del filtro si tiene una propiedad con el nombre indicado cuyo valor se compara con el valor especificado en el filtro de la forma descrita por el operador de comparación.

El operador de comparación puede ser cualquiera de los siguientes:

Operador Significado
= Igual a
< Inferior a
<= Inferior o igual a
> Superior a
>= Superior o igual a
!= No igual a
IN Miembro de (igual a cualquiera de los valores de una lista especificada)

El operador distinto de (!=) realiza dos consultas: una en la que todos los demás filtros no cambian y el filtro distinto de se sustituye por un filtro menor que (<) y otra en la que se sustituye por un filtro mayor que (>) . Después, los resultados se combinan en orden. Una consulta no puede tener más de un filtro distinto y, si tiene uno, no puede tener ningún otro filtro de desigualdad.

El operador IN también realiza varias consultas: una por cada elemento de la lista especificada, con el resto de los filtros sin cambios y el filtro IN sustituido por un filtro de igualdad (=). Los resultados se combinan en el orden de los elementos de la lista. Si una consulta tiene más de un filtro IN, se realiza como varias consultas, una por cada combinación de valores posible en las listas IN.

Una sola consulta que contenga los operadores distinto de (!=) o IN puede incluir un máximo de 30 subconsultas.

Filtros clave

Para filtrar por el valor de la clave de una entidad, usa la propiedad especial __key__:

q = Person.all()
q.filter('__key__ >', last_seen_key)

Al comparar desigualdades, las claves se ordenan según los siguientes criterios:

  1. Ruta de ancestro
  2. Tipo de entidad
  3. Identificador (nombre de clave o ID numérico)

Los elementos de la ruta de ancestros se comparan de forma similar: por tipo (cadena) y, a continuación, por nombre de clave o ID numérico. Los tipos y los nombres de clave son cadenas y se ordenan por valor de byte. Los IDs numéricos son números enteros y se ordenan numéricamente. Si las entidades con el mismo elemento superior y tipo usan una combinación de cadenas de nombres de clave e IDs numéricos, las que tienen IDs numéricos preceden a las que tienen nombres de clave.

Las consultas en claves usan índices al igual que las consultas en propiedades y requieren índices personalizados en los mismos casos, con un par de excepciones: los filtros de desigualdad o un orden de clasificación ascendente en la clave no requieren un índice personalizado, pero sí un orden de clasificación descendente en la clave. Al igual que con todas las consultas, el servidor web de desarrollo crea las entradas adecuadas en el archivo de configuración del índice cuando se prueba una consulta que necesita un índice personalizado.

Filtros de ancestros

Puede filtrar sus consultas de Datastore por un antepasado específico para que los resultados devueltos incluyan solo las entidades que desciendan de ese antepasado:

q = Person.all()
q.ancestor(ancestor_key)

Tipos de consultas especiales

Merecen una mención especial algunos tipos de consultas concretos:

Consultas independientes del tipo

Una consulta sin tipo y sin filtro de ancestro recupera todas las entidades de una aplicación de Datastore. Esto incluye las entidades creadas y gestionadas por otras funciones de App Engine, como las entidades de estadísticas y las entidades de metadatos de Blobstore (si las hay). Estas consultas sin tipo no pueden incluir filtros ni criterios de ordenación en los valores de las propiedades. Sin embargo, pueden filtrar por claves de entidad especificando __key__ como nombre de propiedad:

q = db.Query()
q.filter('__key__ >', last_seen_key)

En Python, cada entidad devuelta por la consulta debe tener una clase de modelo correspondiente definida para el tipo de entidad. Para definir las clases de modelo de los tipos de entidad de estadísticas, debes importar el paquete stats:

from google.appengine.ext.db import stats

Si tu aplicación tiene un valor de Blobstore, debes añadir el siguiente código para que la API de consulta reconozca el tipo de entidad __BlobInfo__. (Importar la API Blobstore no define esta clase).

from google.appengine.ext import db

class BlobInfo(db.Expando):
  @classmethod
  def kind(cls):
    return '__BlobInfo__'

Consultas de ancestros

Una consulta con un filtro de ancestro limita sus resultados a la entidad especificada y a sus descendientes:

tom = Person(key_name='Tom')

wedding_photo = Photo(parent=tom)
wedding_photo.image_url='http://domain.com/some/path/to/wedding_photo.jpg'
wedding_photo.put()

baby_photo = Photo(parent=tom)
baby_photo.image_url='http://domain.com/some/path/to/baby_photo.jpg'
baby_photo.put()

dance_photo = Photo(parent=tom)
dance_photo.image_url='http://domain.com/some/path/to/dance_photo.jpg'
dance_photo.put()

camping_photo = Photo()
camping_photo.image_url='http://domain.com/some/path/to/camping_photo.jpg'
camping_photo.put()


photo_query = Photo.all()
photo_query.ancestor(tom)


# This returns wedding_photo, baby_photo, and dance_photo,
# but not camping_photo, because tom is not an ancestor
for photo in photo_query.run(limit=5):
  # Do something with photo

Consultas de ancestros independientes del tipo

Una consulta sin tipo que incluya un filtro de ancestro recuperará el ancestro especificado y todos sus descendientes, independientemente del tipo. Este tipo de consulta no requiere índices personalizados. Al igual que todas las consultas sin tipo, no puede incluir filtros ni criterios de ordenación en los valores de las propiedades, pero sí puede filtrar por la clave de la entidad:

q = db.Query()
q.ancestor(ancestor_key)
q.filter('__key__ >', last_seen_key)

Para realizar una consulta de ancestro sin tipo mediante GQL (ya sea en la consola de administración de App Engine o con la clase GqlQuery), omite la cláusula FROM:

q = db.GqlQuery('SELECT * WHERE ANCESTOR IS :1 AND __key__ > :2',
                ancestor_key,
                last_seen_key)

En el siguiente ejemplo se muestra cómo obtener todas las entidades que descienden de un ancestro determinado:

tom = Person(key_name='Tom')

wedding_photo = Photo(parent=tom)
wedding_photo.image_url='http://domain.com/some/path/to/wedding_photo.jpg'
wedding_photo.put()

wedding_video = Video(parent=tom)
wedding_video.video_url='http://domain.com/some/path/to/wedding_video.avi'
wedding_video.put()

# The following query returns both weddingPhoto and weddingVideo,
# even though they are of different entity kinds
media_query = db.query_descendants(tom)
for media in media_query.run(limit=5):
  # Do something with media

Consultas para obtener solo las claves

Una consulta de solo claves devuelve solo las claves de las entidades de resultado en lugar de las entidades en sí, con una latencia y un coste inferiores a los de la recuperación de entidades completas:

q = Person.all(keys_only=True)

A menudo es más económico hacer primero una consulta solo de claves y, a continuación, obtener un subconjunto de entidades de los resultados, en lugar de ejecutar una consulta general que puede obtener más entidades de las que realmente necesitas.

Consultas de proyección

A veces, lo único que necesitas de los resultados de una consulta son los valores de algunas propiedades específicas. En estos casos, puedes usar una consulta de proyección para obtener solo las propiedades que te interesen, con una latencia y un coste inferiores a los de obtener toda la entidad. Consulta la página Consultas de proyección para obtener más información.

Ordenar pedidos

El orden de clasificación de una consulta especifica

  • Nombre de una propiedad
  • El orden de clasificación (ascendente o descendente)

En Python, el orden de clasificación descendente se indica con un guion (-) delante del nombre de la propiedad. Si se omite el guion, se especifica el orden ascendente de forma predeterminada. Por ejemplo:

# Order alphabetically by last name:
q = Person.all()
q.order('last_name')

# Order by height, tallest to shortest:
q = Person.all()
q.order('-height')

Si una consulta incluye varios criterios de ordenación, se aplican en la secuencia especificada. En el ejemplo siguiente, se ordena primero por apellido en orden ascendente y, después, por altura en orden descendente:

q = Person.all()
q.order('lastName')
q.order('-height')

Si no se especifica ningún orden, los resultados se devuelven en el orden en que se recuperan de Datastore.

Nota: Debido a la forma en que Datastore ejecuta las consultas, si una consulta especifica filtros de desigualdad en una propiedad y criterios de ordenación en otras propiedades, la propiedad utilizada en los filtros de desigualdad debe ordenarse antes que las demás propiedades.

Índices

Cada consulta de Datastore calcula sus resultados mediante uno o varios índices,que contienen claves de entidad en una secuencia especificada por las propiedades del índice y, opcionalmente, los ancestros de la entidad. Los índices se actualizan de forma incremental para reflejar los cambios que la aplicación haga en sus entidades, de modo que los resultados correctos de todas las consultas estén disponibles sin necesidad de realizar más cálculos.

App Engine predefine un índice simple en cada propiedad de una entidad. Una aplicación de App Engine puede definir más índices personalizados en un archivo de configuración de índices llamado index.yaml. El servidor de desarrollo añade automáticamente sugerencias a este archivo cuando detecta consultas que no se pueden ejecutar con los índices actuales. Puedes ajustar estos índices manualmente modificando el archivo antes de subir la aplicación.

Ejemplo de interfaz de consulta

La API Datastore de Python proporciona dos clases para preparar y ejecutar consultas:

  • Query usa llamadas a métodos para preparar la consulta.
  • GqlQuery usa un lenguaje de consulta similar a SQL llamado GQL para preparar la consulta a partir de una cadena de consulta.
class Person(db.Model):
  first_name = db.StringProperty()
  last_name = db.StringProperty()
  city = db.StringProperty()
  birth_year = db.IntegerProperty()
  height = db.IntegerProperty()


# Query interface constructs a query using instance methods
q = Person.all()
q.filter("last_name =", "Smith")
q.filter("height <=", max_height)
q.order("-height")


# GqlQuery interface constructs a query using a GQL query string
q = db.GqlQuery("SELECT * FROM Person " +
                "WHERE last_name = :1 AND height <= :2 " +
                "ORDER BY height DESC",
                "Smith", max_height)


# Query is not executed until results are accessed
for p in q.run(limit=5):
  print "%s %s, %d inches tall" % (p.first_name, p.last_name, p.height)

Siguientes pasos