Entity, Properti, dan Kunci

Objek data di Datastore dikenal sebagai entity. Entity memiliki satu atau beberapa properti yang telah diberi nama, yang masing-masing dapat memiliki satu atau beberapa nilai. Entity dari jenis yang sama tidak harus memiliki properti yang sama, dan nilai suatu entity untuk properti tertentu tidak semuanya harus memiliki jenis data yang sama. (Jika perlu, aplikasi dapat membuat dan menerapkan pembatasan tersebut dalam model datanya sendiri.)

Datastore mendukung berbagai jenis data untuk nilai properti. Hal ini mencakup, antara lain:

  • Bilangan bulat
  • Angka floating-point
  • String
  • Tanggal
  • Data biner

Untuk mengetahui daftar lengkap jenis, lihat Jenis nilai dan properti.

Setiap entity dalam Datastore memiliki kunci yang secara unik mengidentifikasinya. Kunci ini terdiri dari komponen berikut:

  • Namespace entity, yang memungkinkan multitenancy
  • Jenis entity, yang mengategorikannya untuk tujuan kueri Datastore
  • ID untuk masing-masing entity, yang dapat berupa
    • string nama kunci
    • ID numerik bilangan bulat
  • Jalur ancestor opsional yang mencari entity dalam hierarki Datastore

Aplikasi dapat mengambil setiap entity dari Datastore menggunakan kunci entity, atau mengambil satu atau beberapa entity dengan mengeluarkan kueri berdasarkan kunci entity atau nilai properti.

Go App Engine SDK menyertakan paket untuk merepresentasikan entity Datastore sebagai struktur Go, serta untuk menyimpan dan mengambilnya di Datastore.

Datastore sendiri tidak menerapkan batasan apa pun pada struktur entity, seperti apakah properti tertentu memiliki nilai dari jenis tertentu; hal ini bebas ditentukan oleh aplikasi.

Jenis dan ID

Setiap entity Datastore memiliki jenis tertentu, yang mengategorikan entity untuk tujuan kueri: misalnya, aplikasi sumber daya manusia mungkin mewakili setiap karyawan di sebuah perusahaan dengan entitas jenis Employee. Di Go Datastore API, Anda menentukan jenis entity saat membuat datastore.Key. Semua jenis nama yang dimulai dengan dua garis bawah (__) akan dicadangkan dan tidak boleh digunakan.

Contoh berikut membuat entity jenis Employee, mengisi nilai propertinya, dan menyimpannya ke Datastore:

import (
	"context"
	"time"

	"google.golang.org/appengine/datastore"
)

type Employee struct {
	FirstName          string
	LastName           string
	HireDate           time.Time
	AttendedHRTraining bool
}

func f(ctx context.Context) {
	// ...
	employee := &Employee{
		FirstName: "Antonio",
		LastName:  "Salieri",
		HireDate:  time.Now(),
	}
	employee.AttendedHRTraining = true

	key := datastore.NewIncompleteKey(ctx, "Employee", nil)
	if _, err := datastore.Put(ctx, key, employee); err != nil {
		// Handle err
	}
	// ...
}

Jenis Employee mendeklarasikan empat kolom untuk model data: FirstName, LastName, HireDate, dan AttendedHRTraining.

Selain jenis, setiap entity memiliki ID, yang ditetapkan saat entity dibuat. Karena ID tersebut merupakan bagian dari kunci entity, ID-nya dikaitkan secara permanen dengan entity dan tidak dapat diubah. ID tersebut dapat ditetapkan dengan salah satu dari dua cara berikut:

  • Aplikasi Anda dapat menentukan string key name-nya sendiri untuk entity.
  • Anda dapat meminta Datastore menetapkan ID numerik bilangan bulat ke entity secara otomatis.

Untuk menetapkan nama kunci pada entity, berikan argumen stringID yang tidak kosong ke datastore.NewKey:

// Create a key with a key name "asalieri".
key := datastore.NewKey(
	ctx,        // context.Context
	"Employee", // Kind
	"asalieri", // String ID; empty means no string ID
	0,          // Integer ID; if 0, generate automatically. Ignored if string ID specified.
	nil,        // Parent Key; nil means no parent
)

Agar Datastore menetapkan ID numerik secara otomatis, gunakan argumen stringID kosong:

// Create a key such as Employee:8261.
key := datastore.NewKey(ctx, "Employee", "", 0, nil)
// This is equivalent:
key = datastore.NewIncompleteKey(ctx, "Employee", nil)

Menetapkan ID

Datastore dapat dikonfigurasi untuk menghasilkan ID otomatis menggunakan dua kebijakan ID otomatis yang berbeda:

  • Kebijakan default menghasilkan urutan acak dari ID yang tidak digunakan yang kira-kira didistribusikan secara seragam. Setiap ID dapat memiliki maksimal 16 digit desimal.
  • Kebijakan legacy membuat urutan ID bilangan bulat yang lebih kecil dan tidak berurutan.

Jika Anda ingin menampilkan ID entity kepada pengguna, dan/atau bergantung pada urutannya, sebaiknya Anda menggunakan alokasi manual.

Datastore menghasilkan urutan acak dari ID yang tidak digunakan yang didistribusikan kira-kira secara seragam. Setiap ID dapat memiliki maksimal 16 digit desimal.

Jalur ancestor

Entity dalam Cloud Datastore membentuk ruang yang terstruktur secara hierarkis, yang menyerupai struktur direktori sistem file. Saat membuat entity, Anda dapat memilih untuk menetapkan entity lain sebagai parent;; entity barunya adalah parent; dari parent entity tersebut (perlu diperhatikan bahwa tidak seperti dalam sistem file, parent entity tidak harus benar-benar ada). Entity tanpa parent adalah root entity. Kaitan entity dan induknya bersifat permanen, dan tidak dapat diubah setelah entity tersebut dibuat. Cloud Datastore tidak akan pernah menetapkan ID numerik yang sama ke dua entity dengan parent yang sama, atau ke dua root entity (yang tidak memiliki parent).

Induk, induk dari induk, dan seterusnya dari suatu entity secara rekursif adalah ancestor-nya; turunannya, turunan dari turunannya, dan seterusnya adalah turunannya. Root entity dan semua turunannya termasuk dalam entity group yang sama. Urutan entity yang dimulai dengan root entity dan berlanjut dari parent ke turunan, yang mengarah ke entity tertentu, merupakan jalur ancestor entity tersebut. Kunci lengkap yang mengidentifikasi entity terdiri dari urutan pasangan jenis-ID yang menentukan jalur ancestor-nya dan berakhir dengan entity itu sendiri:

[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]

Untuk root entity, jalur ancestor kosong dan kuncinya hanya terdiri dari jenis dan ID entity itu sendiri:

[Person:GreatGrandpa]

Konsep ini diilustrasikan oleh diagram berikut:

Menampilkan hubungan root entity dengan entity
  turunan dalam entity group

Untuk menetapkan induk entity, gunakan argumen parent ke datastore.NewKey. Nilai argumen ini harus berupa kunci entity induk. Contoh berikut membuat entity jenis Address dan menetapkan entity Employee sebagai induknya:

// Create Employee entity
employee := &Employee{ /* ... */ }
employeeKey, err := datastore.Put(ctx, datastore.NewIncompleteKey(ctx, "Employee", nil), employee)

// Use Employee as Address entity's parent
// and save Address entity to datastore
address := &Address{ /* ... */ }
addressKey := datastore.NewIncompleteKey(ctx, "Address", employeeKey)
_, err = datastore.Put(ctx, addressKey, address)

Transaksi dan entity group

Setiap upaya untuk membuat, memperbarui, atau menghapus entity terjadi dalam konteks transaksi. Satu transaksi dapat mencakup sejumlah operasi tersebut. Untuk menjaga konsistensi data, transaksi tersebut memastikan bahwa semua operasi yang ada di dalamnya diterapkan ke Datastore sebagai unit atau, jika salah satu operasi gagal, bahwa tidak ada satu pun operasi yang diterapkan. Selain itu, semua pembacaan sangat konsisten (kueri atau perolehan ancestor) yang dilakukan dalam transaksi yang sama mengamati snapshot data yang konsisten.

Seperti yang telah disebutkan di atas, entity group adalah serangkaian entity yang terhubung melalui ancestry ke elemen root yang sama. Pengaturan data ke dalam entity group dapat membatasi transaksi yang dapat dilakukan:

  • Semua data yang diakses oleh transaksi harus berada dalam maksimal 25 entity group.
  • Jika Anda ingin menggunakan kueri dalam transaksi, data Anda harus diatur ke dalam entity group sedemikian rupa sehingga Anda dapat menentukan filter ancestor yang akan cocok dengan data yang tepat.
  • Ada batas throughput operasi tulis sekitar satu transaksi per detik dalam satu entity group. Batasan ini diberikan karena Datastore melakukan replikasi sinkron tanpa master untuk setiap entity group di area geografis yang luas guna memberikan keandalan dan fault tolerance yang tinggi.

Dalam banyak penerapan, Anda dapat menggunakan konsistensi tertunda (yaitu kueri non-ancestor yang mencakup beberapa entity group, yang terkadang menampilkan data yang sedikit usang) saat mengambil pandangan yang luas tentang data yang tidak terkait, kemudian menggunakan konsistensi yang kuat (kueri ancestor atau get dari satu entity) saat melihat atau mengedit satu set data yang sangat terkait. Dalam penerapan semacam itu, sebaiknya Anda menggunakan entity group yang terpisah untuk setiap set data yang sangat terkait. Untuk informasi selengkapnya, lihat Membuat Struktur untuk Konsistensi Kuat.

Properti dan jenis nilai

Nilai data yang terkait dengan entity terdiri dari satu atau beberapa properti. Setiap properti memiliki nama dan satu atau beberapa nilai. Properti dapat memiliki nilai yang berisi lebih dari satu jenis, dan dua entity dapat memiliki nilai dari jenis yang berbeda untuk properti yang sama. Properti dapat diindeks atau tidak diindeks (kueri yang mengurutkan atau memfilter pada properti P akan mengabaikan entity jika P tidak diindeks). Entity dapat memiliki maksimal 20.000 properti yang diindeks.

Jenis nilai berikut ini didukung:

Jenis nilai Jenis Go Tata urutan Notes
Bilangan Bulat int
int8
int16
int32
int64
Angka Bilangan bulat 64-bit, ditandai
Angka floating-point float32
float64
Angka Presisi ganda 64-bit,
IEEE 754
Boolean bool false<true
String (pendek) string Unicode
Hingga 1.500 byte. Nilai yang lebih panjang dari 1.500 byte akan menyebabkan error saat runtime.
String (panjang) string (dengan noindex) Tidak ada Hingga 1 megabyte

Tidak diindeks
Slice byte (pendek) datastore.ByteString Urutan byte Hingga 1.500 byte. Nilai yang lebih panjang dari 1.500 byte akan menyebabkan error saat runtime.
Slice byte (panjang) []byte Tidak ada Hingga 1 megabyte

Tidak diindeks
Tanggal dan waktu time.Time Kronologis
Titik geografis appengine.GeoPoint Menurut lintang,
lalu bujur
Kunci Datastore *datastore.Key Menurut elemen jalur
(jenis, ID,
jenis, ID...)
Kunci Blobstore appengine.BlobKey Urutan byte

Anda juga dapat menggunakan struct atau slice untuk menggabungkan properti. Lihat referensi Datastore untuk detail selengkapnya.

Jika kueri melibatkan properti dengan nilai jenis campuran, Datastore akan menggunakan pengurutan deterministik berdasarkan representasi internal:

  1. Nilai null
  2. Angka fixed-point
    • Bilangan bulat
    • Tanggal dan waktu
  3. Nilai boolean
  4. Urutan byte
    • Slice byte (pendek)
    • String unicode
    • Kunci Blobstore
  5. Angka floating-point
  6. Titik geografis
  7. Kunci Datastore

Karena slice byte panjang dan string panjang tidak diindeks, urutannya tidak ditentukan.

Bekerja dengan entity

Aplikasi dapat menggunakan Datastore API untuk membuat, mengambil, memperbarui, dan menghapus entity. Jika aplikasi mengetahui kunci lengkap untuk suatu entity (atau dapat memperolehnya dari kunci, jenis, dan ID parent-nya), aplikasi dapat menggunakan kunci tersebut untuk beroperasi secara langsung pada entity. Aplikasi juga bisa memperoleh kunci entity sebagai hasil dari kueri Datastore; lihat halaman Kueri Datastore untuk informasi selengkapnya.

Membuat entity

Di Go, Anda membuat entity baru dengan membuat instance struct Go, mengisi kolomnya, dan memanggil datastore.Put untuk menyimpannya ke Datastore. Hanya kolom yang diekspor (dimulai dengan huruf besar) yang akan disimpan ke Datastore. Anda dapat menentukan nama kunci entity dengan meneruskan argumen stringID yang tidak kosong ke datastore.NewKey:

employee := &Employee{
	FirstName: "Antonio",
	LastName:  "Salieri",
	HireDate:  time.Now(),
}
employee.AttendedHRTraining = true
key := datastore.NewKey(ctx, "Employee", "asalieri", 0, nil)
_, err = datastore.Put(ctx, key, employee)

Jika Anda memberikan nama kunci kosong, atau menggunakan datastore.NewIncompleteKey, Datastore akan otomatis menghasilkan ID numerik untuk kunci entity:

employee := &Employee{
	FirstName: "Antonio",
	LastName:  "Salieri",
	HireDate:  time.Now(),
}
employee.AttendedHRTraining = true
key := datastore.NewIncompleteKey(ctx, "Employee", nil)
_, err = datastore.Put(ctx, key, employee)

Mengambil entity

Untuk mengambil entity yang diidentifikasi oleh kunci tertentu, teruskan *datastore.Key sebagai argumen ke fungsi datastore.Get. Anda dapat membuat *datastore.Key menggunakan fungsi datastore.NewKey.

employeeKey := datastore.NewKey(ctx, "Employee", "asalieri", 0, nil)
addressKey := datastore.NewKey(ctx, "Address", "", 1, employeeKey)
var addr Address
err = datastore.Get(ctx, addressKey, &addr)

datastore.Get mengisi instance struct Go yang sesuai.

Memperbarui entity

Untuk memperbarui entity yang ada, ubah atribut struktur, lalu panggil datastore.Put. Data menimpa entity yang ada. Seluruh objek dikirim ke Datastore pada setiap panggilan ke datastore.Put.

Menghapus entity

Dengan mempertimbangkan kunci entity, Anda dapat menghapus entity dengan fungsi datastore.Delete:

key := datastore.NewKey(ctx, "Employee", "asalieri", 0, nil)
err = datastore.Delete(ctx, key)

Operasi batch

datastore.Put, datastore.Get, dan datastore.Delete memiliki varian massal yang disebut datastore.PutMulti, datastore.GetMulti, dan datastore.DeleteMulti. Fungsi ini mengizinkan tindakan pada beberapa entity dalam satu panggilan Datastore:

// A batch put.
_, err = datastore.PutMulti(ctx, []*datastore.Key{k1, k2, k3}, []interface{}{e1, e2, e3})

// A batch get.
var entities = make([]*T, 3)
err = datastore.GetMulti(ctx, []*datastore.Key{k1, k2, k3}, entities)

// A batch delete.
err = datastore.DeleteMulti(ctx, []*datastore.Key{k1, k2, k3})

Operasi batch tidak mengubah biaya Anda. Anda akan dikenai biaya untuk setiap kunci dalam operasi batch, terlepas dari apakah setiap kunci ada atau tidak. Ukuran entity yang terlibat dalam operasi tidak memengaruhi biaya.