Requêtes de projection

La plupart des requêtes Datastore renvoient des résultats comprenant des entités entières, mais bien souvent une application ne s'intéresse en réalité qu'à quelques propriétés d'une entité. Les requêtes de projection vous permettent de n'exécuter des requêtes Datastore que sur les propriétés spécifiques d'une entité dont vous avez réellement besoin, ce qui entraîne une latence et un coût inférieurs à ceux induits par la récupération de l'entité entière.

Les requêtes de projection sont semblables aux requêtes SQL de ce type :

SELECT name, email, phone FROM CUSTOMER

Vous pouvez employer toutes les fonctionnalités de filtrage et de tri disponibles pour les requêtes d'entité standards, conformément aux limites décrites ci-après. La requête renvoie des résultats partiels, seules les valeurs des propriétés spécifiées (name, email et phone dans l'exemple) étant indiquées. Toutes les autres propriétés ne sont pas renseignées.

Utiliser des requêtes de projection dans Python 2

Prenons l'exemple suivant :

class Article(ndb.Model):
    title = ndb.StringProperty()
    author = ndb.StringProperty()
    tags = ndb.StringProperty(repeated=True)

Vous spécifiez une projection comme suit :

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

Vous gérez les résultats de ces requêtes comme vous le feriez pour une requête d'entité standard, par exemple, en effectuant une itération sur les résultats.

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

Vous pouvez projeter des sous-propriétés indexées à partir d'une propriété structurée. Pour obtenir uniquement la propriété city de la propriété structurée address d'un contact, vous pouvez utiliser une projection telle que :

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

Regroupement(expérimental)

Les requêtes de projection peuvent utiliser le mot clé distinct pour garantir que seuls des résultats totalement uniques seront renvoyés dans un ensemble de résultats. Cette méthode permet de ne renvoyer que le premier résultat des entités dont les valeurs des propriétés en cours de projection sont identiques.

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

Les deux requêtes sont équivalentes et ne produisent le nom de chaque auteur qu'une seule fois.

Limites relatives aux projections

Les requêtes de projection sont soumises aux restrictions suivantes :

  • Seules les propriétés indexées peuvent être projetées.

    La projection n'est pas compatible avec les propriétés non indexées, explicitement ou implicitement. Les chaînes de texte longues (Text) et les chaînes d'octets longues (Blob) ne sont pas indexées.

  • Une même propriété ne peut pas être projetée plusieurs fois.

  • Les propriétés référencées dans un filtre d'égalité (=) ou d'appartenance (IN) ne peuvent pas être projetées.

    Exemple :

    SELECT A FROM kind WHERE B = 1
    

    est une requête valide (propriété projetée non utilisée dans le filtre d'égalité), de même que :

    SELECT A FROM kind WHERE A > 1
    

    (pas un filtre d'égalité). Par contre :

    SELECT A FROM kind WHERE A = 1
    

    (propriété projetée utilisée dans un filtre d'égalité) n'est pas une requête valide.

  • Les résultats renvoyés par une requête de projection ne doivent pas être réenregistrés dans Datastore.

    Étant donné que la requête renvoie des résultats qui ne sont que partiellement renseignés, vous ne pouvez pas les réécrire dans Datastore.

Projections et propriétés dotées de plusieurs valeurs

La projection d'une propriété dotée de plusieurs valeurs n'insère pas toutes les valeurs de cette propriété. Au lieu de cela, une entité distincte est renvoyée pour chaque combinaison unique de valeurs projetées correspondant à la requête. Par exemple, supposons que vous ayez une entité de genre Foo dotée de deux propriétés ayant plusieurs valeurs, A et B :

entity = Foo(A=[1, 1, 2, 3], B=['x', 'y', 'x'])

La requête de projection suivante :

SELECT A, B FROM Foo WHERE A < 3

renvoie quatre entités avec les combinaisons de valeurs suivantes :

A = 1, B = 'x'
A = 1, B = 'y'
A = 2, B = 'x'
A = 2, B = 'y'

Notez que si une entité possède une propriété à valeurs multiples sans valeur, aucune entrée ne sera incluse dans l'index et une requête de projection incluant cette propriété ne renverra aucun résultat pour cette entité.

Index des projections

Les requêtes de projection exigent que toutes les propriétés spécifiées dans la projection soient incluses dans un index Datastore. Le serveur de développement App Engine génère automatiquement les index dont vous avez besoin dans le fichier de configuration d'index, index.yaml, qui est importé avec votre application.

Une manière de réduire le nombre d'index requis consiste à projeter les mêmes propriétés de façon systématique, même si elles ne sont pas toutes toujours nécessaires. Par exemple, ces requêtes nécessitent deux index distincts :

SELECT A, B FROM Kind
SELECT A, B, C FROM Kind

Toutefois, si vous projetez toujours les propriétés A, B et C, même lorsque la propriété C n'est pas requise, un seul index suffit.

La conversion d'une requête existante en une requête de projection peut nécessiter la création d'un index si les propriétés de la projection ne sont pas déjà incluses dans une autre partie de la requête. Par exemple, supposons que vous ayez la requête existante suivante :

SELECT * FROM Kind WHERE A > 1 ORDER BY A, B

qui nécessite l'index suivant :

Index(Kind, A, B)

Si vous convertissez la requête en l'une de ces requêtes de projection,

SELECT C FROM Kind WHERE A > 1 ORDER BY A, B
SELECT A, B, C FROM Kind WHERE A > 1 ORDER BY A, B

vous obtenez une nouvelle propriété (C), ce qui nécessite la création d'un index Index(Kind, A, B, C). Notez que la requête de projection suivante :

SELECT A, B FROM Kind WHERE A > 1 ORDER BY A, B

ne modifie pas l'index requis, car les propriétés projetées A et B étaient déjà incluses dans la requête existante.