MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Dokumente nach Entfernung filtern Gespeichert im Dokument mit $near

Angenommen, Sie haben bereits ausgearbeitet, auf die Ereignisdaten zu reagieren, sobald Sie sie erhalten, und sie in der Hand zu haben (wenn Sie dies nicht getan haben, dann ist das eine andere Frage, aber schauen Sie sich anpassbare Cursor ), dann sollten Sie ein Objekt mit diesen Daten haben, mit dem Sie die Benutzer abfragen können.

Dies ist daher kein Fall für die JavaScript-Auswertung mit $where , da es nicht auf die Abfragedaten zugreifen kann, die von $near Betrieb sowieso. Was Sie stattdessen wollen, ist $geoNear aus dem Aggregationsframework. Dies kann die aus der Abfrage gefundene "Entfernung" projizieren und es einem späteren Schritt ermöglichen, die Ergebnisse gegen den vom Benutzer gespeicherten Wert für die maximale Entfernung zu "filtern", die er zu veröffentlichten Ereignissen zurücklegen möchte:

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

Jetzt müssen Sie mit der zurückgegebenen Zahl vorsichtig sein, da die von dieser Operation zurückgegebene Entfernung in Radiant und nicht in Standardentfernung angegeben wird, da Sie anscheinend in "Legacy-Koordinatenpaaren" anstelle von GeoJSON speichern. Angenommen, Sie speichern in "Meilen" oder "Kilometern" auf den Benutzerobjekten, dann müssen Sie über die im Handbuch unter "Berechnung von Entfernungen unter Verwendung von Kugelgeometrie" wie im Handbuch erwähnt.

Die Grundlagen sind, dass Sie durch den Äquatorradius der Erde dividieren müssen, der entweder 3.963,2 Meilen oder 6.378,1 Kilometer beträgt, um ihn für einen Vergleich mit dem, was Sie gespeichert haben, umzurechnen.

Die Alternative besteht darin, stattdessen in GeoJSON zu speichern, wo es eine konsistente Messung in Metern gibt.

Unter der Annahme von "Kilometern" wird diese "if"-Zeile zu:

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

Um Ihren gespeicherten Kilometerwert zuverlässig mit dem zurückgegebenen Radiant-Ergebnis zu vergleichen.

Die andere Sache, die Sie beachten sollten, ist $geoNear hat ein standardmäßiges "Limit" von 100 Ergebnissen, also müssen Sie das "Limit"-Argument dort auf die Zahl "aufpumpen", mit der erwartete Benutzer möglicherweise übereinstimmen. Sie könnten dies sogar in "Bereichslisten" von Benutzer-IDs für ein wirklich großes System tun, aber Sie können innerhalb einer einzigen Aggregationsoperation so groß werden, wie der Speicher es zulässt, und möglicherweise allowDiskUse wo nötig.

Wenn Sie diesen Parameter nicht optimieren, werden nur die nächsten 100 Ergebnisse ( Standard ) zurückgegeben, was möglicherweise nicht einmal für Ihre nächste Operation geeignet ist, die "in der Nähe" des Ereignisses zu filtern, um damit zu beginnen. Verwenden Sie jedoch Ihren gesunden Menschenverstand, da Sie sicherlich eine maximale Entfernung haben, um potenzielle Benutzer sogar herauszufiltern, und dies kann der Abfrage ebenfalls hinzugefügt werden.

Wie bereits erwähnt, geht es hier darum, die Distanz zum Vergleich zurückzugeben, also ist die nächste Stufe der $redact Operation, die den eigenen "Reiseentfernungs"-Wert des Benutzers mit der zurückgegebenen Entfernung von dem Ereignis vergleichen kann. Das Endergebnis gibt nur den Benutzern, die innerhalb ihrer eigenen Entfernungsbeschränkung von der Veranstaltung liegen, Anspruch auf eine Benachrichtigung.

Das ist die Logik. Sie projizieren die Entfernung vom Benutzer zum Ereignis und vergleichen dann mit dem gespeicherten Wert des Benutzers, für welche Entfernung er bereit ist zu reisen. Kein JavaScript und alle nativen Operatoren, die es ziemlich schnell machen.

Wie auch in den Optionen und dem allgemeinen Kommentar erwähnt, schlage ich wirklich vor, dass Sie einen "2dsphere" -Index für die genaue Berechnung der sphärischen Entfernung sowie die Konvertierung in den GeoJSON-Speicher für Ihre Koordinatenspeicherung in Ihren Datenbankobjekten verwenden, da dies beide allgemeine Standards sind konsistente Ergebnisse erzielen.