Halaman ini menjelaskan cara mengelola data tanpa skema di Spanner Graph. Selain itu, alat ini juga memberikan praktik terbaik dan tips pemecahan masalah. Sebaiknya Anda memahami skema dan kueri Spanner Graph.
Pengelolaan data tanpa skema memungkinkan Anda membuat definisi grafik yang fleksibel. Anda dapat menambahkan, memperbarui, atau menghapus definisi jenis node dan edge tanpa perubahan skema. Pendekatan ini mendukung pengembangan iteratif dan mengurangi overhead pengelolaan skema, sekaligus mempertahankan pengalaman kueri grafik yang sudah dikenal.
Pengelolaan data tanpa skema berguna untuk skenario berikut:
Mengelola grafik dengan perubahan yang sering terjadi, seperti pembaruan dan penambahan label dan properti elemen.
Grafik dengan banyak jenis node dan tepi, yang membuat pembuatan dan pengelolaan tabel input menjadi rumit.
Untuk mengetahui informasi selengkapnya tentang kapan harus menggunakan pengelolaan data tanpa skema, lihat Pertimbangan untuk pengelolaan data tanpa skema.
Membuat model data tanpa skema
Spanner Graph memungkinkan Anda membuat grafik dari tabel yang memetakan baris ke node dan edge.
Alih-alih menggunakan tabel terpisah untuk setiap jenis elemen, pemodelan data tanpa skema
biasanya menggunakan satu tabel node dan satu tabel edge dengan kolom STRING
untuk label dan kolom JSON
untuk properti.
Membuat tabel input
Anda dapat membuat satu tabel GraphNode
dan satu tabel GraphEdge
untuk menyimpan data tanpa skema, seperti yang ditunjukkan dalam contoh berikut. Nama tabel hanya untuk tujuan ilustrasi—Anda dapat memilih nama Anda sendiri.
CREATE TABLE GraphNode (
id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON,
) PRIMARY KEY (id);
CREATE TABLE GraphEdge (
id INT64 NOT NULL,
dest_id INT64 NOT NULL,
edge_id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON,
) PRIMARY KEY (id, dest_id, edge_id),
INTERLEAVE IN PARENT GraphNode;
Contoh ini melakukan tindakan berikut:
Menyimpan semua node dalam satu tabel,
GraphNode
, yang diidentifikasi olehid
unik.Menyimpan semua edge dalam satu tabel,
GraphEdge
, yang diidentifikasi oleh kombinasi unik sumber (id
), tujuan (dest_id
), dan ID-nya sendiri (edge_id
).edge_id
disertakan sebagai bagian dari kunci utama untuk mengizinkan lebih dari satu edge dari pasanganid
kedest_id
.
Tabel node dan edge memiliki kolom label
dan properties
masing-masing.
Kolom ini masing-masing berjenis STRING
dan JSON
.
Untuk mengetahui informasi selengkapnya tentang pilihan kunci untuk pengelolaan data tanpa skema, lihat Definisi kunci utama untuk node dan tepi.
Membuat grafik properti
Pernyataan CREATE PROPERTY GRAPH memetakan tabel input di bagian sebelumnya sebagai node dan edge. Gunakan klausa berikut untuk menentukan label dan properti untuk data tanpa skema:
DYNAMIC LABEL
: membuat label node atau edge dari kolomSTRING
yang berasal dari tabel input.DYNAMIC PROPERTIES
: membuat properti node atau edge dari kolomJSON
yang berasal dari tabel input.
Contoh berikut menunjukkan cara membuat grafik menggunakan klausa ini:
CREATE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
)
EDGE TABLES (
GraphEdge
SOURCE KEY (id) REFERENCES GraphNode(id)
DESTINATION KEY (dest_id) REFERENCES GraphNode(id)
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
);
Menentukan label dinamis
Klausul DYNAMIC LABEL
menetapkan kolom jenis data STRING
untuk menyimpan
nilai label.
Misalnya, dalam baris GraphNode
, jika kolom label
memiliki nilai person
,
kolom tersebut dipetakan ke node Person
dalam grafik. Demikian pula, dalam baris GraphEdge
, jika
kolom label memiliki nilai owns
, nilai tersebut dipetakan ke tepi Owns
dalam
grafik.
Untuk mengetahui informasi selengkapnya tentang batasan saat menggunakan label dinamis, lihat Batasan.
Menentukan properti dinamis
Klausul DYNAMIC PROPERTIES
menetapkan kolom jenis data JSON
untuk menyimpan
properti. Kunci JSON merepresentasikan nama properti, dan nilai JSON merepresentasikan
nilai properti.
Misalnya, saat baris GraphNode
kolom properties
memiliki nilai JSON
'{"name": "David", "age": 43}'
, Spanner memetakannya ke node yang
memiliki properti age
dan name
dengan 43
dan "David"
sebagai masing-masing
nilainya.
Pertimbangan untuk pengelolaan data tanpa skema
Anda mungkin tidak ingin menggunakan pengelolaan data tanpa skema dalam skenario berikut:
- Jenis node dan edge untuk data grafik Anda sudah ditentukan dengan baik, atau label dan propertinya tidak memerlukan pembaruan yang sering.
- Data Anda sudah disimpan di Spanner, dan Anda lebih memilih untuk membuat grafik dari tabel yang ada, bukan membuat tabel node dan edge baru yang khusus.
- Keterbatasan data tanpa skema mencegah adopsi.
Selain itu, jika beban kerja Anda sangat sensitif terhadap performa penulisan, terutama saat properti sering diperbarui, menggunakan properti yang ditentukan skemanya dengan jenis data primitif seperti STRING
atau INT64
lebih efektif daripada menggunakan properti dinamis dengan jenis JSON
.
Untuk mengetahui informasi selengkapnya tentang cara menentukan skema grafik tanpa menggunakan label dan properti data dinamis, lihat Ringkasan skema Spanner Graph.
Membuat kueri data grafik tanpa skema
Anda dapat membuat kueri data grafik tanpa skema menggunakan Graph Query Language (GQL). Anda dapat menggunakan contoh kueri di Ringkasan Kueri Grafik Spanner dan Referensi GQL dengan sedikit modifikasi.
Mencocokkan node dan tepi menggunakan label
Anda dapat mencocokkan node dan tepi menggunakan ekspresi label di GQL.
Kueri berikut cocok dengan node dan tepi yang terhubung yang memiliki nilai
account
dan transfers
di kolom labelnya.
GRAPH FinGraph
MATCH (a:Account {id: 1})-[t:Transfers]->(d:Account)
RETURN COUNT(*) AS result_count;
Mengakses properti
Spanner memodelkan kunci dan nilai tingkat teratas dari jenis data JSON
sebagai properti, seperti age
dan name
dalam contoh berikut.
JSON document |
Properties |
|
|
|
Contoh berikut menunjukkan cara mengakses properti name
dari node
Person
.
GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN person.name;
Kueri ini akan menampilkan hasil yang mirip dengan berikut ini:
JSON"Tom"
Mengonversi jenis data properti
Spanner memperlakukan properti sebagai nilai jenis data JSON. Dalam beberapa kasus, seperti untuk perbandingan dengan jenis SQL, Anda harus mengonversi properti ke jenis SQL terlebih dahulu.
Dalam contoh berikut, kueri melakukan konversi jenis data berikut:
- Mengonversi properti
is_blocked
menjadi jenis boolean untuk mengevaluasi ekspresi. - Mengonversi properti
order_number_str
menjadi jenis string dan membandingkannya dengan nilai literal"302290001255747"
. - Menggunakan fungsi LAX_INT64
untuk mengonversi
order_number_str
menjadi bilangan bulat dengan aman sebagai jenis nilai yang ditampilkan.
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->()
WHERE BOOL(a.is_blocked) AND STRING(t.order_number_str) = "302290001255747"
RETURN LAX_INT64(t.order_number_str) AS order_number_as_int64;
Tindakan ini akan menampilkan hasil yang mirip dengan berikut ini:
+-----------------------+
| order_number_as_int64 |
+-----------------------+
| 302290001255747 |
+-----------------------+
Dalam klausa seperti GROUP BY
dan ORDER BY
, Anda juga harus mengonversi jenis data JSON. Contoh berikut mengonversi properti city
ke jenis string,
yang memungkinkan Anda menggunakannya untuk pengelompokan.
GRAPH FinGraph
MATCH (person:Person {country: "South Korea"})
RETURN STRING(person.city) as person_city, COUNT(*) as cnt
LIMIT 10
Tips untuk mengonversi jenis data JSON ke jenis data SQL:
- Konverter ketat, seperti
INT64
, melakukan pemeriksaan jenis dan nilai yang ketat. Gunakan pengonversi ketat saat jenis data JSON diketahui dan diterapkan, misalnya, dengan menggunakan batasan skema untuk menerapkan jenis data properti. - Konverter fleksibel, seperti
LAX_INT64
, mengonversi nilai dengan aman jika memungkinkan, dan menampilkanNULL
jika konversi tidak memungkinkan. Gunakan konverter fleksibel jika pemeriksaan yang ketat tidak diperlukan atau jenis sulit diterapkan.
Untuk mengetahui informasi selengkapnya tentang konversi data, lihat tips pemecahan masalah.
Memfilter berdasarkan nilai properti
Dalam
filter properti,
Spanner memperlakukan parameter filter sebagai nilai jenis data JSON
. Misalnya, dalam kueri berikut, Spanner memperlakukanis_blocked
sebagai JSON boolean
dan order_number_str
sebagai JSON
string
.
GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str:"302290001255747"}]->()
RETURN a.id AS account_id;
Tindakan ini akan menampilkan hasil yang mirip dengan berikut ini:
+-----------------------+
| account_id |
+-----------------------+
| 7 |
+-----------------------+
Parameter filter harus cocok dengan jenis dan nilai properti. Misalnya, jika parameter filter order_number_str
adalah bilangan bulat, Spanner tidak akan menemukan kecocokan karena propertinya adalah string
JSON.
GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str: 302290001255747}]->()
RETURN t.order_number_str;
Mengakses properti JSON bertingkat
Spanner tidak memodelkan kunci dan nilai JSON bertingkat sebagai properti.
Dalam contoh berikut, Spanner tidak memodelkan kunci JSON
city
, state
, dan country
sebagai properti karena disarangkan di bawah
location
. Namun, Anda dapat mengaksesnya dengan
operator akses kolom JSON
atau operator subskrip JSON.
JSON document |
Properties |
|
|
|
|
|
Contoh berikut menunjukkan cara mengakses properti bertingkat dengan operator akses kolom JSON.
GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN STRING(person.location.city);
Tindakan ini akan menampilkan hasil yang mirip dengan berikut ini:
"New York"
Mengubah data tanpa skema
Spanner Graph memetakan data dari tabel ke node dan tepi grafik. Saat Anda mengubah data tabel input, perubahan ini secara langsung menyebabkan mutasi pada data grafik yang sesuai. Untuk mengetahui informasi selengkapnya tentang mutasi data grafik, lihat Menyisipkan, memperbarui, atau menghapus data Spanner Graph.
Contoh kueri
Bagian ini memberikan contoh yang menunjukkan cara membuat, mengupdate, dan menghapus data grafik.
Menyisipkan data grafik
Contoh berikut menyisipkan node person
. Nama label dan properti harus
menggunakan huruf kecil.
INSERT INTO GraphNode (id, label, properties)
VALUES (4, "person", JSON'{"name": "David", "age": 43}');
Memperbarui data grafik
Contoh berikut memperbarui node Account
dan menggunakan fungsi
JSON_SET
untuk menetapkan properti is_blocked
-nya.
UPDATE GraphNode
SET properties = JSON_SET(
properties,
'$.is_blocked', false
)
WHERE label = "account" AND id = 16;
Contoh berikut memperbarui node person
dengan kumpulan properti baru.
UPDATE GraphNode
SET properties = JSON'{"name": "David", "age": 43}'
WHERE label = "person" AND id = 4;
Contoh berikut menggunakan fungsi
JSON_REMOVE
untuk menghapus properti is_blocked
dari node Account
. Setelah
dieksekusi, semua properti yang ada lainnya tidak akan berubah.
UPDATE GraphNode
SET properties = JSON_REMOVE(
properties,
'$.is_blocked'
)
WHERE label = "account" AND id = 16;
Menghapus data grafik
Contoh berikut menghapus edge Transfers
pada node Account
yang ditransfer ke akun yang diblokir.
DELETE FROM GraphEdge
WHERE label = "transfers" AND id IN {
GRAPH FinGraph
MATCH (a:Account)-[:Transfers]->{1,2}(:Account {is_blocked: TRUE})
RETURN a.id
}
Batasan umum
Bagian ini mencantumkan batasan penggunaan pengelolaan data tanpa skema.
Persyaratan tabel tunggal untuk label dinamis
Anda hanya dapat memiliki satu tabel node jika label dinamis digunakan dalam definisinya. Batasan ini juga berlaku untuk tabel edge. Spanner tidak mengizinkan hal berikut:
- Menentukan tabel node dengan label dinamis bersama dengan tabel node lainnya.
- Menentukan tabel tepi dengan label dinamis bersama dengan tabel tepi lainnya.
- Menentukan beberapa tabel node atau beberapa tabel edge yang masing-masing menggunakan label dinamis.
Misalnya, kode berikut gagal saat mencoba membuat beberapa node grafik dengan label dinamis.
CREATE OR REPLACE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNodeOne
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties),
GraphNodeTwo
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties),
Account
LABEL Account PROPERTIES(create_time)
)
EDGE TABLES (
...
);
Nama label harus berupa huruf kecil
Anda harus menyimpan nilai string label sebagai huruf kecil untuk pencocokan. Sebaiknya terapkan aturan ini di kode aplikasi atau menggunakan batasan skema.
Meskipun nilai string label harus disimpan sebagai huruf kecil, nilai tersebut tidak peka huruf besar/kecil saat Anda mereferensikannya dalam kueri.
Contoh berikut menunjukkan cara menyisipkan label dalam nilai huruf kecil:
INSERT INTO GraphNode (id, label) VALUES (1, "account");
INSERT INTO GraphNode (id, label) VALUES (2, "account");
Anda dapat menggunakan label yang tidak peka huruf besar/kecil untuk mencocokkan GraphNode
atau GraphEdge
.
GRAPH FinGraph
MATCH (accnt:Account {id: 1})-[:Transfers]->(dest_accnt:Account)
RETURN dest_accnt.id;
Nama properti harus berupa huruf kecil
Anda harus menyimpan nama properti dalam huruf kecil. Sebaiknya terapkan aturan ini di kode aplikasi atau menggunakan batasan skema.
Meskipun nama properti harus disimpan sebagai huruf kecil, nama tersebut tidak peka huruf besar/kecil saat Anda mereferensikannya dalam kueri.
Contoh berikut menyisipkan properti name
dan age
menggunakan huruf kecil.
INSERT INTO GraphNode (id, label, properties)
VALUES (25, "person", JSON '{"name": "Kim", "age": 27}');
Dalam teks kueri, nama properti tidak peka huruf besar/kecil. Misalnya, Anda dapat menggunakan Age
atau age
untuk mengakses properti.
GRAPH FinGraph
MATCH (n:Person {Age: 27})
RETURN n.id;
Batasan tambahan
- Model Spanner hanya menampilkan kunci tingkat teratas dari
jenis data
JSON
sebagai properti. - Jenis data properti harus sesuai dengan spesifikasi jenis JSON Spanner.
Praktik terbaik untuk data tanpa skema
Bagian ini menjelaskan praktik terbaik yang membantu Anda memodelkan data tanpa skema.
Menentukan kunci utama untuk node dan tepi
Kunci node harus unik di semua node grafik. Misalnya, sebagai kolom INT64
atau string
UUID
.
Jika ada beberapa tepi di antara dua node, perkenalkan ID unik untuk tepi tersebut. Contoh skema menggunakan kolom INT64
edge_id
logika aplikasi.
Saat membuat skema untuk tabel node dan edge, sertakan kolom
label
sebagai kolom kunci utamajika nilai
tidak dapat diubah. Jika Anda melakukannya, kunci gabungan yang dibentuk oleh semua kolom kunci harus
unik di semua node atau tepi. Teknik ini meningkatkan performa untuk
kueri yang hanya difilter menurut label.
Untuk mengetahui informasi selengkapnya tentang pilihan kunci utama, lihat Memilih kunci utama.
Membuat indeks sekunder untuk properti yang sering diakses
Untuk meningkatkan performa kueri untuk properti yang sering digunakan dalam filter, buat indeks sekunder terhadap kolom properti yang dihasilkan. Kemudian, gunakan dalam skema dan kueri grafik.
Contoh berikut menunjukkan cara menambahkan kolom age
yang dibuat ke tabel GraphNode
untuk node person
. Nilainya adalah NULL
untuk node tanpa label
person
.
ALTER TABLE GraphNode
ADD COLUMN person_age INT64 AS
(IF (label = "person", LAX_INT64(properties.age), NULL));
Pernyataan DDL berikut kemudian membuat NULL FILTERED INDEX
untuk
person_age
dan menyisipkannya ke dalam tabel GraphNode
untuk akses lokal.
CREATE NULL_FILTERED INDEX IdxPersonAge
ON GraphNode(id, label, person_age), INTERLEAVE IN GraphNode;
Tabel GraphNode
menyertakan kolom baru yang tersedia sebagai properti
node grafik. Untuk mencerminkan hal ini dalam definisi grafik properti, gunakan pernyataan CREATE OR
REPLACE PROPERTY GRAPH
. Tindakan ini akan mengompilasi ulang definisi dan menyertakan
kolom person_age
baru sebagai properti.
Untuk mengetahui informasi selengkapnya, lihat memperbarui definisi node atau edge yang ada.
Pernyataan berikut mengompilasi ulang definisi dan menyertakan kolom
person_age
baru sebagai properti.
CREATE OR REPLACE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
)
EDGE TABLES (
GraphEdge
SOURCE KEY (id) REFERENCES GraphNode (id)
DESTINATION KEY (dest_id) REFERENCES GraphNode (id)
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
);
Contoh berikut menjalankan kueri dengan properti yang diindeks.
GRAPH FinGraph
MATCH (person:Person {person_age: 43})
RETURN person.id, person.name;
(Opsional) Jalankan perintah ANALYZE
setelah pembuatan indeks agar pengoptimal kueri diperbarui dengan statistik
database terbaru.
Menggunakan batasan pemeriksaan untuk integritas data
Spanner mendukung objek skema seperti batasan pemeriksaan untuk menerapkan integritas data label dan properti. Bagian ini mencantumkan rekomendasi untuk batasan pemeriksaan yang dapat Anda gunakan dengan data tanpa skema.
Menerapkan nilai label
Sebaiknya gunakan NOT NULL
dalam definisi kolom label untuk menghindari nilai label yang tidak ditentukan.
CREATE TABLE GraphNode (
id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON,
) PRIMARY KEY (id);
Menerapkan nilai label dan nama properti huruf kecil
Karena nama label dan properti harus disimpan sebagai nilai huruf kecil, lakukan salah satu hal berikut:
- Terapkan pemeriksaan dalam logika aplikasi.
- Buat batasan pemeriksaan dalam skema.
Pada waktu kueri, nama label dan properti tidak peka huruf besar/kecil.
Contoh berikut menunjukkan cara menambahkan batasan label node ke tabel GraphNode
untuk memastikan label dalam huruf kecil.
ALTER TABLE GraphNode ADD CONSTRAINT NodeLabelLowerCaseCheck
CHECK(LOWER(label) = label);
Contoh berikut menunjukkan cara menambahkan batasan pemeriksaan ke nama
properti tepi. Pemeriksaan menggunakan
JSON_KEYS
untuk
mengakses kunci tingkat teratas.
COALESCE
mengonversi output menjadi array kosong jika JSON_KEYS
menampilkan NULL
, lalu
memeriksa bahwa setiap kunci adalah huruf kecil.
ALTER TABLE GraphEdge ADD CONSTRAINT EdgePropertiesLowerCaseCheck
CHECK(NOT array_includes(COALESCE(JSON_KEYS(properties, 1), []), key->key<>LOWER(key)));
Memastikan properti ada
Buat batasan yang memeriksa apakah properti ada untuk label.
Dalam contoh berikut, batasan memeriksa apakah node person
memiliki properti name
.
ALTER TABLE GraphNode
ADD CONSTRAINT NameMustExistForPersonConstraint
CHECK (IF(label = 'person', properties.name IS NOT NULL, TRUE));
Menerapkan properti unik
Buat batasan berbasis properti yang memeriksa apakah properti node atau edge bersifat unik di seluruh node atau edge dengan label yang sama. Untuk melakukannya, gunakan UNIQUE INDEX terhadap kolom yang dihasilkan properti.
Dalam contoh berikut, indeks unik memeriksa apakah gabungan properti name
dan country
bersifat unik untuk setiap node person
.
Tambahkan kolom yang dihasilkan untuk
PersonName
.ALTER TABLE GraphNode ADD COLUMN person_name STRING(MAX) AS (IF(label = 'person', STRING(properties.name), NULL)) Hidden;
Tambahkan kolom yang dihasilkan untuk
PersonCountry
.ALTER TABLE GraphNode ADD COLUMN person_country STRING(MAX) AS (IF(label = 'person', STRING(properties.country), NULL)) Hidden;
Buat indeks unik
NULL_FILTERED
terhadap propertiPersonName
danPersonCountry
.CREATE UNIQUE NULL_FILTERED INDEX NameAndCountryMustBeUniqueForPerson ON GraphNode (person_name, person_country);
Menerapkan jenis data properti
Terapkan jenis data properti menggunakan batasan jenis data pada nilai properti
untuk label, seperti yang ditunjukkan dalam contoh berikut. Contoh ini menggunakan fungsi
JSON_TYPE
untuk memeriksa bahwa properti name
dari label person
menggunakan
jenis STRING
.
ALTER TABLE GraphNode
ADD CONSTRAINT PersonNameMustBeStringTypeConstraint
CHECK (IF(label = 'person', JSON_TYPE(properties.name) = 'string', TRUE));
Menggabungkan label yang ditentukan dan dinamis
Spanner memungkinkan node dalam grafik properti memiliki label yang ditentukan (ditentukan dalam skema) dan label dinamis (berasal dari data). Sesuaikan label untuk menggunakan fleksibilitas ini.
Pertimbangkan skema berikut yang menunjukkan pembuatan tabel GraphNode
:
CREATE OR REPLACE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
LABEL Entity -- Defined label
DYNAMIC LABEL (label) -- Dynamic label from data column 'label'
DYNAMIC PROPERTIES (properties)
);
Di sini, setiap node yang dibuat dari GraphNode
memiliki label defined Entity
. Selain
itu, setiap node memiliki label dinamis yang ditentukan oleh nilai di kolom
labelnya.
Kemudian, tulis kueri yang mencocokkan node berdasarkan jenis label. Misalnya,
kueri berikut menemukan node menggunakan label Entity
yang ditentukan:
GRAPH FinGraph
MATCH (node:Entity {id: 1}) -- Querying by the defined label
RETURN node.name;
Meskipun kueri ini menggunakan label Entity
yang ditentukan, ingatlah bahwa
node yang cocok juga membawa label dinamis berdasarkan datanya.
Contoh skema
Gunakan contoh skema di bagian ini sebagai template untuk membuat skema Anda sendiri. Komponen skema utama meliputi:
- Pembuatan tabel input grafik
- Pembuatan grafik properti
- Opsional: indeks penelusuran tepi terbalik untuk meningkatkan performa penelusuran terbalik
- Opsional: beri label indeks untuk meningkatkan performa kueri menurut label
- Opsional: batasan skema untuk menerapkan label huruf kecil dan nama properti
Contoh berikut menunjukkan cara membuat tabel input dan grafik properti:
CREATE TABLE GraphNode (
id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON
) PRIMARY KEY (id);
CREATE TABLE GraphEdge (
id INT64 NOT NULL,
dest_id INT64 NOT NULL,
edge_id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON
) PRIMARY KEY (id, dest_id, edge_id),
INTERLEAVE IN PARENT GraphNode;
CREATE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
)
EDGE TABLES (
GraphEdge
SOURCE KEY (id) REFERENCES GraphNode(id)
DESTINATION KEY (dest_id) REFERENCES GraphNode(id)
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
);
Contoh berikut menggunakan indeks untuk meningkatkan penelusuran tepi terbalik. Klausa
STORING (properties)
menyertakan salinan properti tepi, yang mempercepat
kueri yang memfilter properti ini. Anda dapat menghilangkan klausa
STORING (properties)
jika kueri Anda tidak mendapatkan manfaat darinya.
CREATE INDEX R_EDGE ON GraphEdge (dest_id, id, edge_id) STORING (properties),
INTERLEAVE IN GraphNode;
Contoh berikut menggunakan indeks label untuk mempercepat pencocokan node menurut label.
CREATE INDEX IDX_NODE_LABEL ON GraphNode (label);
Contoh berikut menambahkan batasan yang menerapkan label dan
properti huruf kecil. Dua contoh terakhir menggunakan fungsi
JSON_KEYS
. Secara opsional, Anda dapat menerapkan pemeriksaan huruf kecil dalam logika aplikasi.
ALTER TABLE GraphNode ADD CONSTRAINT node_label_lower_case
CHECK(LOWER(label) = label);
ALTER TABLE GraphEdge ADD CONSTRAINT edge_label_lower_case
CHECK(LOWER(label) = label);
ALTER TABLE GraphNode ADD CONSTRAINT node_property_keys_lower_case
CHECK(
NOT array_includes(COALESCE(JSON_KEYS(properties, 1), []), key->key<>LOWER(key)));
ALTER TABLE GraphEdge ADD CONSTRAINT edge_property_keys_lower_case
CHECK(
NOT array_includes(COALESCE(JSON_KEYS(properties, 1), []), key->key<>LOWER(key)));
Mengoptimalkan update batch properti dinamis dengan DML
Memodifikasi properti dinamis menggunakan fungsi seperti
JSON_SET
dan
JSON_REMOVE
melibatkan operasi baca-modifikasi-tulis. Hal ini dapat menyebabkan biaya yang lebih tinggi dibandingkan dengan memperbarui properti jenis STRING
atau INT64
.
Jika beban kerja melibatkan update batch pada properti dinamis menggunakan DML, gunakan rekomendasi berikut untuk mencapai performa yang lebih baik:
Perbarui beberapa baris dalam satu pernyataan DML, bukan memproses baris satu per satu.
Saat memperbarui rentang kunci yang luas, kelompokkan dan urutkan baris yang terpengaruh menurut kunci utama. Memperbarui rentang yang tidak tumpang-tindih dengan setiap DML akan mengurangi pertentangan kunci.
Gunakan parameter kueri dalam pernyataan DML, bukan meng-hard code-nya untuk meningkatkan performa.
Berdasarkan saran ini, contoh berikut menunjukkan cara memperbarui properti
is_blocked
untuk 100 node dalam satu pernyataan DML. Parameter
kueri mencakup:
@node_ids
: Kunci barisGraphNode
, disimpan dalam parameterARRAY
. Jika berlaku, pengelompokan dan pengurutan di seluruh DML akan menghasilkan performa yang lebih baik.@is_blocked_values
: Nilai terkait yang akan diperbarui, disimpan dalam parameterARRAY
.
UPDATE GraphNode
SET properties = JSON_SET(
properties,
'$.is_blocked',
CASE id
WHEN @node_ids[OFFSET(0)] THEN @is_blocked_values[OFFSET(0)]
WHEN @node_ids[OFFSET(1)] THEN @is_blocked_values[OFFSET(1)]
...
WHEN @node_ids[OFFSET(99)] THEN @is_blocked_values[OFFSET(99)]
END,
create_if_missing => TRUE)
WHERE id IN UNNEST(@node_ids)
Memecahkan masalah
Bagian ini menjelaskan cara memecahkan masalah terkait data tanpa skema.
Properti muncul beberapa kali dalam hasil TO_JSON
Masalah
Node berikut memodelkan properti birthday
dan name
sebagai properti
dinamis di kolom JSON
-nya. Properti birthday
dan name
duplikat
muncul di hasil JSON elemen grafik.
GRAPH FinGraph
MATCH (n: Person {id: 14})
RETURN SAFE_TO_JSON(n) AS n;
Tindakan ini akan menampilkan hasil yang mirip dengan berikut ini:
{
…,
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex",
"id": 14,
"label": "person",
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex"
}
}
…
}
Kemungkinan penyebab
Secara default, semua kolom tabel dasar ditentukan sebagai properti. Menggunakan
TO_JSON
atau
SAFE_TO_JSON
untuk menampilkan elemen grafik akan menghasilkan properti duplikat. Hal ini terjadi karena
kolom JSON
(properties
) adalah properti yang ditentukan skemanya, sedangkan
kunci tingkat pertama JSON
dimodelkan sebagai properti dinamis.
Solusi yang direkomendasikan
Untuk menghindari perilaku ini, gunakan klausa PROPERTIES ALL COLUMNS EXCEPT
untuk mengecualikan kolom properties
saat Anda menentukan properti dalam
skema, seperti yang ditunjukkan dalam contoh berikut:
CREATE OR REPLACE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
PROPERTIES ALL COLUMNS EXCEPT (properties)
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
);
Setelah perubahan skema, elemen grafik yang ditampilkan dari jenis data JSON
tidak memiliki duplikat.
GRAPH FinGraph
MATCH (n: Person {id: 1})
RETURN TO_JSON(n) AS n;
Kueri ini menampilkan hal berikut:
{
…
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex",
"id": 1,
"label": "person",
}
}
Masalah umum saat nilai properti tidak dikonversi dengan benar
Untuk memperbaiki masalah berikut, selalu gunakan konversi nilai properti saat menggunakan properti di dalam ekspresi kueri.
Perbandingan nilai properti tanpa konversi
Masalah
No matching signature for operator = for argument types: JSON, STRING
Kemungkinan penyebab
Kueri tidak mengonversi nilai properti dengan benar. Misalnya, properti name
tidak dikonversi ke jenis STRING
dalam perbandingan:
GRAPH FinGraph
MATCH (p:Person)
WHERE p.name = "Alex"
RETURN p.id;
Solusi yang direkomendasikan
Untuk memperbaiki masalah ini, gunakan konversi nilai sebelum perbandingan.
GRAPH FinGraph
MATCH (p:Person)
WHERE STRING(p.name) = "Alex"
RETURN p.id;
Tindakan ini akan menampilkan hasil yang mirip dengan berikut ini:
+------+
| id |
+------+
| 1 |
+------+
Atau, gunakan filter properti untuk menyederhanakan perbandingan kesamaan saat konversi nilai terjadi secara otomatis. Perhatikan bahwa
jenis nilai ("Alex") harus sama persis dengan jenis STRING
properti dalam
JSON
.
GRAPH FinGraph
MATCH (p:Person {name: 'Alex'})
RETURN p.id;
Tindakan ini akan menampilkan hasil yang mirip dengan berikut ini:
+------+
| id |
+------+
| 1 |
+------+
Penggunaan nilai properti RETURN DISTINCT
tanpa konversi
Masalah
Column order_number_str of type JSON cannot be used in `RETURN DISTINCT
Kemungkinan penyebab
Dalam contoh berikut, order_number_str
belum dikonversi sebelum
digunakan dalam pernyataan RETURN DISTINCT
:
GRAPH FinGraph
MATCH -[t:Transfers]->
RETURN DISTINCT t.order_number_str AS order_number_str;
Solusi yang direkomendasikan
Untuk memperbaiki masalah ini, gunakan konversi nilai sebelum RETURN DISTINCT
.
GRAPH FinGraph
MATCH -[t:Transfers]->
RETURN DISTINCT STRING(t.order_number_str) AS order_number_str;
Tindakan ini akan menampilkan hasil yang mirip dengan berikut ini:
+-----------------+
| order_number_str|
+-----------------+
| 302290001255747 |
| 103650009791820 |
| 304330008004315 |
| 304120005529714 |
+-----------------+
Properti yang digunakan sebagai kunci pengelompokan tanpa konversi
Masalah
Grouping by expressions of type JSON is not allowed.
Kemungkinan penyebab
Dalam contoh berikut, t.order_number_str
tidak dikonversi sebelum digunakan
untuk mengelompokkan objek JSON:
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN t.order_number_str, COUNT(*) AS total_transfers;
Solusi yang direkomendasikan
Untuk memperbaiki masalah ini, gunakan konversi nilai sebelum menggunakan properti sebagai kunci pengelompokan.
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN STRING(t.order_number_str) AS order_number_str, COUNT(*) AS total_transfers;
Tindakan ini akan menampilkan hasil yang mirip dengan berikut ini:
+-----------------+------------------+
| order_number_str | total_transfers |
+-----------------+------------------+
| 302290001255747 | 1 |
| 103650009791820 | 1 |
| 304330008004315 | 1 |
| 304120005529714 | 2 |
+-----------------+------------------+
Properti yang digunakan sebagai kunci pengurutan tanpa konversi
Masalah
ORDER BY does not support expressions of type JSON
Kemungkinan penyebab
Dalam contoh berikut, t.amount
tidak dikonversi sebelum digunakan untuk
mengurutkan hasil:
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN a.Id AS from_account, b.Id AS to_account, t.amount
ORDER BY t.amount DESC
LIMIT 1;
Solusi yang direkomendasikan
Untuk memperbaiki masalah ini, lakukan konversi pada t.amount
dalam klausa ORDER BY
.
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN a.Id AS from_account, b.Id AS to_account, t.amount
ORDER BY DOUBLE(t.amount) DESC
LIMIT 1;
Tindakan ini akan menampilkan hasil yang mirip dengan berikut ini:
+--------------+------------+--------+
| from_account | to_account | amount |
+--------------+------------+--------+
| 20 | 7 | 500 |
+--------------+------------+--------+
Jenis tidak cocok selama konversi
Masalah
The provided JSON input is not an integer
Kemungkinan penyebab
Dalam contoh berikut, properti order_number_str
disimpan sebagai jenis data JSON
STRING
. Jika Anda mencoba melakukan konversi ke INT64
, error akan ditampilkan.
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE INT64(e.order_number_str) = 302290001255747
RETURN e.amount;
Solusi yang direkomendasikan
Untuk memperbaiki masalah ini, gunakan konverter nilai persis yang cocok dengan jenis nilai.
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE STRING(e.order_number_str) = "302290001255747"
RETURN e.amount;
Tindakan ini akan menampilkan hasil yang mirip dengan berikut ini:
+-----------+
| amount |
+-----------+
| JSON"200" |
+-----------+
Atau, gunakan konverter fleksibel jika nilai dapat dikonversi ke jenis target, seperti yang ditunjukkan dalam contoh berikut:
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE LAX_INT64(e.order_number_str) = 302290001255747
RETURN e.amount;
Tindakan ini akan menampilkan hasil yang mirip dengan berikut ini:
+-----------+
| amount |
+-----------+
| JSON"200" |
+-----------+
Langkah berikutnya
- Untuk mempelajari JSON lebih lanjut, lihat Mengubah data JSON dan Daftar fungsi JSON.
- Bandingkan Spanner Graph dan openCypher.
- Migrasikan ke Spanner Graph.