Saya mencari untuk mendapatkan catatan acak dari yang sangat besar (rekor 100 juta) mongodb
.
Apa cara tercepat dan paling efisien untuk melakukannya? Data sudah ada di sana dan tidak ada bidang di mana saya bisa menghasilkan angka acak dan mendapatkan baris acak.
Ada saran?
mongodb
mongodb-query
Will M
sumber
sumber
Jawaban:
Dimulai dengan rilis 3.2 MongoDB, Anda bisa mendapatkan N dokumen acak dari koleksi menggunakan
$sample
operator pipa agregasi:Jika Anda ingin memilih dokumen acak dari subset koleksi yang difilter, tambahkan
$match
tahapan ke pipeline:Seperti disebutkan dalam komentar, ketika
size
lebih besar dari 1, mungkin ada duplikat dalam sampel dokumen yang dikembalikan.sumber
Lakukan penghitungan semua catatan, hasilkan angka acak antara 0 dan hitungan, lalu lakukan:
sumber
Pembaruan untuk MongoDB 3.2
3,2 memperkenalkan $ sampel ke pipa agregasi.
Ada juga posting blog yang bagus untuk mempraktikkannya.
Untuk versi yang lebih lama (jawaban sebelumnya)
Ini sebenarnya adalah permintaan fitur: http://jira.mongodb.org/browse/SERVER-533 tetapi diajukan di bawah "Tidak akan diperbaiki."
Cookbook memiliki resep yang sangat baik untuk memilih dokumen acak dari koleksi: http://cookbook.mongodb.org/patterns/random-attribute/
Untuk memparafrasekan resep, Anda menetapkan nomor acak ke dokumen Anda:
Kemudian pilih dokumen acak:
Permintaan dengan keduanya
$gte
dan$lte
perlu untuk menemukan dokumen dengan nomor acak terdekatrand
.Dan tentu saja Anda ingin mengindeks pada bidang acak:
Jika Anda sudah menanyakan indeks, cukup jatuhkan, tambahkan
random: 1
, dan tambahkan lagi.sumber
$gte
yang pertama. Solusi alternatif stackoverflow.com/a/9499484/79201 akan bekerja lebih baik dalam kasus ini.Anda juga dapat menggunakan fitur pengindeksan geospasial MongoDB untuk memilih dokumen 'terdekat' ke nomor acak.
Pertama, aktifkan pengindeksan geospasial pada koleksi:
Untuk membuat banyak dokumen dengan titik acak pada sumbu X:
Maka Anda bisa mendapatkan dokumen acak dari koleksi seperti ini:
Atau Anda dapat mengambil beberapa dokumen terdekat dari titik acak:
Ini hanya membutuhkan satu permintaan dan tidak ada pemeriksaan nol, ditambah kodenya bersih, sederhana dan fleksibel. Anda bahkan bisa menggunakan sumbu Y dari geopoint untuk menambahkan dimensi keacakan kedua ke kueri Anda.
sumber
Resep berikut ini sedikit lebih lambat daripada solusi buku masak mongo (tambahkan kunci acak pada setiap dokumen), tetapi mengembalikan dokumen acak yang didistribusikan lebih merata. Ini sedikit kurang merata daripada
skip( random )
solusi, tetapi jauh lebih cepat dan lebih aman jika dokumen dihapus.Ini juga mengharuskan Anda untuk menambahkan bidang "acak" acak ke dokumen Anda jadi jangan lupa untuk menambahkan ini saat Anda membuatnya: Anda mungkin perlu menginisialisasi koleksi Anda seperti yang ditunjukkan oleh Geoffrey
Hasil benchmark
Metode ini jauh lebih cepat daripada
skip()
metode (ceejayoz) dan menghasilkan lebih banyak dokumen acak yang seragam daripada metode "buku masak" yang dilaporkan oleh Michael:Untuk koleksi dengan 1.000.000 elemen:
Metode ini memakan waktu kurang dari satu milidetik pada mesin saya
yang
skip()
metode mengambil 180 ms rata-rataMetode buku masak akan menyebabkan sejumlah besar dokumen tidak dapat dipilih karena jumlah acak mereka tidak disukai.
Metode ini akan memilih semua elemen secara merata dari waktu ke waktu.
Dalam tolok ukur saya, itu hanya 30% lebih lambat dari metode buku resep.
keacakannya tidak 100% sempurna tetapi sangat bagus (dan itu dapat ditingkatkan jika perlu)
Resep ini tidak sempurna - solusi sempurna akan menjadi fitur bawaan seperti yang telah dicatat orang lain.
Namun itu harus menjadi kompromi yang baik untuk banyak tujuan.
sumber
Berikut adalah cara menggunakan nilai default
ObjectId
untuk_id
dan sedikit matematika dan logika.Itulah logika umum dalam representasi shell dan mudah beradaptasi.
Jadi dalam poin:
Temukan nilai kunci utama minimum dan maksimum dalam koleksi
Hasilkan nomor acak yang berada di antara cap waktu dari dokumen-dokumen itu.
Tambahkan angka acak ke nilai minimum dan temukan dokumen pertama yang lebih besar atau sama dengan nilai itu.
Ini menggunakan "padding" dari nilai timestamp di "hex" untuk membentuk
ObjectId
nilai yang valid karena itulah yang kami cari. Menggunakan bilangan bulat sebagai_id
nilai pada dasarnya lebih sederhana tetapi ide dasar yang sama dalam poin.sumber
Dalam Python menggunakan pymongo:
sumber
count()
denganestimated_document_count()
yangcount()
sudah ditinggalkan di Mongdo v4.2.Sekarang Anda bisa menggunakan agregat. Contoh:
Lihat dokumen .
sumber
sulit jika tidak ada data di sana untuk dikunci. apa bidang _id? apakah mereka mongodb objek id? Jika demikian, Anda bisa mendapatkan nilai tertinggi dan terendah:
maka jika Anda menganggap id didistribusikan secara seragam (tetapi tidak, tapi setidaknya ini awal):
sumber
Menggunakan Python (pymongo), fungsi agregat juga berfungsi.
Pendekatan ini jauh lebih cepat daripada menjalankan kueri untuk nomor acak (mis. Collection.find ([random_int]). Ini khususnya kasus untuk koleksi besar.
sumber
Anda dapat memilih cap waktu acak dan mencari objek pertama yang dibuat sesudahnya. Itu hanya akan memindai satu dokumen, meskipun itu tidak selalu memberi Anda distribusi yang seragam.
sumber
Solusi saya di php:
sumber
Untuk mendapatkan jumlah dokumen acak yang ditentukan tanpa duplikat:
loop mendapatkan indeks acak dan lewati duplikat
sumber
Saya akan menyarankan menggunakan peta / mengurangi, di mana Anda menggunakan fungsi peta hanya memancarkan ketika nilai acak di atas probabilitas yang diberikan.
Fungsi pengurangan di atas berfungsi karena hanya satu kunci ('1') yang dipancarkan dari fungsi peta.
Nilai "probabilitas" didefinisikan dalam "lingkup", ketika memohon mapRreduce (...)
Menggunakan mapReduce seperti ini juga bisa digunakan pada db yang di-shard.
Jika Anda ingin memilih dengan tepat dari dokumen dari db, Anda dapat melakukannya seperti ini:
Di mana "countTotal" (m) adalah jumlah dokumen dalam db, dan "countSubset" (n) adalah jumlah dokumen yang akan diambil.
Pendekatan ini mungkin memberikan beberapa masalah pada basis data sharded.
sumber
Anda dapat memilih _id acak dan mengembalikan objek yang sesuai:
Di sini Anda tidak perlu menghabiskan ruang untuk menyimpan nomor acak dalam koleksi.
sumber
Saya sarankan menambahkan bidang int acak ke setiap objek. Maka Anda bisa melakukan a
untuk memilih dokumen acak. Pastikan Anda memastikanIndex ({random_field: 1})
sumber
Ketika saya dihadapkan dengan solusi yang serupa, saya mundur dan menemukan bahwa permintaan bisnis sebenarnya untuk menciptakan beberapa bentuk rotasi inventaris yang disajikan. Dalam hal ini, ada opsi yang jauh lebih baik, yang memiliki jawaban dari mesin pencari seperti Solr, bukan toko data seperti MongoDB.
Singkatnya, dengan persyaratan untuk "memutar secara cerdas" konten, apa yang harus kita lakukan alih-alih nomor acak di semua dokumen adalah memasukkan pengubah skor q pribadi. Untuk menerapkan ini sendiri, dengan asumsi populasi kecil pengguna, Anda dapat menyimpan dokumen per pengguna yang memiliki productId, jumlah tayangan, jumlah klik per tayang, tanggal terakhir terlihat, dan apa pun faktor lain yang ditemukan bisnis yang berarti untuk menghitung skor aq pengubah. Saat mengambil set untuk ditampilkan, biasanya Anda meminta lebih banyak dokumen dari penyimpanan data daripada yang diminta oleh pengguna akhir, kemudian menerapkan pengubah skor q, mengambil jumlah catatan yang diminta oleh pengguna akhir, lalu mengacak halaman hasil, sedikit mengatur, jadi cukup mengurutkan dokumen dalam lapisan aplikasi (dalam memori).
Jika semesta pengguna terlalu besar, Anda dapat mengategorikan pengguna ke dalam kelompok perilaku dan indeks berdasarkan kelompok perilaku daripada pengguna.
Jika semesta produk cukup kecil, Anda dapat membuat indeks per pengguna.
Saya menemukan teknik ini jauh lebih efisien, tetapi yang lebih penting lebih efektif dalam menciptakan pengalaman yang relevan dan bermanfaat dalam menggunakan solusi perangkat lunak.
sumber
tidak ada solusi yang bekerja dengan baik untuk saya. terutama ketika ada banyak celah dan set kecil. ini bekerja sangat baik untuk saya (dalam php):
sumber
find
+skip
sangat buruk, Anda mengembalikan semua dokumen hanya untuk memilih satu: S.Jika Anda menggunakan luwak maka Anda dapat menggunakan luwak-acak luwak-acak
sumber
PHP / MongoDB saya mengurutkan / memesan dengan solusi RANDOM. Semoga ini bisa membantu siapa saja.
Catatan: Saya memiliki ID numerik dalam koleksi MongoDB saya yang merujuk ke catatan database MySQL.
Pertama saya membuat array dengan 10 angka yang dihasilkan secara acak
Dalam agregasi saya, saya menggunakan operator pipa $ addField yang dikombinasikan dengan $ arrayElemAt dan $ mod (modulus). Operator modulus akan memberi saya angka dari 0 - 9 yang kemudian saya gunakan untuk memilih angka dari array dengan angka yang dihasilkan secara acak.
Setelah itu Anda bisa menggunakan semacam Pipeline.
sumber
Jika Anda memiliki kunci id sederhana, Anda bisa menyimpan semua id dalam sebuah array, dan kemudian memilih id acak. (Jawaban Ruby):
sumber
Menggunakan Map / Reduce, Anda tentu bisa mendapatkan catatan acak, hanya saja tidak harus sangat efisien tergantung pada ukuran koleksi yang difilter yang Anda akhirnya bekerja dengan.
Saya telah menguji metode ini dengan 50.000 dokumen (filter menguranginya menjadi sekitar 30.000), dan dijalankan dalam sekitar 400ms pada Intel i3 dengan ram 16GB dan HDD SATA3 ...
Fungsi Peta hanya membuat array id dari semua dokumen yang cocok dengan kueri. Dalam kasus saya, saya menguji ini dengan sekitar 30.000 dari 50.000 dokumen yang mungkin.
Fungsi Reduce hanya mengambil integer acak antara 0 dan jumlah item (-1) dalam array, dan kemudian mengembalikan _id itu dari array.
400ms kedengarannya seperti waktu yang lama, dan memang benar, jika Anda memiliki lima puluh juta rekaman, bukan lima puluh ribu, ini dapat meningkatkan overhead ke titik di mana ia menjadi tidak dapat digunakan dalam situasi multi-pengguna.
Ada masalah terbuka untuk MongoDB untuk memasukkan fitur ini dalam inti ... https://jira.mongodb.org/browse/SERVER-533
Jika pilihan "acak" ini dibangun ke dalam pencarian indeks alih-alih mengumpulkan id ke dalam array dan kemudian memilihnya, ini akan sangat membantu. (pilih itu!)
sumber
Ini berfungsi dengan baik, cepat, bekerja dengan banyak dokumen dan tidak memerlukan
rand
bidang isian, yang pada akhirnya akan mengisi sendiri:ps. Cara menemukan catatan acak dalam pertanyaan mongodb ditandai sebagai duplikat dari pertanyaan ini. Perbedaannya adalah bahwa pertanyaan ini menanyakan secara eksplisit tentang catatan tunggal seperti yang lain secara eksplisit tentang mendapatkan acak dokumen s .
sumber
Jika Anda menggunakan mongoid, pembungkus dokumen-ke-objek, Anda dapat melakukan hal berikut di Ruby. (Dengan asumsi model Anda adalah Pengguna)
Di .irbrc saya, saya punya
jadi di konsol rel, saya bisa lakukan, misalnya,
untuk mendapatkan dokumen secara acak dari koleksi apa pun.
sumber
Anda juga dapat menggunakan shuffle-array setelah mengeksekusi kueri Anda
var shuffle = membutuhkan ('shuffle-array');
Accounts.find (qry, function (err, results_array) {newIndexArr = shuffle (results_array);
sumber
Apa yang bekerja secara efisien dan andal adalah ini:
Tambahkan bidang yang disebut "acak" untuk setiap dokumen dan berikan nilai acak padanya, tambahkan indeks untuk bidang acak dan lanjutkan sebagai berikut:
Anggaplah kita memiliki koleksi tautan web yang disebut "tautan" dan kami ingin tautan acak darinya:
Untuk memastikan tautan yang sama tidak akan muncul lagi, perbarui bidang acaknya dengan nomor acak baru:
sumber