Agregasi (aggregation) menggabungkan banyak baris hasil kueri menjadi satu nilai ringkas, seperti jumlah, rata-rata, atau deretan nilai. Topik ini membahas fungsi agregat bawaan Cypher, pengelompokan otomatis berdasarkan kunci non-agregat, fungsi collect untuk membentuk list, yaitu deretan nilai, serta klausa WITH yang bekerja sebagai pipeline antarbagian kueri. Setelah topik ini dibaca, kueri bertingkat dengan pola MATCH, WITH agregasi, WHERE, dan RETURN dapat disusun serta dijalankan pada dataset jaringan sosial modul ini.
Dataset jaringan sosial#
Seluruh contoh pada modul ini menggunakan satu dataset kecil yang sama, yaitu jaringan sosial berisi tujuh node berlabel :Person dengan properti name dan kota, ditambah dua node :Hobi. Relasi :KENAL membentuk rantai Eko, Fajar, Gita, Hana, ditambah cabang Eko, Iwan, Hana, serta dua cabang pendek menuju Jihan dan Rara. Hanya Hana dan Fajar yang dihubungkan ke hobi melalui relasi :PUNYA_HOBI. Properti kota, relasi :KENAL, dan sebaran hobi yang tidak merata menyediakan bahan pengelompokan, penghitungan, dan pengumpulan nilai pada topik ini.
Skrip setup.cypher berikut membangun seluruh dataset dengan MERGE, sehingga aman dijalankan berulang tanpa menduplikasi data. Cara termudah memuat skrip tersebut adalah mengalirkannya ke cypher-shell melalui pipa, atau mengeksekusinya dari Neo4j Browser.
// Dataset modul Pola kueri: jaringan sosial dengan relasi KENAL dan hobi.
// Skrip ini idempoten, sehingga aman dijalankan lebih dari satu kali.
// Tujuh orang beserta kota asalnya
MERGE (p:Person {name: 'Eko'}) SET p.kota = 'Bandung';
MERGE (p:Person {name: 'Fajar'}) SET p.kota = 'Bandung';
MERGE (p:Person {name: 'Gita'}) SET p.kota = 'Semarang';
MERGE (p:Person {name: 'Hana'}) SET p.kota = 'Surabaya';
MERGE (p:Person {name: 'Iwan'}) SET p.kota = 'Semarang';
MERGE (p:Person {name: 'Jihan'}) SET p.kota = 'Bandung';
MERGE (p:Person {name: 'Rara'}) SET p.kota = 'Yogyakarta';
// Dua hobi
MERGE (h:Hobi {name: 'Fotografi'});
MERGE (h:Hobi {name: 'Bersepeda'});
// Rantai KENAL, kedua ujung diikat variabel agar MERGE hanya menangani relasinya
MATCH (a:Person {name: 'Eko'}), (b:Person {name: 'Fajar'}) MERGE (a)-[:KENAL]->(b);
MATCH (a:Person {name: 'Fajar'}), (b:Person {name: 'Gita'}) MERGE (a)-[:KENAL]->(b);
MATCH (a:Person {name: 'Gita'}), (b:Person {name: 'Hana'}) MERGE (a)-[:KENAL]->(b);
MATCH (a:Person {name: 'Eko'}), (b:Person {name: 'Iwan'}) MERGE (a)-[:KENAL]->(b);
MATCH (a:Person {name: 'Iwan'}), (b:Person {name: 'Hana'}) MERGE (a)-[:KENAL]->(b);
MATCH (a:Person {name: 'Gita'}), (b:Person {name: 'Jihan'}) MERGE (a)-[:KENAL]->(b);
MATCH (a:Person {name: 'Fajar'}), (b:Person {name: 'Rara'}) MERGE (a)-[:KENAL]->(b);
// Hobi hanya dimiliki sebagian orang, sehingga null muncul secara alami
MATCH (a:Person {name: 'Hana'}), (b:Hobi {name: 'Fotografi'}) MERGE (a)-[:PUNYA_HOBI]->(b);
MATCH (a:Person {name: 'Fajar'}), (b:Hobi {name: 'Bersepeda'}) MERGE (a)-[:PUNYA_HOBI]->(b);# tidak-dijalankan
# memuat dataset melalui cypher-shell, kata sandi diminta interaktif
cypher-shell -a bolt://localhost:7687 -u neo4j -f setup.cypherFungsi agregat#
Fungsi agregat menerima satu ekspresi dari setiap baris hasil, lalu mengembalikan satu nilai untuk sekumpulan baris. Fungsi count() menghitung banyaknya baris atau nilai yang tidak null, sedangkan bentuk count(DISTINCT x) hanya menghitung nilai yang saling berbeda. Kueri berikut menghitung node :Person dan banyaknya kota unik yang tersimpan pada properti kota.
MATCH (p:Person)
RETURN count(p) AS jumlah_orang, count(DISTINCT p.kota) AS jumlah_kota;jumlah_orang, jumlah_kota
7, 4Keluarannya satu baris karena tidak ada ekspresi non-agregat pada RETURN, sehingga seluruh baris diperlakukan sebagai satu kelompok. Fungsi lainnya dirangkum pada tabel berikut.
| Fungsi | Nilai yang dihasilkan |
|---|---|
count(x) | banyaknya baris atau nilai yang tidak null |
sum(x) | penjumlahan seluruh nilai numerik |
avg(x) | rata-rata nilai numerik, berupa bilangan pecahan |
min(x) | nilai terkecil pada kelompoknya |
max(x) | nilai terbesar pada kelompoknya |
collect(x) | list berisi nilai x dari setiap baris |
Fungsi sum(), avg(), min(), dan max() membutuhkan kolom numerik, dan dataset ini tidak menyimpan angka sebagai properti. Penerapan keempatnya ditunjukkan pada bagian tentang WITH, karena angka yang dihitung dari pola baru tersedia sebagai kolom setelah agregasi di tengah kueri.
Pengelompokan otomatis#
Cypher mengelompokkan baris secara otomatis tanpa menuntut klausa pengelompokan. Setiap ekspresi pada RETURN atau WITH yang bukan fungsi agregat menjadi kunci pengelompokan (grouping key), yaitu acuan untuk menentukan baris mana yang digabungkan. Baris-baris dengan nilai kunci yang sama membentuk satu kelompok, dan setiap kelompok menghasilkan satu baris keluaran. Kueri berikut menghitung banyaknya orang pada setiap kota.
MATCH (p:Person)
RETURN p.kota AS kota, count(p) AS jumlah
ORDER BY kota;kota, jumlah
"Bandung", 3
"Semarang", 2
"Surabaya", 1
"Yogyakarta", 1Properti p.kota menjadi kunci pengelompokannya, sedangkan count(p) dihitung terpisah untuk setiap kelompok, sehingga Bandung yang dihuni Eko, Fajar, dan Jihan menghasilkan angka tiga. Perbandingan dengan kueri pada bagian sebelumnya memperlihatkan peran kunci tersebut, karena kueri tanpa ekspresi non-agregat menghasilkan satu baris, sedangkan penambahan p.kota memecah keluaran menjadi empat baris sesuai banyaknya kelompok. Apabila kendali eksplisit diinginkan, klausa GROUP BY yang diperkenalkan pada Neo4j 2026.07 dapat menuliskan kunci pengelompokan tersebut, sedangkan pengelompokan implisit tetap berlaku apabila klausa itu tidak ada.
Mengumpulkan nilai dengan collect#
Fungsi collect(x) mengumpulkan nilai x dari seluruh baris dalam satu kelompok menjadi satu list, sehingga menjadi penghubung antara baris hasil dengan list yang dapat diolah lebih lanjut. Argumennya dapat berupa properti maupun ekspresi apa pun yang dievaluasi untuk setiap baris dalam kelompoknya. Kedudukan inilah yang menjadikan collect kunci dari pola berpikir pipeline pada topik ini. Kueri berikut mengumpulkan nama-nama anggota setiap kota.
MATCH (p:Person)
WITH p ORDER BY p.name
RETURN p.kota AS kota, collect(p.name) AS anggota
ORDER BY kota;kota, anggota
"Bandung", ["Eko", "Fajar", "Jihan"]
"Semarang", ["Gita", "Iwan"]
"Surabaya", ["Hana"]
"Yogyakarta", ["Rara"]Klausa WITH p ORDER BY p.name mengurutkan baris sebelum agregasi dilakukan, karena ORDER BY tidak dapat ditulis di dalam fungsi agregat seperti collect(). Fungsi collect menyusun list sesuai urutan baris yang diterimanya, sedangkan urutan baris hasil pencocokan itu sendiri tidak dijamin tanpa pengurutan, sehingga klausa ini diperlukan apabila isi list harus terulang sama pada setiap eksekusi. Keluaran memperlihatkan anggota list Bandung terurut menurut nama, yaitu Eko, Fajar, dan Jihan.
WITH sebagai pipeline#
Klausa WITH berperan seperti RETURN, tetapi hasilnya tidak mengakhiri kueri, melainkan menjadi masukan bagi bagian kueri berikutnya. Pada titik ini, agregasi dapat dilakukan di tengah kueri, variabel yang dibawa ke tahap berikutnya dapat dibatasi, dan hasil agregasi dapat disaring dengan WHERE, sesuatu yang tidak mungkin dilakukan pada WHERE sebelum agregasi karena fungsinya belum memiliki baris untuk dihitung. Kueri berikut menghitung banyaknya orang yang dikenal setiap orang, lalu mempertahankan baris yang jumlahnya dua atau lebih.
MATCH (p:Person)
OPTIONAL MATCH (p)-[:KENAL]->(lain:Person)
WITH p, count(lain) AS jumlah_kenalan
WHERE jumlah_kenalan >= 2
RETURN p.name AS nama, jumlah_kenalan
ORDER BY nama;nama, jumlah_kenalan
"Eko", 2
"Fajar", 2
"Gita", 2Agregasi pada WITH mengelompokkan baris dengan aturan yang sama seperti pada RETURN, yaitu variabel non-agregat yang dicantumkan menjadi kuncinya, dan hanya p serta jumlah_kenalan yang tersisa pada tahap berikutnya. Pola OPTIONAL MATCH, yang dibahas pada topik tersendiri dalam modul ini, menjaga orang tanpa relasi :KENAL tetap menghasilkan baris, sedangkan count(lain) mengabaikan null sehingga jumlahnya menjadi nol. Klausa WHERE setelah WITH kemudian menyaring baris hasil agregasi, bukan baris pola.
Pola berpikir pipeline#
Kueri yang memanfaatkan WITH tersusun sebagai pipeline, yaitu rangkaian tahap yang menerima baris dari tahap sebelumnya, mengubah baris tersebut, lalu meneruskan hasilnya. Pola yang paling sering muncul adalah MATCH untuk menjaring baris pola, WITH untuk agregasi, WHERE untuk menyaring hasil agregasinya, kemudian MATCH atau RETURN. Kueri berikut menambahkan satu tahap MATCH setelah penyaringan, yaitu mencari orang yang dikenalnya berjumlah dua atau lebih dan memiliki hobi.
MATCH (p:Person)-[:KENAL]->(lain:Person)
WITH p, count(lain) AS jumlah_kenalan
WHERE jumlah_kenalan >= 2
MATCH (p)-[:PUNYA_HOBI]->(h:Hobi)
RETURN p.name AS nama, jumlah_kenalan, h.name AS hobi
ORDER BY nama;nama, jumlah_kenalan, hobi
"Fajar", 2, "Bersepeda"Tahap WITH menyisakan Eko, Fajar, dan Gita, sedangkan MATCH kedua menghilangkan Eko dan Gita karena keduanya tidak memiliki relasi :PUNYA_HOBI, sehingga hanya Fajar yang lolos. Variabel p yang dibawa WITH menghubungkan kedua tahap, dan kolom jumlah_kenalan yang tersisa dapat langsung diagregasi lagi oleh fungsi agregat lain.
MATCH (p:Person)
OPTIONAL MATCH (p)-[:KENAL]->(lain:Person)
WITH p, count(lain) AS jumlah_kenalan
RETURN min(jumlah_kenalan) AS minimal, max(jumlah_kenalan) AS maksimal, sum(jumlah_kenalan) AS total, round(avg(jumlah_kenalan), 2) AS rata_rata;minimal, maksimal, total, rata_rata
0, 2, 7, 1.0Fungsi avg() menghasilkan bilangan pecahan, dan round() membulatkannya agar keluarannya ringkas. Hasil ini memperlihatkan sifat pipeline yang berlapis, yaitu agregasi menghasilkan kolom baru yang dapat diagregasi lagi pada tahap berikutnya. Pipeline juga menjadi tempat yang wajar bagi collect, sebagaimana kueri berikut yang mengumpulkan hobi seluruh kenalan setiap orang.
MATCH (p:Person)-[:KENAL]->(lain:Person)-[:PUNYA_HOBI]->(h:Hobi)
WITH p, collect(DISTINCT h.name) AS hobi_kenalan
RETURN p.name AS nama, hobi_kenalan
ORDER BY nama;nama, hobi_kenalan
"Eko", ["Bersepeda"]
"Gita", ["Fotografi"]
"Iwan", ["Fotografi"]Fajar tidak muncul karena MATCH menuntut rantai pola terpenuhi utuh, sedangkan kenalannya tidak memiliki hobi, sehingga kasus semacam itu ditangani OPTIONAL MATCH. Bentuk collect(DISTINCT ...) membuang nilai ganda apabila dua baris membawa nilai yang sama. Setelah terkumpul sebagai list, hasilnya siap diolah lebih lanjut pada tahap berikutnya.
Kesalahan umum#
Kesalahan pertama adalah menempatkan fungsi agregat langsung di dalam WHERE yang mengikuti MATCH, misalnya WHERE count(lain) >= 2. Eksekusinya berhenti dengan error invalid use of aggregate function, karena fungsi agregat menghitung atas banyak baris, sedangkan WHERE pada posisi itu dievaluasi baris demi baris. Perbaikannya adalah memindahkan agregasi ke WITH, lalu menyaring nilai agregat pada WHERE setelahnya.
Kesalahan kedua adalah menyaring pada posisi yang keliru. WHERE sebelum WITH menyaring baris pola, sedangkan WHERE sesudah WITH menyaring kelompok hasil agregasinya. Memindahkan kondisi antarposisi tanpa memeriksa maknanya mengubah angka yang dihitung, karena baris yang dikeluarkan sebelum agregasi tidak pernah ikut terhitung.
Kesalahan ketiga adalah menganggap collect otomatis membuang nilai ganda. Dua baris yang membawa nilai sama menghasilkan dua anggota list, misalnya ketika dua kenalan berbeda mempunyai hobi yang sama. Perbaikannya adalah menuliskan collect(DISTINCT x) apabila nilai unik yang dikehendaki, sebagaimana digunakan pada kueri hobi kenalan di bagian sebelumnya.
Ringkasan#
- Fungsi agregat menggabungkan banyak baris menjadi satu nilai, dan setiap ekspresi non-agregat pada
RETURNatauWITHmenjadi kunci pengelompokan secara otomatis. collectmengumpulkan nilai dari banyak baris menjadi satu list per kelompok, sedangkancollect(DISTINCT x)membuang nilai gandanya.WITHmeneruskan hasil ke bagian kueri berikutnya, melakukan agregasi di tengah kueri, danWHEREsesudahWITHmenyaring hasil agregasi tersebut.- Pola
MATCH,WITHagregasi,WHERE, laluMATCHatauRETURNmenyusun kueri bertingkat sebagai pipeline yang setiap tahapnya mengubah baris.
Referensi#
- Aggregating functions — Neo4j Docs
— Daftar fungsi agregat Cypher beserta semantik
count,sum,avg,min,max, dancollect. - WITH — Neo4j Docs
— Referensi klausa
WITH, termasuk pengendalian lingkup variabel, agregasi, dan penyaringan hasil. - GROUP BY — Neo4j Docs — Subklausa pengendali kunci pengelompokan beserta penjelasan pengelompokan implisit apabila klausa itu tidak ada.