Anggaplah Anda telah bekerja untuk bertindak berdasarkan data acara saat Anda menerimanya dan memilikinya di tangan ( jika belum, maka itu adalah pertanyaan lain, tetapi lihat kursor tailable ), maka Anda harus memiliki objek dengan data tersebut untuk kueri pengguna.
Oleh karena itu, ini bukan kasus untuk evaluasi JavaScript dengan $where
, karena tidak dapat mengakses data kueri yang dikembalikan dari $near
operasi pula. Yang Anda inginkan adalah $geoNear
dari kerangka agregasi. Ini dapat memproyeksikan "jarak" yang ditemukan dari kueri, dan memungkinkan tahap selanjutnya untuk "memfilter" hasil terhadap nilai yang disimpan pengguna untuk jarak maksimum yang ingin mereka tempuh ke acara yang dipublikasikan:
// Represent retrieved event data
var eventData = {
eventLocation: {
latlong: [long,lat]
}
};
// Find users near that event within their stored distance
User.aggregate(
[
{ "$geoNear": {
"near": {
"type": "Point",
"coordinates": eventData.eventLocation.latlong
},
"distanceField": "eventDistance",
"limit": 100000,
"spherical": true
}},
{ "$redact": {
"$cond": {
"if": { "$lt": [ "$eventDistance", "$maxDistance" ] },
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
]
function(err,results) {
// Work with results in here
}
)
Sekarang Anda perlu berhati-hati dengan nomor yang dikembalikan, karena karena Anda tampaknya menyimpan dalam "pasangan koordinat lama" alih-alih GeoJSON, maka jarak yang dikembalikan dari operasi ini akan dalam radian dan bukan jarak standar. Jadi anggaplah Anda menyimpan dalam "mil" atau "kilometer" pada objek pengguna, maka Anda perlu menghitung melalui rumus yang disebutkan dalam manual di bawah "Menghitung Jarak Menggunakan Geometri Bulat" seperti yang disebutkan dalam manual.
Dasar-dasarnya adalah Anda perlu membaginya dengan jari-jari ekuator bumi, menjadi 3.963,2 mil atau 6.378,1 kilometer untuk dikonversi sebagai perbandingan dengan apa yang telah Anda simpan.
Alternatifnya adalah menyimpan di GeoJSON, di mana ada pengukuran yang konsisten dalam meter.
Dengan asumsi "kilometer" bahwa garis "jika" menjadi:
"if": { "$lt": [
"$eventDistance",
{ "$divide": [ "$maxDistance", 6,378.1 ] }
]},
Untuk membandingkan secara andal nilai kilometer tersimpan Anda dengan hasil radian yang ditampilkan.
Hal lain yang perlu diperhatikan adalah $geoNear
memiliki "batas" default 100 hasil, jadi Anda perlu "memompa" argumen "batas" di sana ke nomor yang mungkin cocok dengan pengguna yang diharapkan. Anda bahkan mungkin ingin melakukan ini di "daftar rentang" id pengguna untuk sistem yang sangat besar, tetapi Anda dapat menggunakan memori sebesar yang diizinkan dalam satu operasi agregasi dan mungkin menambahkan allowDiskUse
bila diperlukan.
Jika Anda tidak menyetel parameter itu, maka hanya 100 hasil terdekat ( default ) yang akan dikembalikan, yang mungkin bahkan tidak sesuai dengan operasi penyaringan Anda berikutnya yang "dekat" dengan acara untuk memulai. Gunakan akal sehat, karena Anda pasti memiliki jarak maksimal untuk menyaring calon pengguna, dan itu juga dapat ditambahkan ke kueri.
Seperti yang dinyatakan, intinya di sini adalah mengembalikan jarak untuk perbandingan, jadi tahap selanjutnya adalah $redact
operasi yang dapat menyesuaikan nilai "jarak perjalanan" pengguna dengan jarak yang dikembalikan dari acara. Hasil akhirnya hanya memberikan pengguna yang berada dalam batasan jarak mereka sendiri dari acara yang akan memenuhi syarat untuk pemberitahuan.
Itulah logikanya. Anda memproyeksikan jarak dari pengguna ke acara dan kemudian membandingkan dengan nilai yang disimpan pengguna untuk jarak yang siap mereka tempuh. Tidak ada JavaScript, dan semua operator asli yang membuatnya cukup cepat.
Juga seperti yang disebutkan dalam opsi dan komentar umum, saya sangat menyarankan Anda menggunakan indeks "2dsphere" untuk perhitungan jarak bola yang akurat serta mengonversi ke penyimpanan GeoJSON untuk penyimpanan koordinat Anda di objek database Anda, karena keduanya adalah standar umum yang menghasilkan hasil yang konsisten.