Membuat peningkatan Gremlin yang efisien dengan fold()/coalesce()/unfold() - Amazon Neptune

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

Membuat peningkatan Gremlin yang efisien dengan fold()/coalesce()/unfold()

Upsert (atau sisipan bersyarat) menggunakan kembali simpul atau tepi jika sudah ada, atau membuatnya jika tidak. Upserts yang efisien dapat membuat perbedaan yang signifikan dalam kinerja kueri Gremlin.

Halaman ini menunjukkan bagaimana menggunakan pola fold()/coalesce()/unfold() Gremlin untuk membuat upserts yang efisien. Namun, dengan rilis TinkerPop versi 3.6.x yang diperkenalkan di Neptunus dalam versi mesin 1.2.1.0, langkah baru mergeV() dan lebih disukai dalam banyak kasus. mergeE() fold()/coalesce()/unfold()Pola yang dijelaskan di sini mungkin masih berguna dalam beberapa situasi yang kompleks, tetapi dalam penggunaan umum mergeV() dan mergeE() jika Anda bisa, seperti yang dijelaskan dalamMembuat peningkatan yang efisien dengan mergeV() Gremlin dan langkah-langkah mergeE().

Upserts memungkinkan Anda untuk menulis operasi penyisipan idempoten: tidak peduli berapa kali Anda menjalankan operasi seperti itu, hasil keseluruhannya sama. Ini berguna dalam skenario penulisan yang sangat bersamaan di mana modifikasi bersamaan pada bagian grafik yang sama dapat memaksa satu atau lebih transaksi untuk memutar kembali dengan aConcurrentModificationException, sehingga memerlukan percobaan ulang.

Misalnya, kueri berikut meningkatkan simpul dengan terlebih dahulu mencari simpul yang ditentukan dalam kumpulan data, dan kemudian melipat hasilnya ke dalam daftar. Dalam traversal pertama yang diberikan ke coalesce() langkah, kueri kemudian membuka daftar ini. Jika daftar yang tidak dilipat tidak kosong, hasilnya dipancarkan dari. coalesce() Namun, jika unfold() mengembalikan koleksi kosong karena simpul saat ini tidak ada, lanjutkan coalesce() untuk mengevaluasi traversal kedua yang telah disediakan, dan dalam traversal kedua ini kueri membuat simpul yang hilang.

g.V('v-1').fold() .coalesce( unfold(), addV('Person').property(id, 'v-1') .property('email', 'person-1@example.org') )

Gunakan formulir yang dioptimalkan coalesce() untuk upserts

Neptunus dapat mengoptimalkan fold().coalesce(unfold(), ...) idiom untuk membuat pembaruan throughput tinggi, tetapi pengoptimalan ini hanya berfungsi jika kedua bagian pengembalian baik simpul atau tepi coalesce() tetapi tidak ada yang lain. Jika Anda mencoba mengembalikan sesuatu yang berbeda, seperti properti, dari bagian mana puncoalesce(), pengoptimalan Neptunus tidak terjadi. Kueri mungkin berhasil, tetapi tidak akan berkinerja sebaik versi yang dioptimalkan, terutama terhadap kumpulan data besar.

Karena kueri upsert yang tidak dioptimalkan meningkatkan waktu eksekusi dan mengurangi throughput, ada baiknya menggunakan explain titik akhir Gremlin untuk menentukan apakah kueri upsert sepenuhnya dioptimalkan. Saat meninjau explain rencana, cari garis yang dimulai dengan + not converted into Neptune steps danWARNING: >>. Sebagai contoh:

+ not converted into Neptune steps: [FoldStep, CoalesceStep([[UnfoldStep], [AddEdgeSte... WARNING: >> FoldStep << is not supported natively yet

Peringatan ini dapat membantu Anda mengidentifikasi bagian-bagian kueri yang mencegahnya dioptimalkan sepenuhnya.

Terkadang tidak mungkin untuk mengoptimalkan kueri sepenuhnya. Dalam situasi ini Anda harus mencoba meletakkan langkah-langkah yang tidak dapat dioptimalkan di akhir kueri, sehingga memungkinkan mesin untuk mengoptimalkan sebanyak mungkin langkah. Teknik ini digunakan dalam beberapa contoh batch upsert, di mana semua upsert yang dioptimalkan untuk satu set simpul atau tepi dilakukan sebelum modifikasi tambahan yang berpotensi tidak dioptimalkan diterapkan pada simpul atau tepi yang sama.

Batching upserts untuk meningkatkan throughput

Untuk skenario penulisan throughput tinggi, Anda dapat menggabungkan langkah-langkah upsert bersama-sama untuk meningkatkan simpul dan tepi dalam batch. Batching mengurangi overhead transaksional untuk menaikkan sejumlah besar simpul dan tepi. Anda kemudian dapat lebih meningkatkan throughput dengan meningkatkan permintaan batch secara paralel menggunakan beberapa klien.

Sebagai aturan praktis, kami merekomendasikan untuk meningkatkan sekitar 200 catatan per permintaan batch. Rekaman adalah satu titik atau label tepi atau properti. Sebuah simpul dengan label tunggal dan 4 properti, misalnya, membuat 5 catatan. Tepi dengan label dan properti tunggal menciptakan 2 catatan. Jika Anda ingin meningkatkan kumpulan simpul, masing-masing dengan label tunggal dan 4 properti, Anda harus mulai dengan ukuran batch 40, karena. 200 / (1 + 4) = 40

Anda dapat bereksperimen dengan ukuran batch. 200 catatan per batch adalah titik awal yang baik, tetapi ukuran batch yang ideal mungkin lebih tinggi atau lebih rendah tergantung pada beban kerja Anda. Perhatikan, bagaimanapun, bahwa Neptunus dapat membatasi jumlah keseluruhan langkah Gremlin per permintaan. Batas ini tidak didokumentasikan, tetapi untuk berada di sisi yang aman cobalah untuk memastikan bahwa permintaan Anda mengandung tidak lebih dari 1500 langkah Gremlin. Neptunus dapat menolak permintaan batch besar dengan lebih dari 1500 langkah.

Untuk meningkatkan throughput, Anda dapat meningkatkan batch secara paralel menggunakan beberapa klien (lihat). Membuat Penulisan Gremlin Multithreaded yang Efisien Jumlah klien harus sama dengan jumlah thread pekerja pada instance penulis Neptunus Anda, yang biasanya 2 x vCPUs jumlah di server. Misalnya, sebuah r5.8xlarge instance memiliki 32 vCPUs dan 64 thread pekerja. Untuk skenario penulisan throughput tinggi menggunakanr5.8xlarge, Anda akan menggunakan 64 klien yang menulis batch upserts ke Neptunus secara paralel.

Setiap klien harus mengirimkan permintaan batch dan menunggu permintaan selesai sebelum mengirimkan permintaan lain. Meskipun beberapa klien berjalan secara paralel, setiap klien individu mengirimkan permintaan secara serial. Ini memastikan bahwa server disuplai dengan aliran permintaan yang stabil yang menempati semua thread pekerja tanpa membanjiri antrian permintaan sisi server (lihat). Mengukur instans DB dalam sebuah klaster Neptune DB

Cobalah untuk menghindari langkah-langkah yang menghasilkan banyak pelintas

Ketika langkah Gremlin dijalankan, dibutuhkan traverser masuk, dan memancarkan satu atau lebih traverser keluaran. Jumlah pelintas yang dipancarkan oleh satu langkah menentukan berapa kali langkah berikutnya dijalankan.

Biasanya, ketika melakukan operasi batch Anda ingin setiap operasi, seperti upsert vertex A, untuk mengeksekusi sekali, sehingga urutan operasi terlihat seperti ini: upsert vertex A, kemudian upsert vertex B, kemudian upsert vertex C, dan seterusnya. Selama sebuah langkah membuat atau memodifikasi hanya satu elemen, ia hanya memancarkan satu traverser, dan langkah-langkah yang mewakili operasi berikutnya dijalankan hanya sekali. Jika, di sisi lain, sebuah operasi membuat atau memodifikasi lebih dari satu elemen, ia memancarkan beberapa pelintas, yang pada gilirannya menyebabkan langkah-langkah selanjutnya dieksekusi beberapa kali, sekali per traverser yang dipancarkan. Hal ini dapat mengakibatkan database melakukan pekerjaan tambahan yang tidak perlu, dan dalam beberapa kasus dapat mengakibatkan penciptaan simpul tambahan yang tidak diinginkan, tepi atau nilai properti.

Contoh bagaimana hal-hal bisa salah adalah dengan kueri sepertig.V().addV(). Kueri sederhana ini menambahkan simpul untuk setiap simpul yang ditemukan dalam grafik, karena V() memancarkan traverser untuk setiap simpul dalam grafik dan masing-masing pelintas tersebut memicu panggilan ke. addV()

Lihat Mencampur upserts dan sisipan cara menangani operasi yang dapat memancarkan banyak pelintas.

Menambah simpul

Anda dapat menggunakan ID simpul untuk menentukan apakah ada simpul yang sesuai. Ini adalah pendekatan yang lebih disukai, karena Neptunus mengoptimalkan upserts untuk kasus penggunaan yang sangat bersamaan. IDs Sebagai contoh, kueri berikut membuat simpul dengan ID simpul yang diberikan jika belum ada, atau menggunakannya kembali jika memang demikian:

g.V('v-1') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-1') .property('email', 'person-1@example.org')) .id()

Perhatikan bahwa kueri ini diakhiri dengan id() langkah. Meskipun tidak sepenuhnya diperlukan untuk tujuan meningkatkan simpul, menambahkan id() langkah ke akhir kueri upsert memastikan bahwa server tidak membuat serial semua properti simpul kembali ke klien, yang membantu mengurangi biaya penguncian kueri.

Atau, Anda dapat menggunakan properti simpul untuk menentukan apakah simpul itu ada:

g.V() .hasLabel('Person') .has('email', 'person-1@example.org') .fold() .coalesce(unfold(), addV('Person').property('email', 'person-1@example.org')) .id()

Jika memungkinkan, gunakan sendiri yang disediakan pengguna IDs untuk membuat simpul, dan gunakan ini IDs untuk menentukan apakah simpul ada selama operasi upsert. Ini memungkinkan Neptunus mengoptimalkan upserts di sekitar. IDs Peningkatan berbasis ID dapat secara signifikan lebih efisien daripada peningkatan berbasis properti dalam skenario modifikasi yang sangat bersamaan.

Bagian atas simpul berantai

Anda dapat menghubungkan bagian atas simpul bersama-sama untuk menyisipkannya dalam batch:

g.V('v-1') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-1') .property('email', 'person-1@example.org')) .V('v-2') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-2') .property('email', 'person-2@example.org')) .V('v-3') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-3') .property('email', 'person-3@example.org')) .id()

Tepi yang menjulang

Anda dapat menggunakan tepi IDs untuk menaikkan tepi dengan cara yang sama Anda menaikkan simpul menggunakan simpul kustom. IDs Sekali lagi, ini adalah pendekatan yang disukai karena memungkinkan Neptunus untuk mengoptimalkan kueri. Misalnya, kueri berikut membuat tepi berdasarkan ID tepinya jika belum ada, atau menggunakannya kembali jika ada. Kueri juga menggunakan simpul from dan to simpul jika perlu membuat tepi baru. IDs

g.E('e-1') .fold() .coalesce(unfold(), addE('KNOWS').from(V('v-1')) .to(V('v-2')) .property(id, 'e-1')) .id()

Banyak aplikasi menggunakan simpul khususIDs, tetapi meninggalkan Neptunus untuk menghasilkan tepi. IDs Jika Anda tidak tahu ID tepi, tetapi Anda tahu from dan to simpulIDs, Anda dapat menggunakan formulasi ini untuk meningkatkan tepi:

g.V('v-1') .outE('KNOWS') .where(inV().hasId('v-2')) .fold() .coalesce(unfold(), addE('KNOWS').from(V('v-1')) .to(V('v-2'))) .id()

Perhatikan bahwa langkah simpul dalam where() klausa harus inV() (atau outV() jika Anda pernah inE() menemukan tepi), bukan. otherV() Jangan gunakanotherV(), di sini, atau kueri tidak akan dioptimalkan dan kinerja akan menurun. Misalnya, Neptunus tidak akan mengoptimalkan kueri berikut:

// Unoptimized upsert, because of otherV() g.V('v-1') .outE('KNOWS') .where(otherV().hasId('v-2')) .fold() .coalesce(unfold(), addE('KNOWS').from(V('v-1')) .to(V('v-2'))) .id()

Jika Anda tidak tahu tepi atau simpul IDs di depan, Anda dapat meningkatkan menggunakan properti simpul:

g.V() .hasLabel('Person') .has('name', 'person-1') .outE('LIVES_IN') .where(inV().hasLabel('City').has('name', 'city-1')) .fold() .coalesce(unfold(), addE('LIVES_IN').from(V().hasLabel('Person') .has('name', 'person-1')) .to(V().hasLabel('City') .has('name', 'city-1'))) .id()

Seperti halnya vertex upserts, lebih baik menggunakan edge upserts berbasis ID menggunakan ID tepi atau from dan to simpul, daripada upsert berbasis propertiIDs, sehingga Neptunus dapat sepenuhnya mengoptimalkan upsert.

Memeriksa from dan to keberadaan simpul

Perhatikan konstruksi langkah-langkah yang membuat tepi baru:addE().from().to(). Konstruksi ini memastikan bahwa kueri memeriksa keberadaan simpul from dan to simpul. Jika salah satu dari ini tidak ada, kueri mengembalikan kesalahan sebagai berikut:

{ "detailedMessage": "Encountered a traverser that does not map to a value for child... "code": "IllegalArgumentException", "requestId": "..." }

Jika mungkin salah satu from atau to simpul tidak ada, Anda harus mencoba untuk meningkatkannya sebelum menaikkan tepi di antara keduanya. Lihat Menggabungkan vertex dan edge upserts.

Ada konstruksi alternatif untuk membuat tepi yang tidak boleh Anda gunakan:V().addE().to(). Itu hanya menambahkan tepi jika from simpul ada. Jika to simpul tidak ada, kueri menghasilkan kesalahan, seperti yang dijelaskan sebelumnya, tetapi jika from simpul tidak ada, itu diam-diam gagal memasukkan tepi, tanpa menghasilkan kesalahan apa pun. Misalnya, upsert berikut selesai tanpa menaikkan tepi jika from simpul tidak ada:

// Will not insert edge if from vertex does not exist g.V('v-1') .outE('KNOWS') .where(inV().hasId('v-2')) .fold() .coalesce(unfold(), V('v-1').addE('KNOWS') .to(V('v-2'))) .id()

Bagian atas tepi rantai

Jika Anda ingin menghubungkan edge upserts bersama-sama untuk membuat permintaan batch, Anda harus memulai setiap upsert dengan pencarian simpul, bahkan jika Anda sudah tahu tepi. IDs

Jika Anda sudah tahu tepi IDs yang ingin Anda sertakan, dan simpul dan to simpul, Anda dapat menggunakan formulasi ini: IDs from

g.V('v-1') .outE('KNOWS') .hasId('e-1') .fold() .coalesce(unfold(), V('v-1').addE('KNOWS') .to(V('v-2')) .property(id, 'e-1')) .V('v-3') .outE('KNOWS') .hasId('e-2').fold() .coalesce(unfold(), V('v-3').addE('KNOWS') .to(V('v-4')) .property(id, 'e-2')) .V('v-5') .outE('KNOWS') .hasId('e-3') .fold() .coalesce(unfold(), V('v-5').addE('KNOWS') .to(V('v-6')) .property(id, 'e-3')) .id()

Mungkin skenario peningkatan tepi batch yang paling umum adalah Anda mengetahui from dan to simpulnyaIDs, tetapi tidak tahu tepi IDs yang ingin Anda tingkatkan. Dalam hal ini, gunakan formulasi berikut:

g.V('v-1') .outE('KNOWS') .where(inV().hasId('v-2')) .fold() .coalesce(unfold(), V('v-1').addE('KNOWS') .to(V('v-2'))) .V('v-3') .outE('KNOWS') .where(inV().hasId('v-4')) .fold() .coalesce(unfold(), V('v-3').addE('KNOWS') .to(V('v-4'))) .V('v-5') .outE('KNOWS') .where(inV().hasId('v-6')) .fold() .coalesce(unfold(), V('v-5').addE('KNOWS').to(V('v-6'))) .id()

Jika Anda mengetahui IDs tepi yang ingin Anda sertakan, tetapi tidak tahu simpulnya from dan IDs to simpulnya (ini tidak biasa), Anda dapat menggunakan formulasi ini:

g.V() .hasLabel('Person') .has('email', 'person-1@example.org') .outE('KNOWS') .hasId('e-1') .fold() .coalesce(unfold(), V().hasLabel('Person') .has('email', 'person-1@example.org') .addE('KNOWS') .to(V().hasLabel('Person') .has('email', 'person-2@example.org')) .property(id, 'e-1')) .V() .hasLabel('Person') .has('email', 'person-3@example.org') .outE('KNOWS') .hasId('e-2') .fold() .coalesce(unfold(), V().hasLabel('Person') .has('email', 'person-3@example.org') .addE('KNOWS') .to(V().hasLabel('Person') .has('email', 'person-4@example.org')) .property(id, 'e-2')) .V() .hasLabel('Person') .has('email', 'person-5@example.org') .outE('KNOWS') .hasId('e-1') .fold() .coalesce(unfold(), V().hasLabel('Person') .has('email', 'person-5@example.org') .addE('KNOWS') .to(V().hasLabel('Person') .has('email', 'person-6@example.org')) .property(id, 'e-3')) .id()

Menggabungkan vertex dan edge upserts

Terkadang Anda mungkin ingin meningkatkan kedua simpul dan tepi yang menghubungkannya. Anda dapat mencampur contoh batch yang disajikan di sini. Contoh berikut meningkatkan 3 simpul dan 2 tepi:

g.V('p-1') .fold() .coalesce(unfold(), addV('Person').property(id, 'p-1') .property('email', 'person-1@example.org')) .V('p-2') .fold() .coalesce(unfold(), addV('Person').property(id, 'p-2') .property('name', 'person-2@example.org')) .V('c-1') .fold() .coalesce(unfold(), addV('City').property(id, 'c-1') .property('name', 'city-1')) .V('p-1') .outE('LIVES_IN') .where(inV().hasId('c-1')) .fold() .coalesce(unfold(), V('p-1').addE('LIVES_IN') .to(V('c-1'))) .V('p-2') .outE('LIVES_IN') .where(inV().hasId('c-1')) .fold() .coalesce(unfold(), V('p-2').addE('LIVES_IN') .to(V('c-1'))) .id()

Mencampur upserts dan sisipan

Terkadang Anda mungkin ingin meningkatkan kedua simpul dan tepi yang menghubungkannya. Anda dapat mencampur contoh batch yang disajikan di sini. Contoh berikut meningkatkan 3 simpul dan 2 tepi:

Upserts biasanya melanjutkan satu elemen pada satu waktu. Jika Anda tetap berpegang pada pola upsert yang disajikan di sini, setiap operasi upsert memancarkan satu traverser, yang menyebabkan operasi berikutnya dijalankan hanya sekali.

Namun, terkadang Anda mungkin ingin mencampur upserts dengan sisipan. Ini bisa terjadi, misalnya, jika Anda menggunakan tepi untuk mewakili contoh tindakan atau peristiwa. Permintaan mungkin menggunakan upserts untuk memastikan bahwa semua simpul yang diperlukan ada, dan kemudian menggunakan sisipan untuk menambahkan tepi. Dengan permintaan semacam ini, perhatikan potensi jumlah pelintas yang dipancarkan dari setiap operasi.

Perhatikan contoh berikut, yang mencampur upsert dan sisipan untuk menambahkan tepi yang mewakili peristiwa ke dalam grafik:

// Fully optimized, but inserts too many edges g.V('p-1') .fold() .coalesce(unfold(), addV('Person').property(id, 'p-1') .property('email', 'person-1@example.org')) .V('p-2') .fold() .coalesce(unfold(), addV('Person').property(id, 'p-2') .property('name', 'person-2@example.org')) .V('p-3') .fold() .coalesce(unfold(), addV('Person').property(id, 'p-3') .property('name', 'person-3@example.org')) .V('c-1') .fold() .coalesce(unfold(), addV('City').property(id, 'c-1') .property('name', 'city-1')) .V('p-1', 'p-2') .addE('FOLLOWED') .to(V('p-1')) .V('p-1', 'p-2', 'p-3') .addE('VISITED') .to(V('c-1')) .id()

Kueri harus menyisipkan 5 tepi: 2 FOLLOWED tepi dan 3 VISITED tepi. Namun, kueri sebagai tertulis menyisipkan 8 tepi: 2 FOLLOWED dan 6VISITED. Alasan untuk ini adalah bahwa operasi yang menyisipkan 2 FOLLOWED tepi memancarkan 2 pelintas, menyebabkan operasi penyisipan berikutnya, yang menyisipkan 3 tepi, dieksekusi dua kali.

Perbaikannya adalah menambahkan fold() langkah setelah setiap operasi yang berpotensi memancarkan lebih dari satu traverser:

g.V('p-1') .fold() .coalesce(unfold(), addV('Person').property(id, 'p-1') .property('email', 'person-1@example.org')) .V('p-2') .fold() .coalesce(unfold(), addV('Person').property(id, 'p-2'). .property('name', 'person-2@example.org')) .V('p-3') .fold() .coalesce(unfold(), addV('Person').property(id, 'p-3'). .property('name', 'person-3@example.org')) .V('c-1') .fold(). .coalesce(unfold(), addV('City').property(id, 'c-1'). .property('name', 'city-1')) .V('p-1', 'p-2') .addE('FOLLOWED') .to(V('p-1')) .fold() .V('p-1', 'p-2', 'p-3') .addE('VISITED') .to(V('c-1')). .id()

Di sini kita telah memasukkan fold() langkah setelah operasi yang menyisipkan FOLLOWED tepi. Ini menghasilkan traverser tunggal, yang kemudian menyebabkan operasi berikutnya dieksekusi hanya sekali.

Kelemahan dari pendekatan ini adalah bahwa query sekarang tidak sepenuhnya dioptimalkan, karena tidak fold() dioptimalkan. Operasi insert yang mengikuti sekarang tidak fold() akan dioptimalkan.

Jika Anda perlu menggunakan fold() untuk mengurangi jumlah pelintas atas nama langkah-langkah selanjutnya, cobalah untuk memesan operasi Anda sehingga yang paling murah menempati bagian kueri yang tidak dioptimalkan.

Upserts yang memodifikasi simpul dan tepi yang ada

Terkadang Anda ingin membuat simpul atau tepi jika tidak ada, dan kemudian menambahkan atau memperbarui properti ke sana, terlepas dari apakah itu simpul atau tepi baru atau yang sudah ada.

Untuk menambah atau memodifikasi properti, gunakan property() langkah. Gunakan langkah ini di luar coalesce() langkah. Jika Anda mencoba memodifikasi properti simpul atau tepi yang ada di dalam coalesce() langkah, kueri mungkin tidak dioptimalkan oleh mesin kueri Neptunus.

Kueri berikut menambahkan atau memperbarui properti penghitung pada setiap simpul yang diupsert. Setiap property() langkah memiliki kardinalitas tunggal untuk memastikan bahwa nilai baru menggantikan nilai yang ada, daripada ditambahkan ke serangkaian nilai yang ada.

g.V('v-1') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-1') .property('email', 'person-1@example.org')) .property(single, 'counter', 1) .V('v-2') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-2') .property('email', 'person-2@example.org')) .property(single, 'counter', 2) .V('v-3') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-3') .property('email', 'person-3@example.org')) .property(single, 'counter', 3) .id()

Jika Anda memiliki nilai properti, seperti nilai lastUpdated stempel waktu, yang berlaku untuk semua elemen yang muncul, Anda dapat menambahkan atau memperbaruinya di akhir kueri:

g.V('v-1') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-1') .property('email', 'person-1@example.org')) .V('v-2'). .fold(). .coalesce(unfold(), addV('Person').property(id, 'v-2') .property('email', 'person-2@example.org')) .V('v-3') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-3') .property('email', 'person-3@example.org')) .V('v-1', 'v-2', 'v-3') .property(single, 'lastUpdated', datetime('2020-02-08')) .id()

Jika ada kondisi tambahan yang menentukan apakah simpul atau tepi harus dimodifikasi lebih lanjut atau tidak, Anda dapat menggunakan has() langkah untuk memfilter elemen yang akan diterapkan modifikasi. Contoh berikut menggunakan has() langkah untuk memfilter simpul naik berdasarkan nilai properti mereka. version Kueri kemudian memperbarui ke 3 version dari setiap simpul yang version kurang dari 3:

g.V('v-1') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-1') .property('email', 'person-1@example.org') .property('version', 3)) .V('v-2') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-2') .property('email', 'person-2@example.org') .property('version', 3)) .V('v-3') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-3') .property('email', 'person-3@example.org') .property('version', 3)) .V('v-1', 'v-2', 'v-3') .has('version', lt(3)) .property(single, 'version', 3) .id()