Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.
Membuat upserts Gremlin yang efisien dengan fold () /coalesce () /()
Upsert (atau penyisipan kondisional) menggunakan kembali simpul atau tepi jika sudah ada, atau membuatnya jika tidak ada. Upserts yang efisien dapat membuat perbedaan yang signifikan dalam kinerja kueri Gremlin.
Halaman ini menunjukkan bagaimana menggunakan pola fold()/coalesce()/unfold() Gremlin untuk membuat peningkatan yang efisien. Namun, dengan rilis TinkerPop versi 3.6.x yang diperkenalkan di Neptunus di mesin versi 1.2.1.0, langkah baru mergeV() dan mergeE() langkah lebih disukai dalam banyak kasus. P fold()/coalesce()/unfold() ola yang dijelaskan di sini mungkin masih berguna dalam beberapa situasi yang kompleks, tetapi digunakan secara umum mergeV() dan mergeE() jika Anda bisa, seperti yang dijelaskan dalamMembuat upserts yang efisien dengan langkah-langkah Gremlin mergeV () dan merGEE () .
Upserts memungkinkan Anda menulis operasi penyisipan idempotent: 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 diputar kembali dengan aConcurrentModificationException, sehingga memerlukan percobaan ulang.
Misalnya, kueri berikut meningkatkan simpul dengan terlebih dahulu mencari simpul yang ditentukan dalam dataset, dan kemudian melipat hasilnya menjadi daftar. Pada traversal pertama yang diberikan ke coalesce() langkah, kueri kemudian membuka daftar ini. Jika daftar yang tidak dilipat tidak kosong, hasilnya dipancarkan daricoalesce(). Namun, jika unfold() mengembalikan koleksi kosong karena simpul saat ini tidak ada, coalesce() melanjutkan untuk mengevaluasi traversal kedua yang telah disediakan, dan dalam traversal kedua ini kueri menciptakan simpul yang hilang.
g.V('v-1').fold() .coalesce( unfold(), addV('Person').property(id, 'v-1') .property('email', 'person-1@example.org') )
Gunakan bentuk coalesce () yang dioptimalkan untuk upserts
Neptunus dapat mengoptimalkan fold().coalesce(unfold(), ...) idiom untuk membuat pembaruan throughput tinggi, tetapi pengoptimalan ini hanya berfungsi jika kedua bagian coalesce() mengembalikan simpul atau tepi 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 berfungsi 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: >>. 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 menempatkan langkah-langkah yang tidak dapat dioptimalkan di akhir kueri, sehingga memungkinkan mesin untuk mengoptimalkan langkah sebanyak mungkin. Teknik ini digunakan dalam beberapa contoh upsert batch, di mana semua upserts yang dioptimalkan untuk satu set simpul atau tepi dilakukan sebelum modifikasi tambahan yang berpotensi tidak dioptimalkan diterapkan pada simpul atau tepi yang sama.
Peningkatan batch untuk meningkatkan throughput
Untuk skenario penulisan throughput tinggi, Anda dapat merangkai langkah-langkah upsert bersama-sama untuk meningkatkan simpul dan tepi dalam batch. Batching mengurangi overhead transaksional dari peningkatan sejumlah besar simpul dan tepi. Anda kemudian dapat meningkatkan throughput lebih lanjut dengan meningkatkan permintaan batch secara paralel menggunakan beberapa klien.
Sebagai aturan praktis, kami merekomendasikan peningkatan sekitar 200 catatan per permintaan batch. Catatan adalah label atau properti simpul atau tepi tunggal. Sebuah simpul dengan label tunggal dan 4 properti, misalnya, membuat 5 catatan. Tepi dengan label dan properti tunggal membuat 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 memastikan bahwa permintaan Anda berisi 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 (lihatMembuat Penulisan Gremlin Multithreaded yang Efisien). Jumlah klien harus sama dengan jumlah utas pekerja pada instance penulis Neptune Anda, yang biasanya 2 x jumlah vCPU di server. Misalnya, sebuah r5.8xlarge instance memiliki 32 vCPU dan 64 utas pekerja. Untuk skenario penulisan throughput tinggi menggunakan ar5.8xlarge, Anda akan menggunakan 64 klien yang menulis peningkatan batch 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 dilengkapi dengan aliran permintaan yang stabil yang menempati semua utas pekerja tanpa membanjiri antrian permintaan sisi server (lihatMengukur instans DB dalam sebuah klaster Neptune DB).
Cobalah untuk menghindari langkah-langkah yang menghasilkan banyak traverser
Ketika langkah Gremlin dieksekusi, dibutuhkan traverser yang masuk, dan memancarkan satu atau lebih penjelajah keluaran. Jumlah pelintas yang dipancarkan oleh satu langkah menentukan berapa kali langkah berikutnya dijalankan.
Biasanya, saat melakukan operasi batch, Anda ingin setiap operasi, seperti upsert vertex A, dijalankan sekali, sehingga urutan operasi terlihat seperti ini: upsert vertex A, lalu upsert vertex B, lalu upsert vertex C, dan seterusnya. Selama 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, operasi membuat atau memodifikasi lebih dari satu elemen, ia memancarkan beberapa traverser, yang pada gilirannya menyebabkan langkah-langkah berikutnya dijalankan 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, tepi atau nilai properti yang tidak diinginkan.
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 bagian atas dan sisipan cara menangani operasi yang dapat memancarkan beberapa penjelajah.
Menaikkan simpul
Anda dapat menggunakan ID simpul untuk menentukan apakah simpul yang sesuai ada. Ini adalah pendekatan yang disukai, karena Neptunus mengoptimalkan peningkatan untuk kasus penggunaan yang sangat bersamaan di sekitar ID. Sebagai contoh, kueri berikut membuat simpul dengan ID simpul tertentu jika belum ada, atau menggunakannya kembali jika ada:
g.V('v-1') .fold() .coalesce(unfold(), addV('Person').property(id, 'v-1') .property('email', 'person-1@example.org')) .id()
Perhatikan bahwa kueri ini berakhir 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 vertex untuk menentukan apakah simpul 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 ID Anda sendiri yang disediakan pengguna untuk membuat simpul, dan gunakan ID ini untuk menentukan apakah simpul ada selama operasi upsert. Ini memungkinkan Neptunus mengoptimalkan peningkatan di sekitar ID. ID-based Upsert dapat secara signifikan lebih efisien daripada upsert berbasis properti dalam skenario modifikasi yang sangat bersamaan.
Rantai-rantai puncak simpul
Anda dapat merangkai puncak simpul bersama-sama untuk menyisipkannya dalam satu 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 menaikkan
Anda dapat menggunakan ID tepi untuk memperbaiki tepi dengan cara yang sama seperti Anda menaikkan simpul menggunakan ID simpul khusus. 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 ID to simpul from dan jika perlu membuat tepi baru.
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 ID simpul khusus, tetapi meninggalkan Neptunus untuk menghasilkan ID tepi. Jika Anda tidak tahu ID tepi, tetapi Anda tahu ID simpul from danto, 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 seharusnya inV() (atau outV() jika Anda sudah inE() terbiasa menemukan tepi), bukanotherV(). 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 mengetahui ID tepi atau simpul di depan, Anda dapat meningkatkan menggunakan properti vertex:
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 puncak simpul, lebih baik menggunakan tepi upserts menggunakan ID ID-based tepi atau from dan ID simpul, daripada upserts berbasis to properti, sehingga Neptunus dapat sepenuhnya mengoptimalkan upsert.
Memeriksa keberadaan dari dan ke simpul
Perhatikan konstruksi langkah-langkah yang membuat tepi baru:addE().from().to(). Konstruksi ini memastikan bahwa kueri memeriksa keberadaan kedua from dan simp to ul. 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 ada kemungkinan bahwa salah satu simpul from atau simp to ul tidak ada, Anda harus mencoba untuk meningkatkannya sebelum menaikkan tepi di antara keduanya. Lihat Menggabungkan puncak simpul dan tepi.
Ada konstruksi alternatif untuk membuat keunggulan yang tidak boleh Anda gunakan:V().addE().to(). Itu hanya menambahkan tepi jika simp from ul ada. Jika simp to ul tidak ada, query menghasilkan kesalahan, seperti yang dijelaskan sebelumnya, tetapi jika simpul from tidak ada, diam-diam gagal memasukkan tepi, tanpa menghasilkan kesalahan apa pun. Misalnya, upsert berikut selesai tanpa menaikkan tepi jika simpul tidak from 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()
Mengrantai bagian atas tepi
Jika Anda ingin merangkai upserts tepi bersama-sama untuk membuat permintaan batch, Anda harus memulai setiap upsert dengan pencarian simpul, bahkan jika Anda sudah mengetahui ID tepi.
Jika Anda sudah mengetahui ID tepi yang ingin Anda tingkatkan, dan ID to simpul from dan, Anda dapat menggunakan formulasi ini:
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 upsert tepi batch yang paling umum adalah Anda mengetahui ID to simpul from dan, tetapi tidak tahu ID tepi 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 ID tepi yang ingin Anda tingkatkan, tetapi tidak tahu ID to simpul from dan (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 puncak simpul dan tepi
Terkadang Anda mungkin ingin memperbaiki kedua simpul dan tepi yang menghubungkannya. Anda dapat mencampur contoh batch yang disajikan di sini. Contoh berikut menaikkan 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 bagian atas dan sisipan
Terkadang Anda mungkin ingin memperbaiki kedua simpul dan tepi yang menghubungkannya. Anda dapat mencampur contoh batch yang disajikan di sini. Contoh berikut menaikkan 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 traverser tunggal, 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 jumlah potensial pelintas yang dipancarkan dari setiap operasi.
Pertimbangkan contoh berikut, yang menggabungkan upserts 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 memasukkan 5 tepi: 2 tepi YANG DIIKUTI dan 3 tepi YANG DIKUNJUNGI. Namun, kueri seperti tertulis menyisipkan 8 tepi: 2 DIIKUTI dan 6 DIKUNJUNGI. Alasan untuk ini adalah bahwa operasi yang memasukkan 2 tepi YANG DIIKUTI memancarkan 2 travers, 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 tepi YANG DIIKUTI. Ini menghasilkan traverser tunggal, yang kemudian menyebabkan operasi berikutnya dijalankan hanya sekali.
Kelemahan dari pendekatan ini adalah bahwa kueri sekarang tidak sepenuhnya dioptimalkan, karena tidak fold() dioptimalkan. Operasi sisipan yang berikut sekarang tidak fold() akan dioptimalkan.
Jika Anda perlu menggunakan fold() untuk mengurangi jumlah penjelajah 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 untuk itu, terlepas dari apakah itu simpul atau tepi baru atau yang sudah ada.
Untuk menambah atau memodifikasi properti, gunakan langka property() hnya. 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 Neptune.
Kueri berikut menambah atau memperbarui properti counter pada setiap simpul yang terangkat. Setiap property() langkah memiliki kardinalitas tunggal untuk memastikan bahwa nilai-nilai baru menggantikan nilai yang ada, daripada ditambahkan ke satu set 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 stempel lastUpdated waktu, yang berlaku untuk semua elemen yang ditingkatkan, 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, Anda dapat menggunakan has() langkah untuk menyaring elemen yang akan diterapkan modifikasi. Contoh berikut menggunakan has() langkah untuk menyaring simpul yang diangkat berdasarkan nilai properti mereka. version Kueri kemudian diperbarui 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()