MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Filtra i documenti per distanza archiviati nel documento con $vicino

Presumendo che tu abbia già lavorato per agire sui dati dell'evento mentre li ricevi e li hai in mano (in caso contrario, questa è un'altra domanda, ma guarda cursori di coda ), dovresti avere un oggetto con quei dati per cui interrogare gli utenti.

Questo non è quindi un caso per la valutazione JavaScript con $where , poiché non può accedere ai dati della query restituiti da un $near comunque operazione. Quello che vuoi invece è $geoNear dal quadro di aggregazione. Questo può proiettare la "distanza" trovata dalla query e consentire a una fase successiva di "filtrare" i risultati rispetto al valore memorizzato dall'utente per la distanza massima che desidera percorrere per raggiungere gli eventi pubblicati:

// 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
  }
)

Ora devi fare attenzione con il numero restituito, poiché dal momento che sembra che tu stia memorizzando in "coppie di coordinate legacy" anziché GeoJSON, la distanza restituita da questa operazione sarà in radianti e non una distanza standard. Quindi, supponendo che stai memorizzando in "miglia" o "chilometri" sugli oggetti utente, devi calcolare tramite la formula menzionata nel manuale in "Calcola le distanze usando la geometria sferica" come indicato nel manuale.

Le basi sono che devi dividere per il raggio equatoriale della terra, essendo 3.963,2 miglia o 6.378,1 chilometri da convertire per un confronto con ciò che hai memorizzato.

L'alternativa è invece archiviare in GeoJSON, dove è presente una misurazione coerente in metri.

Assumendo "chilometri" quella riga "se" diventa:

"if": { "$lt": [
    "$eventDistance",
    { "$divide": [ "$maxDistance", 6,378.1 ] }
 ]},

Per confrontare in modo affidabile il valore del tuo chilometro memorizzato con il risultato in radianti recuperato.

L'altra cosa da tenere presente è che $geoNear ha un "limite" predefinito di 100 risultati, quindi è necessario "aumentare" l'argomento "limite" sul numero affinché gli utenti previsti possano eventualmente corrispondere. Potresti anche volerlo fare in "liste di intervallo" di ID utente per un sistema davvero grande, ma puoi raggiungere le dimensioni consentite dalla memoria all'interno di una singola operazione di aggregazione ed eventualmente aggiungere allowDiskUse dove necessario.

Se non si sintonizza quel parametro, verranno restituiti solo i 100 risultati più vicini (impostazione predefinita), che potrebbero non adattarsi nemmeno alla prossima operazione di filtraggio di quelli "vicini" all'evento per iniziare. Usa il buon senso, tuttavia, poiché hai sicuramente una distanza massima per filtrare anche potenziali utenti e anche questo può essere aggiunto alla query.

Come affermato, il punto qui è restituire la distanza per il confronto, quindi la fase successiva è il $redact operazione che può adattare il valore della "distanza di viaggio" dell'utente rispetto alla distanza restituita dall'evento. Il risultato finale fornisce solo agli utenti che si trovano entro la propria distanza un vincolo dall'evento che si qualificheranno per la notifica.

Questa è la logica. Si proietta la distanza dall'utente all'evento e quindi si confronta con il valore memorizzato dall'utente per la distanza che sono disposti a percorrere. Nessun JavaScript e tutti gli operatori nativi che lo rendono abbastanza veloce.

Inoltre, come notato nelle opzioni e nel commento generale, ti suggerisco davvero di utilizzare un indice "2dsphere" per un calcolo accurato della distanza sferica e di convertire l'archiviazione GeoJSON per l'archiviazione delle coordinate nel tuo database Oggetti, poiché sono entrambi standard generali che produrre risultati coerenti.