Consultas al almacén de datos

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 := datastore.NewQuery("Person").Filter("Height <=", maxHeight)

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

Filtros clave

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

q := datastore.NewQuery("Person").Filter("__key__ >", lastSeenKey)

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

Puedes filtrar tus consultas de Datastore por un ancestor específico para que los resultados devueltos incluyan solo las entidades que desciendan de ese ancestro:

q := datastore.NewQuery("Person").Ancestor(ancestorKey)

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 := datastore.NewQuery("").Filter("__key__ >", lastSeenKey)

Consultas de ancestros

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

// Create two Photo entities in the datastore with a Person as their ancestor.
tomKey := datastore.NewKey(ctx, "Person", "Tom", 0, nil)

wPhoto := Photo{URL: "http://example.com/some/path/to/wedding_photo.jpg"}
wKey := datastore.NewKey(ctx, "Photo", "", 0, tomKey)
_, err := datastore.Put(ctx, wKey, wPhoto)
// check err

bPhoto := Photo{URL: "http://example.com/some/path/to/baby_photo.jpg"}
bKey := datastore.NewKey(ctx, "Photo", "", 0, tomKey)
_, err = datastore.Put(ctx, bKey, bPhoto)
// check err

// Now fetch all Photos that have tomKey as an ancestor.
// This will populate the photos slice with wPhoto and bPhoto.
q := datastore.NewQuery("Photo").Ancestor(tomKey)
var photos []Photo
_, err = q.GetAll(ctx, &photos)
// check err
// do something with photos

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 := datastore.NewQuery("").Ancestor(ancestorKey).Filter("__key__ >", lastSeenKey)

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

tomKey := datastore.NewKey(ctx, "Person", "Tom", 0, nil)

weddingPhoto := &Photo{URL: "http://example.com/some/path/to/wedding_photo.jpg"}
_, err := datastore.Put(ctx, datastore.NewIncompleteKey(ctx, "Photo", tomKey), weddingPhoto)

weddingVideo := &Video{URL: "http://example.com/some/path/to/wedding_video.avi"}
_, err = datastore.Put(ctx, datastore.NewIncompleteKey(ctx, "Video", tomKey), weddingVideo)

// The following query returns both weddingPhoto and weddingVideo,
// even though they are of different entity kinds.
q := datastore.NewQuery("").Ancestor(tomKey)
t := q.Run(ctx)
for {
	var x interface{}
	_, err := t.Next(&x)
	if err == datastore.Done {
		break
	}
	if err != nil {
		log.Errorf(ctx, "fetching next Photo/Video: %v", err)
		break
	}
	// Do something (e.g. switch on types)
	doSomething(x)
}

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 := datastore.NewQuery("Person").KeysOnly()

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.

Ten en cuenta que una consulta de solo claves puede devolver más de 1000 resultados, pero GetAll solo puede recuperar 1000 claves a la vez y fallará con un error si se llama en un resultado mayor. Por lo tanto, te recomendamos que añadas un límite de 1000 claves a la consulta.

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 Go, 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 := datastore.NewQuery("Person").Order("LastName")

// Order by height, tallest to shortest:
q = datastore.NewQuery("Person").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 := datastore.NewQuery("Person").Order("LastName").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 Go Datastore proporciona un tipo Query para preparar y ejecutar consultas.

type Person struct {
	FirstName string
	LastName  string
	City      string
	BirthYear int
	Height    int
}

func handle(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)

	// The Query type and its methods are used to construct a query.
	q := datastore.NewQuery("Person").
		Filter("LastName =", "Smith").
		Filter("Height <=", maxHeight).
		Order("-Height")

	// To retrieve the results,
	// you must execute the Query using its GetAll or Run methods.
	var people []Person
	if _, err := q.GetAll(ctx, &people); err != nil {
		// Handle error.
	}
	// ...
}

Siguientes pasos