sql >> Base de Datos >  >> NoSQL >> MongoDB

Filtrar documentos por distancia almacenados en documento con $cerca

Suponiendo que ya haya trabajado para actuar sobre los datos del evento a medida que los recibe y los tiene a mano (si no lo ha hecho, entonces esa es otra pregunta, pero mire cursores de cola ), entonces debería tener un objeto con esos datos para consultar a los usuarios.

Por lo tanto, este no es un caso para la evaluación de JavaScript con $where , ya que no puede acceder a los datos de consulta devueltos por un $near operación de todos modos. En cambio, lo que desea es $geoNear del marco de agregación. Esto puede proyectar la "distancia" encontrada a partir de la consulta y permitir que una etapa posterior "filtre" los resultados contra el valor almacenado del usuario para la distancia máxima que desea viajar a los eventos publicados:

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

Ahora debe tener cuidado con el número devuelto, ya que parece que está almacenando en "pares de coordenadas heredados" en lugar de GeoJSON, entonces la distancia devuelta de esta operación será en radianes y no una distancia estándar. Entonces, suponiendo que está almacenando en "millas" o "kilómetros" en los objetos de usuario, entonces necesita calcular a través de la fórmula mencionada en el manual en "Calcular distancias usando geometría esférica" como se menciona en el manual.

Lo básico es que debe dividir por el radio ecuatorial de la tierra, siendo 3963,2 millas o 6378,1 kilómetros para convertir y comparar con lo que ha almacenado.

La alternativa es almacenar en GeoJSON, donde hay una medida consistente en metros.

Asumiendo "kilómetros" esa línea "si" se convierte en:

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

Para comparar de manera confiable su valor de kilómetro almacenado con el resultado en radianes devuelto.

La otra cosa a tener en cuenta es que $geoNear tiene un "límite" predeterminado de 100 resultados, por lo que debe "inflar" el argumento "límite" allí al número para que los usuarios esperados posiblemente coincidan. Incluso es posible que desee hacer esto en "listas de rango" de ID de usuario para un sistema realmente grande, pero puede ir tan grande como lo permita la memoria dentro de una sola operación de agregación y posiblemente agregar allowDiskUse donde sea necesario.

Si no ajusta ese parámetro, solo se devolverán los 100 resultados más cercanos (predeterminados), lo que puede que ni siquiera se adapte a su próxima operación de filtrado de los "cercanos" al evento para empezar. Sin embargo, use el sentido común, ya que seguramente tiene una distancia máxima para filtrar a los usuarios potenciales, y eso también se puede agregar a la consulta.

Como se indicó, el punto aquí es devolver la distancia para comparar, por lo que la siguiente etapa es $redact operación que puede ajustar el propio valor de "distancia de viaje" del usuario frente a la distancia devuelta del evento. El resultado final proporciona solo aquellos usuarios que se encuentran dentro de su propio límite de distancia del evento que calificarán para la notificación.

Esa es la lógica. Usted proyecta la distancia desde el usuario hasta el evento y luego compara con el valor almacenado del usuario la distancia que está preparado para viajar. Sin JavaScript y todos los operadores nativos que lo hacen bastante rápido.

Además, como se indica en las opciones y el comentario general, realmente sugiero que use un índice "2dsphere" para el cálculo preciso de la distancia esférica, así como la conversión al almacenamiento GeoJSON para su almacenamiento de coordenadas en su base de datos Objetos, ya que ambos son estándares generales que producir resultados consistentes.