Requêtes Datastore

Remarque : Les développeurs qui créent des applications sont vivement encouragés à utiliser la bibliothèque cliente NDB qui présente plusieurs avantages supplémentaires par rapport à cette bibliothèque cliente, tels que la mise en cache automatique des entités via l'API Memcache. Si vous utilisez actuellement l'ancienne bibliothèque cliente DB, consultez le guide de migration de DB vers NDB.

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 = Person.all()
q.filter("height <=", max_height)

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 à
!= Différent de
IN Membre de (égal à l'une des valeurs d'une liste spécifiée)

L'opérateur "différent de" (!=) exécute deux requêtes : une dans laquelle tous les autres filtres restent inchangés et le filtre "différent de" est remplacé par un filtre "inférieur à" (<), puis une autre où il est remplacé par un filtre "supérieur à" (>). Les résultats sont ensuite fusionnés dans l'ordre. Une requête ne doit pas comporter plus d'un filtre "différent de" qui, lorsqu'il est présent, ne peut coexister avec aucun autre filtre d'inégalité.

L'opérateur IN exécute également plusieurs requêtes : une pour chaque élément de la liste spécifiée, tous les autres filtres restant inchangés et le filtre IN étant remplacé par un filtre d'égalité (=). Les résultats sont fusionnés dans l'ordre des éléments de la liste. Une requête comportant plus d'un filtre IN est exécutée sous la forme de plusieurs requêtes, une pour chaque combinaison possible de valeurs dans les listes IN.

Une requête unique contenant des opérateurs "différent de" (!=) ou IN est limitée à 30 sous-requêtes.

Filtres de clé

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

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

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 ancestor spécifié, de sorte que les résultats affichés n'incluent que les entités descendant de cet ancêtre :

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

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 des clés d'entité en spécifiant __key__ comme nom de propriété :

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

En langage Python, chaque entité affichée par la requête doit posséder une classe de modèle correspondante définie pour le genre d'entité. Pour définir les classes de modèle des genres d'entités statistiques, vous devez importer le package stats :

from google.appengine.ext.db import stats

Si votre application possède une valeur Blobstore, vous devez ajouter le code suivant pour que l'API de requête reconnaisse le genre d'entité __BlobInfo__. (L'importation de l'API Blobstore ne définit pas cette classe.)

from google.appengine.ext import db

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

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 :

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

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 la clé de l'entité :

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

Pour exécuter une requête ascendante sans genre à l'aide de GQL (dans la console d'administration App Engine ou à l'aide de la classe GqlQuery), omettez la clause FROM :

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

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

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

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 = Person.all(keys_only=True)

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

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 Python, 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 = Person.all()
q.order('last_name')

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

Si une requête comprend plusieurs ordres de tri, ceux-ci 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 = Person.all()
q.order('lastName')
q.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 Python Datastore fournit deux classes pour la préparation et l'exécution de requêtes :

  • Query utilise des appels de méthode pour préparer la requête.
  • GqlQuery utilise un langage de requête de type SQL appelé GQL pour préparer la requête à partir d'une chaîne de requête.
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)

Étape suivante