Datastore 查詢

Datastore 的「查詢」會自 Cloud Datastore 中擷取滿足一組特定條件的實體

典型的查詢包含以下幾項:

執行查詢時,查詢會擷取屬於指定種類且符合所有指定篩選器的所有實體,並按照指定順序排序。查詢會以唯讀方式執行。

本頁說明在 App Engine 中,用於從 Cloud Datastore 擷取資料的查詢結構和種類。

篩選器

查詢的「篩選器」對實體的屬性索引鍵祖系設定限制,以擷取特定實體。

屬性篩選器

「屬性篩選器」指定

  • 屬性名稱
  • 比較運算子
  • 屬性值
例如:

屬性值必須由應用程式提供,不能參照其他屬性或以其他屬性計算得出。如果實體具有指定名稱的屬性,且屬性值與篩選器中指定的值比較,符合比較運算子描述的方式,則該實體符合篩選器的條件。

比較運算子可以為以下任何一種 (在巢狀類別 Query.FilterOperator 中定義為列舉常數):

運算子 意義
EQUAL 等於
LESS_THAN 小於
LESS_THAN_OR_EQUAL 小於或等於
GREATER_THAN 大於
GREATER_THAN_OR_EQUAL 大於或等於
NOT_EQUAL 不等於
IN 成員 (與指定清單中的任何值相等)

NOT_EQUAL 運算子實際上執行兩個查詢:一個是在其他所有篩選器皆未變更的情況下,NOT_EQUAL 篩選器會替換為 LESS_THAN 篩選器;另一個則是會替換為 GREATER_THAN 篩選器,然後系統會將結果合併並排序。查詢中只能有一個 NOT_EQUAL 篩選器,且不能有其他任何不等式篩選器。

IN 運算子也執行多個查詢:每個查詢針對指定清單中的各個項目,而所有其他篩選器均未變更,且 IN 篩選器替換為 EQUAL 篩選器。結果會依照清單中項目的順序進行合併。如果查詢含有多個 IN 篩選器,則會以多個查詢執行,每個查詢對應於 IN 清單中每個可能的值「組合」

包含 NOT_EQUALIN 運算子的單一查詢上限為 30 個子查詢。

如需進一步瞭解 NOT_EQUALIN 查詢如何轉譯為 JDO/JPA 架構中的多個查詢,請參閱使用 != 和 IN 篩選器進行查詢一文。

索引鍵篩選器

如要篩選實體索引鍵的值,請使用特殊屬性 Entity.KEY_RESERVED_PROPERTY

Java 8

Filter keyFilter =
    new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.GREATER_THAN, lastSeenKey);
Query q = new Query("Person").setFilter(keyFilter);

Java 7

Filter keyFilter =
    new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.GREATER_THAN, lastSeenKey);
Query q = new Query("Person").setFilter(keyFilter);

另外也支援依據 Entity.KEY_RESERVED_PROPERTY 進行遞增排序。

在比較不等式時,索引鍵會根據以下標準進行排序:

  1. 祖系路徑
  2. 實體種類
  3. ID (索引鍵名稱或數字 ID)

祖系路徑的元素會以類似的方式進行比較:先比較種類 (字串),再比較索引鍵名稱或數字 ID。種類和索引鍵名稱都是字串,且會按照位元組值排序;數字 ID 為整數,會按照數值順序排序。若父項和種類相同的實體混用索引鍵名稱和數字 ID,則會先列出採用數字 ID 的實體,再列出採用索引鍵名稱的實體。

索引鍵查詢像屬性查詢一樣會使用索引,而且在相同情況下都需要自訂索引,只有少數例外狀況:不等式篩選器或索引鍵遞增排序不需使用自訂索引,但索引鍵遞減排序則必須使用。與所有查詢相同,當測試需要自訂索引的查詢時,開發網路伺服器會在索引設定檔中建立適當的項目。

祖系篩選器

您可以將 Datastore 查詢篩選為指定的祖系,回傳的結果就會單純包含來自指定祖系的實體:

特殊查詢類型

請特別注意下列幾種特定查詢類型:

無種類查詢

沒有種類且沒有祖系篩選器的查詢,會從 Datastore 中擷取應用程式的所有實體,其中包含其他 App Engine 功能建立及管理的實體,例如統計資料實體Blobstore 中繼資料實體 (如果有這類實體)。這類「無種類查詢」不能對屬性值使用篩選器或進行排序。但是,這類查詢可以指定 Entity.KEY_RESERVED_PROPERTY 當做屬性名稱,以篩選實體索引鍵:

Java 8

Filter keyFilter =
    new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.GREATER_THAN, lastSeenKey);
Query q = new Query().setFilter(keyFilter);

Java 7

Filter keyFilter =
    new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.GREATER_THAN, lastSeenKey);
Query q = new Query().setFilter(keyFilter);

祖系查詢

採用祖系篩選器的查詢會將結果限制在指定的實體及其子系:

Java 8

DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

Entity tom = new Entity("Person", "Tom");
Key tomKey = tom.getKey();
datastore.put(tom);

Entity weddingPhoto = new Entity("Photo", tomKey);
weddingPhoto.setProperty("imageURL", "http://domain.com/some/path/to/wedding_photo.jpg");

Entity babyPhoto = new Entity("Photo", tomKey);
babyPhoto.setProperty("imageURL", "http://domain.com/some/path/to/baby_photo.jpg");

Entity dancePhoto = new Entity("Photo", tomKey);
dancePhoto.setProperty("imageURL", "http://domain.com/some/path/to/dance_photo.jpg");

Entity campingPhoto = new Entity("Photo");
campingPhoto.setProperty("imageURL", "http://domain.com/some/path/to/camping_photo.jpg");

List<Entity> photoList = Arrays.asList(weddingPhoto, babyPhoto, dancePhoto, campingPhoto);
datastore.put(photoList);

Query photoQuery = new Query("Photo").setAncestor(tomKey);

// This returns weddingPhoto, babyPhoto, and dancePhoto,
// but not campingPhoto, because tom is not an ancestor
List<Entity> results =
    datastore.prepare(photoQuery).asList(FetchOptions.Builder.withDefaults());

Java 7

DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

Entity tom = new Entity("Person", "Tom");
Key tomKey = tom.getKey();
datastore.put(tom);

Entity weddingPhoto = new Entity("Photo", tomKey);
weddingPhoto.setProperty("imageURL", "http://domain.com/some/path/to/wedding_photo.jpg");

Entity babyPhoto = new Entity("Photo", tomKey);
babyPhoto.setProperty("imageURL", "http://domain.com/some/path/to/baby_photo.jpg");

Entity dancePhoto = new Entity("Photo", tomKey);
dancePhoto.setProperty("imageURL", "http://domain.com/some/path/to/dance_photo.jpg");

Entity campingPhoto = new Entity("Photo");
campingPhoto.setProperty("imageURL", "http://domain.com/some/path/to/camping_photo.jpg");

List<Entity> photoList = Arrays.asList(weddingPhoto, babyPhoto, dancePhoto, campingPhoto);
datastore.put(photoList);

Query photoQuery = new Query("Photo").setAncestor(tomKey);

// This returns weddingPhoto, babyPhoto, and dancePhoto,
// but not campingPhoto, because tom is not an ancestor
List<Entity> results =
    datastore.prepare(photoQuery).asList(FetchOptions.Builder.withDefaults());

無種類祖系查詢

無論種類為何,包含祖系篩選器的無種類查詢將會擷取指定的祖系及其所有子系。這類查詢不需要自訂索引。如同所有無種類查詢,這類查詢不能對屬性值使用篩選器或進行排序,但是可以篩選實體索引鍵:

Java 8

Filter keyFilter =
    new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.GREATER_THAN, lastSeenKey);
Query q = new Query().setAncestor(ancestorKey).setFilter(keyFilter);

Java 7

Filter keyFilter =
    new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.GREATER_THAN, lastSeenKey);
Query q = new Query().setAncestor(ancestorKey).setFilter(keyFilter);

以下範例說明如何擷取指定祖系的所有實體:

Java 8

DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

Entity tom = new Entity("Person", "Tom");
Key tomKey = tom.getKey();
datastore.put(tom);

Entity weddingPhoto = new Entity("Photo", tomKey);
weddingPhoto.setProperty("imageURL", "http://domain.com/some/path/to/wedding_photo.jpg");

Entity weddingVideo = new Entity("Video", tomKey);
weddingVideo.setProperty("videoURL", "http://domain.com/some/path/to/wedding_video.avi");

List<Entity> mediaList = Arrays.asList(weddingPhoto, weddingVideo);
datastore.put(mediaList);

// By default, ancestor queries include the specified ancestor itself.
// The following filter excludes the ancestor from the query results.
Filter keyFilter =
    new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.GREATER_THAN, tomKey);

Query mediaQuery = new Query().setAncestor(tomKey).setFilter(keyFilter);

// Returns both weddingPhoto and weddingVideo,
// even though they are of different entity kinds
List<Entity> results =
    datastore.prepare(mediaQuery).asList(FetchOptions.Builder.withDefaults());

Java 7

DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

Entity tom = new Entity("Person", "Tom");
Key tomKey = tom.getKey();
datastore.put(tom);

Entity weddingPhoto = new Entity("Photo", tomKey);
weddingPhoto.setProperty("imageURL", "http://domain.com/some/path/to/wedding_photo.jpg");

Entity weddingVideo = new Entity("Video", tomKey);
weddingVideo.setProperty("videoURL", "http://domain.com/some/path/to/wedding_video.avi");

List<Entity> mediaList = Arrays.asList(weddingPhoto, weddingVideo);
datastore.put(mediaList);

// By default, ancestor queries include the specified ancestor itself.
// The following filter excludes the ancestor from the query results.
Filter keyFilter =
    new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.GREATER_THAN, tomKey);

Query mediaQuery = new Query().setAncestor(tomKey).setFilter(keyFilter);

// Returns both weddingPhoto and weddingVideo,
// even though they are of different entity kinds
List<Entity> results =
    datastore.prepare(mediaQuery).asList(FetchOptions.Builder.withDefaults());

純索引鍵查詢

「純索引鍵查詢」僅傳回結果實體的索引鍵而非實體本身,與擷取整個實體相比,延遲時間及成本都較低。

一般查詢會擷取的實體可能多於您實際需要的實體,因此,先進行純索引鍵查詢,再從結果中擷取實體子集通常較符合經濟效益。

投影查詢

有時候,您只需要用到查詢結果中幾項特定屬性的值。在這種情況下,您可以使用「投影查詢」單純擷取您需要的屬性,相較於擷取整個實體,這種方式更有助於減少延遲時間及降低成本;如需詳情請參閱投影查詢頁面。

排序順序

查詢的「排序順序」指定

  • 屬性名稱
  • 排序方向 (遞增或遞減)

例如:

如果查詢包含多個排序順序,則會按照指定的順序套用排序。下列範例會先將「last name」遞增排列,再將「height」遞減排列:

如果未指定排序順序,則會按照從 Cloud Datastore 擷取的順序傳回結果。

附註:由於 Cloud Datastore 執行查詢的方式所致,如果查詢指定以不等式篩選某個屬性並對其他屬性進行排序,則在不等式篩選中使用的屬性必須排序在其他屬性之前。

索引

每個 Datastore 查詢會使用一或多個索引 (包含由索引的屬性及實體的祖系 (選用) 指定之序列中的實體索引鍵) 計算其結果。索引會以漸進方式更新,以反映應用程式對其實體所做的任何更改,因此所有查詢均可獲得正確的結果,無需再進一步計算。

App Engine 會針對實體的每個屬性預先定義一個簡易索引。App Engine 應用程式可在名為 datastore-indexes.xml索引設定檔中進一步定義自訂索引,而這個索引設定檔是在應用程式的 /war/WEB-INF/appengine-generated 目錄中產生。開發伺服器遇到無法使用現有索引執行的查詢時,即會自動在這個檔案中新增建議。在上傳應用程式之前,您可以透過編輯檔案的方式來手動調整索引。

查詢介面範例

低階 Java Datastore API 會提供建構查詢的 Query 類別,以及從 Datastore 擷取實體的 PreparedQuery 介面:

Java 8

DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

Filter heightMinFilter =
    new FilterPredicate("height", FilterOperator.GREATER_THAN_OR_EQUAL, minHeight);

Filter heightMaxFilter =
    new FilterPredicate("height", FilterOperator.LESS_THAN_OR_EQUAL, maxHeight);

// Use CompositeFilter to combine multiple filters
CompositeFilter heightRangeFilter =
    CompositeFilterOperator.and(heightMinFilter, heightMaxFilter);

// Use class Query to assemble a query
Query q = new Query("Person").setFilter(heightRangeFilter);

// Use PreparedQuery interface to retrieve results
PreparedQuery pq = datastore.prepare(q);

for (Entity result : pq.asIterable()) {
  String firstName = (String) result.getProperty("firstName");
  String lastName = (String) result.getProperty("lastName");
  Long height = (Long) result.getProperty("height");

  out.println(firstName + " " + lastName + ", " + height + " inches tall");
}

Java 7

DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

Filter heightMinFilter =
    new FilterPredicate("height", FilterOperator.GREATER_THAN_OR_EQUAL, minHeight);

Filter heightMaxFilter =
    new FilterPredicate("height", FilterOperator.LESS_THAN_OR_EQUAL, maxHeight);

// Use CompositeFilter to combine multiple filters
CompositeFilter heightRangeFilter =
    CompositeFilterOperator.and(heightMinFilter, heightMaxFilter);

// Use class Query to assemble a query
Query q = new Query("Person").setFilter(heightRangeFilter);

// Use PreparedQuery interface to retrieve results
PreparedQuery pq = datastore.prepare(q);

for (Entity result : pq.asIterable()) {
  String firstName = (String) result.getProperty("firstName");
  String lastName = (String) result.getProperty("lastName");
  Long height = (Long) result.getProperty("height");

  out.println(firstName + " " + lastName + ", " + height + " inches tall");
}

請注意使用 FilterPredicateCompositeFilter 來建構篩選器的做法。如果您只想在查詢中設定一個篩選器,則可單獨使用 FilterPredicate

Java 8

Filter heightMinFilter =
    new FilterPredicate("height", FilterOperator.GREATER_THAN_OR_EQUAL, minHeight);

Query q = new Query("Person").setFilter(heightMinFilter);

Java 7

Filter heightMinFilter =
    new FilterPredicate("height", FilterOperator.GREATER_THAN_OR_EQUAL, minHeight);

Query q = new Query("Person").setFilter(heightMinFilter);

但是,如果您要在查詢中設定多個篩選器,則必須使用至少需要兩個篩選器的 CompositeFilter。上述範例採用捷徑輔助函式 CompositeFilterOperator.and;下列範例則顯示建構複合式 OR 篩選器的一種方式:

Java 8

Filter tooShortFilter = new FilterPredicate("height", FilterOperator.LESS_THAN, minHeight);

Filter tooTallFilter = new FilterPredicate("height", FilterOperator.GREATER_THAN, maxHeight);

Filter heightOutOfRangeFilter = CompositeFilterOperator.or(tooShortFilter, tooTallFilter);

Query q = new Query("Person").setFilter(heightOutOfRangeFilter);

Java 7

Filter tooShortFilter = new FilterPredicate("height", FilterOperator.LESS_THAN, minHeight);

Filter tooTallFilter = new FilterPredicate("height", FilterOperator.GREATER_THAN, maxHeight);

Filter heightOutOfRangeFilter = CompositeFilterOperator.or(tooShortFilter, tooTallFilter);

Query q = new Query("Person").setFilter(heightOutOfRangeFilter);

後續步驟

本頁內容對您是否有任何幫助?請提供意見:

傳送您對下列選項的寶貴意見...

這個網頁
Java 適用的 App Engine 標準環境