Kueri Datastore

Kueri Datastore mengambil entitas dari Datastore yang memenuhi serangkaian kondisi tertentu.

Kueri tipikal mencakup hal berikut:

  • Jenis entity tempat kueri diterapkan
  • Filter opsional berdasarkan nilai properti, kunci, dan ancestor entity
  • Urutan sortir opsional untuk mengurutkan hasil

Saat dieksekusi, kueri mengambil semua entity dari jenis tertentu yang memenuhi semua filter yang diberikan, yang diurutkan dalam urutan yang telah ditentukan. Kueri dijalankan sebagai hanya baca.

Halaman ini menjelaskan struktur dan jenis kueri yang digunakan dalam App Engine untuk mengambil data dari Datastore.

Filter

Filter kueri menetapkan batasan pada properti, kunci, dan ancestor dari entity yang akan diambil.

Filter properti

Filter properti menentukan

  • Nama properti
  • Operator perbandingan
  • Nilai properti
Contoh:

Filter propertyFilter =
    new FilterPredicate("height", FilterOperator.GREATER_THAN_OR_EQUAL, minHeight);
Query q = new Query("Person").setFilter(propertyFilter);

Nilai properti harus diberikan oleh aplikasi; ini tidak bisa merujuk ke atau dihitung dari segi properti lainnya. Entitas memenuhi filter jika memiliki properti dengan nama tertentu yang nilainya dibandingkan dengan nilai yang ditentukan dalam filter dengan cara yang dijelaskan oleh operator perbandingan.

Operator perbandingan dapat berupa salah satu dari berikut (didefinisikan sebagai konstanta terenumerasi dalam class bertingkat Query.FilterOperator):

Operator Arti
EQUAL Sama dengan
LESS_THAN Kurang dari
LESS_THAN_OR_EQUAL Kurang dari atau sama dengan
GREATER_THAN Lebih dari
GREATER_THAN_OR_EQUAL Lebih dari atau sama dengan
NOT_EQUAL Tidak sama dengan
IN Anggota (sama dengan nilai mana pun dalam daftar yang ditentukan)

Operator NOT_EQUAL sebenarnya menjalankan dua kueri: satu yang semua filter lainnya tidak berubah dan filter NOT_EQUAL diganti dengan filter LESS_THAN, dan satu lagi diganti dengan filter GREATER_THAN. Hasilnya kemudian digabungkan secara berurutan. Kueri tidak boleh memiliki lebih dari satu filter NOT_EQUAL, dan kueri yang memiliki satu filter tidak boleh memiliki filter ketidaksetaraan lainnya.

Operator IN juga menjalankan beberapa kueri: satu untuk setiap item dalam daftar yang ditentukan, dengan semua filter lainnya tidak berubah dan filter IN diganti dengan filter EQUAL. Hasilnya digabungkan sesuai urutan item dalam daftar. Jika memiliki lebih dari satu filter IN, kueri akan dijalankan sebagai beberapa kueri, satu untuk setiap kemungkinan kombinasi nilai dalam daftar IN.

Satu kueri yang berisi operator NOT_EQUAL atau IN dibatasi hingga maksimum 30 subkueri.

Untuk informasi selengkapnya tentang bagaimana kueri NOT_EQUAL dan IN diterjemahkan menjadi beberapa kueri dalam framework JDO/JPA, lihat artikel Kueri dengan filter != dan IN.

Filter utama

Untuk memfilter nilai kunci entitas, gunakan properti khusus Entity.KEY_RESERVED_PROPERTY:

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

Urutan menaik di Entity.KEY_RESERVED_PROPERTY juga didukung.

Saat membandingkan ketidaksetaraan, kunci diurutkan berdasarkan kriteria berikut secara berurutan:

  1. Jalur ancestor
  2. Jenis entity
  3. ID (nama kunci atau ID numerik)

Elemen jalur ancestor dibandingkan dengan cara serupa: berdasarkan jenis (string), lalu menurut nama kunci atau ID numerik. Jenis dan nama kunci merupakan string dan diurutkan berdasarkan nilai byte; ID numerik adalah bilangan bulat dan diurutkan secara numerik. Jika entity dengan induk dan jenis yang sama menggunakan campuran string nama kunci dan ID numerik, entity dengan ID numerik akan mendahului nama kunci tersebut.

Kueri pada kunci menggunakan indeks seperti kueri pada properti dan memerlukan indeks kustom dalam kasus yang sama, dengan beberapa pengecualian: filter ketidaksetaraan atau tata urutan menaik pada kunci tidak memerlukan indeks kustom, tetapi adanya tata urutan menurun pada kunci. Seperti halnya semua kueri, server web pengembangan akan membuat entri yang sesuai dalam file konfigurasi indeks saat kueri yang memerlukan indeks kustom sedang diuji.

Filter ancestor

Anda dapat memfilter kueri Datastore ke ancestor yang ditentukan sehingga hasil yang ditampilkan hanya akan menyertakan entity yang diturunkan dari ancestor tersebut:

Query q = new Query("Person").setAncestor(ancestorKey);

Jenis kueri khusus

Beberapa jenis kueri tertentu perlu disebutkan secara khusus:

Kueri tanpa jenis

Kueri tanpa jenis dan filter ancestor mengambil semua entity aplikasi dari Datastore. Hal ini mencakup entitas yang dibuat dan dikelola oleh fitur App Engine lainnya, seperti entitas statistik dan Entitas metadata Blobstore (jika ada). Kueri tanpa jenis tersebut tidak dapat menyertakan filter atau tata urutan berdasarkan nilai properti. Namun, mereka dapat memfilter kunci entitas dengan menentukan Entity.KEY_RESERVED_PROPERTY sebagai nama properti:

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

Kueri ancestor

Kueri dengan filter ancestor membatasi hasilnya ke entity yang ditentukan dan turunannya:

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

Kueri ancestor tanpa jenis

Kueri tanpa jenis yang menyertakan filter ancestor akan mengambil ancestor yang ditentukan dan semua turunannya, apa pun jenisnya. Jenis kueri ini tidak memerlukan indeks kustom. Seperti semua kueri tanpa jenis, kueri tidak dapat menyertakan filter atau tata urutan pada nilai properti, tetapi dapat memfilter kunci entity:

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

Contoh berikut menggambarkan cara mengambil semua entity yang merupakan turunan dari ancestor tertentu:

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

Kueri khusus kunci

Kueri khusus kunci hanya menampilkan kunci entity hasil, bukan entity itu sendiri, dengan latensi dan biaya yang lebih rendah daripada mengambil seluruh entity:

Query q = new Query("Person").setKeysOnly();

Sering kali lebih ekonomis untuk melakukan kueri khusus kunci terlebih dahulu, lalu mengambil subset entity dari hasil, daripada menjalankan kueri umum yang dapat mengambil lebih banyak entity daripada yang sebenarnya Anda butuhkan.

Kueri proyeksi

Terkadang yang sangat Anda perlukan dari hasil kueri adalah nilai dari beberapa properti tertentu. Dalam kasus seperti itu, Anda dapat menggunakan kueri proyeksi untuk mengambil properti yang benar-benar Anda minati saja, dengan latensi dan biaya yang lebih rendah daripada mengambil seluruh entity; lihat halaman Kueri Proyeksi untuk mengetahui detailnya.

Tata urutan

Tata urutan kueri menentukan

  • Nama properti
  • Arah pengurutan (menaik atau menurun)

Contoh:

// Order alphabetically by last name:
Query q1 = new Query("Person").addSort("lastName", SortDirection.ASCENDING);

// Order by height, tallest to shortest:
Query q2 = new Query("Person").addSort("height", SortDirection.DESCENDING);

Jika kueri menyertakan beberapa tata urutan, maka akan diterapkan dalam urutan yang ditentukan. Contoh berikut mengurutkan nama belakang secara menaik, lalu mengurutkan tinggi secara menurun:

Query q =
    new Query("Person")
        .addSort("lastName", SortDirection.ASCENDING)
        .addSort("height", SortDirection.DESCENDING);

Jika tidak ada tata urutan yang ditentukan, hasilnya akan ditampilkan sesuai urutan pengambilannya dari Datastore.

Catatan: Karena cara Datastore mengeksekusi kueri, jika kueri menentukan filter ketidaksetaraan di properti dan tata urutan di properti lain, properti yang digunakan dalam filter ketidaksetaraan harus diurutkan sebelum properti lainnya.

Indeks

Setiap kueri Datastore menghitung hasilnya menggunakan satu atau beberapa indeks, yang berisi kunci entity dalam urutan yang ditentukan oleh properti indeks dan, secara opsional, ancestor entity. Indeks diperbarui secara bertahap untuk mencerminkan setiap perubahan yang dilakukan aplikasi pada entity-nya, sehingga hasil yang benar dari semua kueri tersedia tanpa perlu komputasi lebih lanjut.

App Engine menentukan terlebih dahulu indeks sederhana di setiap properti entity. Aplikasi App Engine dapat menentukan indeks kustom lebih lanjut dalam file konfigurasi indeks bernama datastore-indexes.xml, yang dibuat dalam direktori /war/WEB-INF/appengine-generated aplikasi Anda . Server pengembangan secara otomatis menambahkan saran ke file ini saat menemukan kueri yang tidak dapat dijalankan dengan indeks yang ada. Anda dapat menyesuaikan indeks secara manual dengan mengedit file sebelum mengupload aplikasi.

Contoh antarmuka kueri

Java Datastore API tingkat rendah menyediakan class Query untuk membuat kueri dan antarmuka PreparedQuery untuk mengambil entity dari Datastore:

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

Perhatikan penggunaan FilterPredicate dan CompositeFilter untuk membuat filter. Jika Anda hanya menetapkan satu filter pada kueri, Anda dapat menggunakan FilterPredicate itu sendiri:

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

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

Namun, jika ingin menetapkan lebih dari satu filter pada kueri, Anda harus menggunakan CompositeFilter, yang memerlukan setidaknya dua filter. Contoh di atas menggunakan helper pintasan CompositeFilterOperator.and; contoh berikut menunjukkan salah satu cara untuk membuat filter OR komposit:

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

Apa langkah selanjutnya?