Requêtes de projection

La plupart des requêtes Cloud 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'interroger Cloud 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 les requêtes de projection en Java 8

Pour créer une requête de projection, créez un objet Query et ajoutez-y des propriétés à l'aide de la méthode addProjection() :

Java 8

private void addGuestbookProjections(Query query) {
  query.addProjection(new PropertyProjection("content", String.class));
  query.addProjection(new PropertyProjection("date", Date.class));
}

Java 7

private void addGuestbookProjections(Query query) {
  query.addProjection(new PropertyProjection("content", String.class));
  query.addProjection(new PropertyProjection("date", Date.class));
}

Le type que vous spécifiez pour chaque propriété doit correspondre à celui que vous avez utilisé lors de la définition initiale de la propriété avec Entity.setProperty(). L'exemple suivant montre comment traiter les résultats de la requête en parcourant la liste des entités renvoyées et en convertissant chaque valeur de propriété dans le type attendu :

Java 8

private void printGuestbookEntries(DatastoreService datastore, Query query, PrintWriter out) {
  List<Entity> guests = datastore.prepare(query).asList(FetchOptions.Builder.withLimit(5));
  for (Entity guest : guests) {
    String content = (String) guest.getProperty("content");
    Date stamp = (Date) guest.getProperty("date");
    out.printf("Message %s posted on %s.\n", content, stamp.toString());
  }
}

Java 7

private void printGuestbookEntries(DatastoreService datastore, Query query, PrintWriter out) {
  List<Entity> guests = datastore.prepare(query).asList(FetchOptions.Builder.withLimit(5));
  for (Entity guest : guests) {
    String content = (String) guest.getProperty("content");
    Date stamp = (Date) guest.getProperty("date");
    out.printf("Message %s posted on %s.\n", content, stamp.toString());
  }
}

Regroupement (expérimental)

Les requêtes de projection peuvent utiliser la méthode setDistinct() 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.

Java 8

Query q = new Query("TestKind");
q.addProjection(new PropertyProjection("A", String.class));
q.addProjection(new PropertyProjection("B", Long.class));
q.setDistinct(true);
q.setFilter(Query.FilterOperator.LESS_THAN.of("B", 1L));
q.addSort("B", Query.SortDirection.DESCENDING);
q.addSort("A");

Java 7

Query q = new Query("TestKind");
q.addProjection(new PropertyProjection("A", String.class));
q.addProjection(new PropertyProjection("B", Long.class));
q.setDistinct(true);
q.setFilter(Query.FilterOperator.LESS_THAN.of("B", 1L));
q.addSort("B", Query.SortDirection.DESCENDING);
q.addSort("A");

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é (EQUAL) 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 Cloud Datastore.

    La requête renvoie des résultats qui ne sont que partiellement renseignés. Par conséquent, vous ne devez pas les réécrire dans Cloud 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 Cloud 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 (datastore-indexes-auto.xml) 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 manière cohérente, 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 des requêtes de projection ci-après :

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) et devez donc créer 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.