MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Filtrar Documentos por Distância Armazenada no Documento com $near


Presumindo que você já trabalhou nos dados do evento ao recebê-los e tê-los em mãos (se não tiver, isso é outra pergunta, mas veja cursores tailable ), então você deve ter um objeto com esses dados para consultar os usuários.

Portanto, este não é um caso para avaliação de JavaScript com $where , pois não pode acessar os dados de consulta retornados de um $near operação de qualquer maneira. O que você quer é $geoNear da estrutura de agregação. Isso pode projetar a "distância" encontrada na consulta e permitir que um estágio posterior "filtre" os resultados em relação ao valor armazenado pelo usuário para a distância máxima que deseja percorrer até os 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
  }
)

Agora você precisa ter cuidado com o número retornado, pois, como parece estar armazenando em "pares de coordenadas herdadas" em vez de GeoJSON, a distância retornada dessa operação será em radianos e não em uma distância padrão. Então, supondo que você esteja armazenando em "milhas" ou "quilômetros" nos objetos do usuário, então você precisa calcular através da fórmula mencionada no manual em "Calcular distâncias usando geometria esférica" conforme mencionado no manual.

O básico é que você precisa dividir pelo raio equatorial da Terra, sendo 3.963,2 milhas ou 6.378,1 quilômetros para converter para uma comparação com o que você armazenou.

A alternativa é armazenar em GeoJSON, onde há uma medição consistente em metros.

Assumindo "quilômetros" que a linha "se" se torna:
"if": { "$lt": [
    "$eventDistance",
    { "$divide": [ "$maxDistance", 6,378.1 ] }
 ]},

Para comparar de forma confiável seu valor de quilômetro armazenado com o resultado em radianos retornado.

A outra coisa que você deve saber é que $geoNear tem um "limite" padrão de 100 resultados, então você precisa "bombear" o argumento "limite" para o número esperado de usuários para corresponder. Você pode até querer fazer isso em "listas de intervalo" de IDs de usuários para um sistema realmente grande, mas você pode ir tão grande quanto a memória permitir em uma única operação de agregação e possivelmente adicionar allowDiskUse onde necessário.

Se você não ajustar esse parâmetro, apenas os 100 resultados mais próximos ( default ) serão retornados, o que pode não se adequar à sua próxima operação de filtrar aqueles "próximos" ao evento para começar. Use o bom senso, pois você certamente tem uma distância máxima para filtrar usuários em potencial, e isso também pode ser adicionado à consulta.

Como dito, o ponto aqui é retornar a distância para comparação, então o próximo estágio é o $redact operação que pode ajustar o valor de "distância de viagem" do próprio usuário em relação à distância retornada do evento. O resultado final fornece apenas os usuários que se enquadram em sua própria restrição de distância do evento que se qualificarão para notificação.

Essa é a lógica. Você projeta a distância do usuário ao evento e, em seguida, compara com o valor armazenado pelo usuário para a distância que eles estão preparados para percorrer. Sem JavaScript e todos os operadores nativos que o tornam bastante rápido.

Também conforme observado nas opções e no comentário geral, eu realmente sugiro que você use um índice "2dsphere" para cálculo preciso da distância esférica, bem como converter para armazenamento GeoJSON para seu armazenamento de coordenadas em seus objetos de banco de dados, pois ambos são padrões gerais que produzir resultados consistentes.