投影查詢

大多數 Datastore 查詢結果會傳回完整實體,但應用程式真正需要的往往只有該實體的幾項屬性。您可以按照實際需求,運用「投影查詢」在 Datastore 中單純查詢某個實體的特定屬性,相較於擷取完整實體,這種方式更有助於減少延遲時間及降低費用。

投影查詢近似於下列形式的 SQL 查詢:

SELECT name, email, phone FROM CUSTOMER

您可以使用所有適用於查詢標準實體的篩選及排序功能,但必須遵守以下說明的幾項限制。查詢會傳回只包含指定屬性的簡略版結果 (以此範例來說就是 nameemailphone),且這些屬性均已填入值;其他所有屬性均沒有資料。

在 Java 8 中使用投影查詢

如要建構投影查詢,您必須建立 Query 物件,並使用 addProjection() 方法新增屬性:

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

您為每個屬性指定的類型,都必須符合第一次使用 Entity.setProperty() 定義屬性時使用的類型。以下範例顯示如何透過疊代所傳回實體的清單,並將每個屬性值轉換成預期類型,來處理查詢的結果。

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

分組(實驗版)

投影查詢可以使用 setDistinct() 方法,確認結果集只傳回完全不重複的結果。如有多個實體的投影屬性包含相同的值,這種方式只會傳回第一個結果。

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");

投影限制

投影查詢必須遵循以下限制:

  • 只能投影已建立索引的屬性。

    未建立索引的屬性不支援投影功能 (不分明確或隱含)。長文字字串 (Text) 和長位元組字串 (Blob) 不會建立索引。

  • 不能重複投影同一個屬性。

  • 不能投影等式 (EQUAL) 或成員資格 (IN) 篩選器所參照的屬性。

    例如,假設使用者要求系統 將文字從英文翻譯成法文

    SELECT A FROM kind WHERE B = 1
    

    有效 (等式篩選器未使用投影屬性),

    SELECT A FROM kind WHERE A > 1
    

    (非等式篩選器) 也有效,但

    SELECT A FROM kind WHERE A = 1
    

    (等式篩選條件使用了投影屬性) 則無效。

  • 不應將投影查詢傳回的結果存回 Datastore。

    由於這類查詢只傳回部分填入的結果,因此不應再將結果寫回 Datastore。

投影與多值屬性

投影有多個值的屬性時,並不會填入該屬性的所有值,而是每找到一組與查詢相符且不重複的投影值,就傳回一個獨立實體。例如,假設有一個 Foo 種類的實體,包含兩個多值屬性 AB

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

之後,投影查詢

SELECT A, B FROM Foo WHERE A < 3

會傳回四個包含下列組合值的實體:

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

請注意,如果實體的某個多值屬性沒有值,則不會在索引中加入項目,也不會從包含該屬性的投影查詢傳回該實體的結果。

投影索引

投影查詢要求必須將投影指定的屬性全數納入 Datastore 索引。App Engine 開發伺服器會在索引設定檔 datastore-indexes-auto.xml (這是使用您的應用程式上傳的檔案) 中自動產生需要的索引。

如要盡可能減少必要的索引數目,其中一種方法是一致投影相同的屬性 (即使並非每一次都需要其中所有屬性)。例如,以下查詢需要兩個獨立的索引:

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

不過,如果一律投影 ABC 等屬性 (即使是在不需要 C 的情況下),則只需要一個索引。

如欲將現有查詢轉換為投影查詢,若查詢的其他部分尚不包含投影中的屬性,則可能必須建構一個新的索引。例如,假設有一個如下所示的現有查詢:

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

這項查詢需要以下索引:

Index(Kind, A, B)

如果將這項索引轉換成以下任一個投影查詢:

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

就會出現一個新的屬性 (C),因而必須建立新索引 Index(Kind, A, B, C)。請注意,投影查詢

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

「不會」變更需要的索引,因為現有查詢中已包含投影屬性 AB