Requêtes Datastore

Une requête Datastore récupère depuis Cloud Datastore des entités qui répondent à un ensemble déterminé de conditions.

Une requête type comprend les éléments suivants :

  • Un genre d'entité auquel s'applique la requête
  • Des filtres facultatifs basés sur les valeurs de propriété, les clés et les ancêtres des entités
  • Des ordres de tri facultatifs pour séquencer les résultats
Lorsque la requête est exécutée, elle récupère toutes les entités d'un genre donné qui satisfont à l'ensemble des filtres définis, en les triant dans l'ordre spécifié. Les requêtes s'exécutent en lecture seule.

Cette page décrit la structure et les genres de requêtes employés dans App Engine pour récupérer des données depuis Cloud Datastore.

Filtres

Les filtres d'une requête définissent des contraintes sur les propriétés, les clés et les ancêtres des entités à récupérer.

Filtres de propriété

Un filtre de propriété spécifie

  • Un nom de propriété
  • Un opérateur de comparaison
  • Une valeur de propriété
Par exemple :

q := datastore.NewQuery("Person").Filter("Height <=", maxHeight)

La valeur de propriété doit être fournie par l'application. Elle ne peut pas faire référence à d'autres propriétés ni être calculée en fonction de celles-ci. Une entité satisfait aux critères de filtre si elle possède une propriété du nom donné, dont la valeur est comparable à celle spécifiée dans le filtre, de la manière décrite par l'opérateur de comparaison.

L'opérateur de comparaison peut être l'un des suivants :

Opérateur Signification
= Égal à
< Inférieur à
<= Inférieur ou égal à
> Supérieur à
>= Supérieur ou égal à

Filtres de clé

Pour filtrer sur la valeur d'une clé d'entité, faites appel à la propriété spéciale __key__ :

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

En cas de comparaison d'inégalité, les clés sont triées selon les critères suivants, dans cet ordre :

  1. Chemin d'ancêtre
  2. Genre d'entité
  3. Identifiant (nom de clé ou ID numérique)

Les éléments du chemin d'ancêtre sont comparés de la même manière : par genre (chaîne), puis par nom de clé ou ID numérique. Les genres et les noms de clé sont des chaînes, et sont triés par valeur d'octet. Les ID numériques sont des entiers et sont triés par ordre numérique. Si des entités ayant le même parent et le même genre emploient une combinaison de chaînes de nom de clé et d'ID numériques, celles avec des ID numériques précèdent celles portant des noms de clé.

Les requêtes portant sur des clés utilisent des index, tout comme celles portant sur des propriétés. En outre, elles nécessitent des index personnalisés dans les mêmes cas, à quelques exceptions près : les filtres d'inégalité ou un ordre de tri croissant sur la clé ne nécessitent pas d'index personnalisé, contrairement à un ordre de tri décroissant sur la clé. Comme pour toutes les requêtes, le serveur Web de développement crée les entrées appropriées dans le fichier de configuration d'index lorsqu'une requête nécessitant un index personnalisé est testée.

Filtres d'ancêtre

Vous pouvez filtrer vos requêtes Datastore sur un ancêtre spécifié de sorte que les résultats renvoyés n'incluent que les entités descendant de cet ancêtre :

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

Types spéciaux de requêtes

Certains types spécifiques de requêtes méritent une attention particulière :

Requêtes sans genre

Une requête sans genre ni filtre d'ancêtre récupère toutes les entités d'une application à partir de Datastore. Cela inclut les entités créées et gérées par d'autres fonctionnalités d'App Engine, telles que les entités statistiques et les entités de métadonnées Blobstore (le cas échéant). Ces requêtes sans genre ne peuvent pas inclure de filtres ni d'ordres de tri sur les valeurs de propriété. Toutefois, elles peuvent filtrer sur des clés d'entité en spécifiant __key__ comme nom de propriété :

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

Requêtes ascendantes

Une requête dotée d'un filtre d'ancêtre limite ses résultats à l'entité spécifiée et à ses descendants :

// 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

Requêtes ascendantes sans genre

Une requête sans genre incluant un filtre d'ancêtre récupérera l'ancêtre spécifié et tous ses descendants, quel qu'en soit le genre. Ce type de requête ne nécessite pas d'index personnalisé. Comme toutes les requêtes sans genre, elle ne peut pas inclure de filtres ni d'ordres de tri sur les valeurs de propriété, mais elle peut filtrer sur la clé de l'entité :

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

L'exemple suivant montre comment récupérer toutes les entités descendant d'un ancêtre donné :

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)
}

Requêtes ne contenant que des clés

Une requête ne contenant que des clés affiche uniquement les clés des entités de résultat, et non les entités elles-mêmes. Cela entraîne une latence et un coût inférieurs à ceux induits par la récupération d'entités entières :

q := datastore.NewQuery("Person").KeysOnly()

Il est souvent plus économique de commencer par ce type de requête, puis d'extraire un sous-ensemble d'entités parmi les résultats, plutôt que d'exécuter une requête générale pouvant extraire plus d'entités que nécessaire.

Notez qu'une requête ne contenant que des clés peut renvoyer plus de 1 000 résultats, mais la commande GetAll ne peut en extraire que 1 000 à la fois et échouera avec une erreur si elle est appelée sur un résultat plus volumineux. Par conséquent, nous vous recommandons d'ajouter une limite de 1 000 clés à la requête.

Requêtes de projection

Parfois, les seuls éléments dont vous avez besoin dans les résultats d'une requête sont les valeurs de quelques propriétés spécifiques. Dans ce cas, vous pouvez employer une requête de projection pour ne récupérer que les propriétés qui vous intéressent réellement, à une latence et à un coût inférieurs à ceux induits par la récupération de l'entité entière. Pour en savoir plus, consultez la page Requêtes de projection.

Ordres de tri

L'ordre de tri d'une requête spécifie les éléments suivants :

  • Un nom de propriété
  • Un sens de tri (croissant ou décroissant)

En langage Go, l'ordre de tri décroissant est indiqué par un trait d'union (--) précédant le nom de la propriété. En cas d'omission du trait d'union, l'ordre croissant est la valeur par défaut. Exemple :

// Order alphabetically by last name:
q := datastore.NewQuery("Person").Order("LastName")

// Order by height, tallest to shortest:
q = datastore.NewQuery("Person").Order("-Height")

Si une requête comprend plusieurs ordres de tri, ils sont appliqués selon la séquence spécifiée. L'exemple suivant effectue d'abord un tri par ordre croissant de nom, puis par ordre décroissant de taille :

q := datastore.NewQuery("Person").Order("LastName").Order("-Height")

Si aucun ordre de tri n'est spécifié, les résultats sont renvoyés dans l'ordre dans lequel ils sont récupérés depuis Datastore.

Remarque : En raison de la manière dont Datastore exécute les requêtes, si une requête spécifie des filtres d'inégalité sur une propriété et des ordres de tri sur d'autres propriétés, la propriété employée dans les filtres d'inégalité doit être triée avant les autres.

Index

Chaque requête Datastore calcule ses résultats au moyen d'un ou de plusieurs index, qui contiennent des clés d'entité dans un ordre spécifié par leurs propriétés et, éventuellement, par les ancêtres de l'entité. Les index sont mis à jour de manière incrémentielle pour refléter les modifications apportées par l'application à ses entités, afin que les résultats corrects de toutes les requêtes soient disponibles sans qu'aucun calcul supplémentaire ne soit nécessaire.

App Engine prédéfinit un index simple sur chaque propriété d'une entité. Une application App Engine peut définir d'autres index personnalisés dans un fichier de configuration d'index appelé index.yaml. Le serveur de développement ajoute automatiquement des suggestions à ce fichier lorsqu'il est confronté à des requêtes qui ne peuvent pas être exécutées avec les index existants. Vous pouvez définir des index manuellement en modifiant le fichier avant d'importer l'application.

Exemple d'interface de requête

L'API Datastore Go fournit un type de requête pour la préparation et l'exécution de requêtes.

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.
	}
	// ...
}

Étapes suivantes