Mengelola data tanpa skema

Halaman ini menjelaskan cara mengelola data tanpa skema di Spanner Graph. Panduan 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 dengan id unik.

  • Menyimpan semua edge dalam satu tabel, GraphEdge, yang diidentifikasi dengan 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 pasangan id ke dest_id.

Tabel node dan edge memiliki kolom label dan properties sendiri. 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 klausul berikut untuk menentukan label dan properti untuk data tanpa skema:

  • DYNAMIC LABEL: membuat label node atau edge dari kolom STRING yang berasal dari tabel input.
  • DYNAMIC PROPERTIES: membuat properti node atau edge dari kolom JSON 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 akan dipetakan ke node Person dalam grafik. Demikian pula, dalam baris GraphEdge, jika kolom label memiliki nilai owns, kolom tersebut dipetakan ke edge Owns dalam grafik.

Memetakan label GraphNode ke label GraphEdge

Untuk mengetahui informasi selengkapnya tentang batasan saat menggunakan label dinamis, lihat Batasan.

Menentukan properti dinamis

Klausa DYNAMIC PROPERTIES menetapkan kolom jenis data JSON untuk menyimpan properti. Kunci JSON mewakili nama properti, dan nilai JSON mewakili nilai properti.

Misalnya, jika kolom properties baris GraphNode memiliki nilai JSON '{"name": "David", "age": 43}', Spanner akan memetakannya ke node yang memiliki properti age dan name dengan 43 dan "David" sebagai nilainya masing-masing.

Pertimbangan untuk pengelolaan data tanpa skema

Anda mungkin tidak ingin menggunakan pengelolaan data tanpa skema dalam skenario berikut:

  • Jenis node dan tepi 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 daripada memperkenalkan tabel node dan edge khusus yang baru.
  • Batasan 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 skema 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 dalam Ringkasan Kueri Spanner Graph dan Referensi GQL dengan modifikasi terbatas.

Mencocokkan node dan tepi menggunakan label

Anda dapat mencocokkan node dan edge menggunakan ekspresi label di GQL.

Kueri berikut mencocokkan node dan edge 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 level teratas dari jenis data JSON sebagai properti, seperti age dan name dalam contoh berikut.

JSON document Properties

   {
     "name": "Tom",
     "age": 43,
   }
"name": "Tom"
"age": 43

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:

"Tom"

Memfilter, membandingkan, dan mengurutkan berdasarkan properti dinamis

Anda dapat menggunakan properti dinamis secara langsung di filter properti Spanner Graph dan dalam klausa WHERE, GROUP BY, ORDER BY, dan RETURN DISTINCT. Hal ini karena Spanner memperlakukan properti dinamis sebagai nilai jenis data JSON, yang mendukung perbandingan dan pengurutan JSON.

Misalnya, kueri berikut memfilter properti boolean is_blocked dan properti string order_number_str dalam klausa WHERE:

GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->()
WHERE a.is_blocked = false AND t.order_number_str = "302290001255747"
RETURN a.id AS account_id;

Tindakan ini akan menampilkan hasil yang mirip dengan berikut ini:

+------------+
| account_id |
+------------+
| 7          |
+------------+

Anda dapat mengekspresikan kondisi kesetaraan yang sama menggunakan filter properti pola elemen:

GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str: "302290001255747"}]->()
RETURN a.id AS account_id;

Anda juga dapat menggunakan properti dinamis secara langsung dalam klausa GROUP BY, ORDER BY, dan RETURN DISTINCT:

  • Contoh GROUP BY:

    GRAPH FinGraph
    MATCH (person:Person {country: "South Korea"})
    RETURN person.city AS person_city, COUNT(*) AS cnt
    LIMIT 10;
    
  • Contoh 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 t.amount DESC
    LIMIT 1;
    
  • Contoh RETURN DISTINCT:

    GRAPH FinGraph
    MATCH -[t:Transfers]->
    RETURN DISTINCT t.order_number_str AS order_number_str;
    

Saat memfilter, membandingkan, atau mengurutkan properti dinamis, aturan berikut berlaku:

  • Nilai yang dibandingkan harus cocok dengan jenis dan nilai JSON properti. Misalnya, karena order_number_str disimpan sebagai string JSON, membandingkannya dengan literal bilangan bulat tidak akan menemukan kecocokan:

    GRAPH FinGraph
    MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str: 302290001255747}]->()
    RETURN t.order_number_str;
    
  • Mengevaluasi properti boolean JSON secara langsung sebagai kondisi WHERE mandiri, seperti WHERE a.is_blocked, akan menampilkan error. Sebagai gantinya, bandingkan properti dengan literal boolean, seperti WHERE a.is_blocked = true, atau konversikan secara eksplisit dengan WHERE BOOL(a.is_blocked).

  • Untuk mengetahui aturan dan batasan mendetail tentang perbandingan, pengelompokan, dan pengurutan nilai JSON, lihat Perbandingan dan pengurutan JSON dan Pemaksaan perbandingan JSON.

Mengonversi jenis data properti dinamis

Konversikan properti dinamis menjadi jenis data SQL dalam skenario berikut:

  • Mengonversi nilai properti ke jenis data yang berbeda untuk perbandingan atau output, seperti mengonversi string JSON "302290001255747" menjadi bilangan bulat INT64, atau menampilkan jenis data SQL, bukan JSON.
  • Menggunakan nilai properti dalam fungsi, operator, atau ekspresi SQL yang tidak mendukung operand JSON, seperti operator aritmetika (+, -), fungsi string (LOWER, LIKE), fungsi agregat (SUM, AVG), atau IN UNNEST.
  • Menentukan kolom yang dihasilkan pada properti untuk indeks sekunder atau indeks unik, karena ekspresi JSON tidak dapat diindeks secara langsung.

Dalam contoh berikut, order_number_str disimpan sebagai string JSON, dan kueri menggunakan fungsi LAX_INT64 untuk mengonversinya menjadi bilangan bulat INT64 untuk perbandingan dan pengembalian:

GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->()
WHERE a.is_blocked = true AND LAX_INT64(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       |
+-----------------------+

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

   {
     "name": "Tom",
     "age": 43,
     "location": {
       "city": "New York",
       "state": "NY",
       "country": "USA",
     }
   }
"name": "Tom"
"age": 34
"location": {
  "city": "New York",
  "state": "NY",
  "country": "USA",
}

Contoh berikut menunjukkan cara mengakses properti bertingkat dengan operator akses kolom JSON.

GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN 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 edge 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, memperbarui, 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 mengupdate 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 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 menggunakan 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 menggunakan huruf kecil

Anda harus menyimpan nama properti dalam huruf kecil. Sebaiknya Anda menerapkan aturan ini baik dalam kode aplikasi atau menggunakan batasan skema.

Meskipun nama properti harus disimpan sebagai huruf kecil, nama properti 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

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. Contoh skema menggunakan kolom INT64 edge_id logika aplikasi.

Saat Anda membuat skema untuk tabel node dan edge, sertakan kolom label sebagai kolom kunci utama secara opsional jika nilainya tidak dapat diubah. Jika Anda melakukannya, kunci komposit yang dibentuk oleh semua kolom kunci harus unik di semua node atau edge. Teknik ini meningkatkan performa untuk kueri yang hanya difilter menurut label.

Untuk 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 dihasilkan 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;

Atau, 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 dalam huruf kecil

Karena nama label dan properti harus disimpan sebagai nilai huruf kecil, lakukan salah satu hal berikut:

Pada waktu kueri, label dan nama properti tidak peka huruf besar/kecil.

Contoh berikut menunjukkan cara menambahkan batasan label node ke tabel GraphNode untuk memastikan labelnya dalam huruf kecil.

ALTER TABLE GraphNode ADD CONSTRAINT NodeLabelLowerCaseCheck
CHECK(LOWER(label) = label);

Contoh berikut menunjukkan cara menambahkan batasan pemeriksaan ke nama properti edge. Pemeriksaan ini menggunakan JSON_KEYS untuk mengakses kunci tingkat atas. COALESCE mengonversi output menjadi array kosong jika JSON_KEYS menampilkan NULL, lalu memeriksa apakah setiap kunci menggunakan 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 unik di seluruh node atau edge dengan label yang sama. Untuk melakukannya, gunakan INDEKS UNIK terhadap kolom yang dihasilkan dari properti.

Dalam contoh berikut, indeks unik memeriksa bahwa properti name dan country yang digabungkan bersifat unik untuk node person apa pun.

  1. Tambahkan kolom yang dihasilkan untuk PersonName.

    ALTER TABLE GraphNode
    ADD COLUMN person_name STRING(MAX)
    AS (IF(label = 'person', STRING(properties.name), NULL)) Hidden;
    
  2. Tambahkan kolom yang dibuat untuk PersonCountry.

    ALTER TABLE GraphNode
    ADD COLUMN person_country STRING(MAX)
    AS (IF(label = 'person', STRING(properties.country), NULL)) Hidden;
    
  3. Buat indeks unik NULL_FILTERED terhadap properti PersonName dan PersonCountry.

    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 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 salah satu 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 yang ditentukan Entity, 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 mencakup hal berikut:

  • Pembuatan tabel input grafik
  • Pembuatan graf properti
  • Opsional: indeks traversal edge terbalik untuk meningkatkan performa traversal terbalik
  • Opsional: indeks label untuk meningkatkan performa kueri berdasarkan 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 berdasarkan 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

Mengubah properti dinamis menggunakan fungsi seperti JSON_SET dan JSON_REMOVE melibatkan operasi baca-ubah-tulis. Hal ini dapat menyebabkan biaya yang lebih tinggi dibandingkan dengan memperbarui properti jenis STRING atau INT64.

Jika beban kerja melibatkan kumpulan update ke 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 berdasarkan 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:

  1. @node_ids: Kunci baris GraphNode, disimpan dalam parameter ARRAY. Jika memungkinkan, pengelompokan dan pengurutan di seluruh DML akan menghasilkan performa yang lebih baik.

  2. @is_blocked_values: Nilai terkait yang akan diperbarui, disimpan dalam parameter ARRAY.

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 dalam 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 skema, 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",
  }
}

Langkah berikutnya