sql >> Databasteknik >  >> NoSQL >> MongoDB

Filtrera dokument efter avstånd lagrade i dokument med $near

Förutsatt att du redan har tränat på att agera på händelsedata när du tar emot den och har den i handen (om du inte har det är det en annan fråga, men titta på slutbara markörer ), så bör du ha ett objekt med den data som du kan fråga användarna med.

Detta är därför inte ett fall för JavaScript-utvärdering med $where , eftersom den inte kan komma åt frågedata som returneras från en $nära operation i alla fall. Det du istället vill ha är $geoNear från aggregeringsramverket. Detta kan projicera "avståndet" som hittas från frågan och tillåta ett senare skede att "filtrera" resultaten mot användarens lagrade värde för det maximala avståndet de vill resa till publicerade händelser:

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

Nu måste du vara försiktig med det returnerade numret, eftersom du ser ut att lagra i "legacy koordinatpar" istället för GeoJSON, så kommer avståndet som returneras från denna operation att vara i radianer och inte ett standardavstånd. Så om du antar att du lagrar i "miles" eller "kilometer" på användarobjekten så måste du beräkna via formeln som nämns i manualen under "Beräkna avstånd med sfärisk geometri" som nämns i manualen.

Grunderna är att du måste dividera med jordens ekvatorialradie, antingen 3 963,2 miles eller 6 378,1 kilometer för att konvertera för en jämförelse med vad du har lagrat.

Alternativet är att lagra i GeoJSON istället, där det finns en konsekvent mätning i meter.

Förutsatt att "kilometer" blir "om"-raden:

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

För att på ett tillförlitligt sätt jämföra ditt lagrade kilometervärde med det returnerade radianresultatet.

En annan sak att vara medveten om är att $geoNear har en standard "limit" på 100 resultat, så du måste "pumpa upp" argumentet "limit" där till antalet för förväntade användare att eventuellt matcha. Du kanske till och med vill göra detta i "intervalllistor" med användar-id:n för ett riktigt stort system, men du kan gå så stort som minnet tillåter inom en enda aggregeringsoperation och eventuellt lägga till allowDiskUse där det behövs.

Om du inte ställer in den parametern kommer bara de närmaste 100 resultaten (standard) att returneras, vilket kanske inte ens passar din nästa operation att filtrera de "nära" händelsen till att börja med. Använd dock sunt förnuft, eftersom du säkerligen har ett maxavstånd för att till och med filtrera bort potentiella användare, och det kan också läggas till i frågan.

Som sagt, poängen här är att returnera avståndet för jämförelse, så nästa steg är $redact operation som kan anpassa användarens eget "färdavstånd"-värde mot det returnerade avståndet från händelsen. Slutresultatet ger endast de användare som faller inom sitt eget avståndsbegränsning från evenemanget som kommer att kvalificera sig för meddelande.

Det är logiken. Du projicerar avståndet från användaren till händelsen och jämför sedan med användarens lagrade värde för vilket avstånd de är beredda att resa. Inget JavaScript, och alla inbyggda operatorer som gör det ganska snabbt.

Också, som nämnts i alternativen och den allmänna kommentaren, föreslår jag verkligen att du använder ett "2dsphere"-index för exakt sfärisk avståndsberäkning samt konvertering till GeoJSON-lagring för din koordinatlagring i dina databasobjekt, eftersom de båda är allmänna standarder som ger konsekventa resultat.



  1. mongoose för att avgöra update-upsert gör infoga eller uppdatering

  2. lagra data i redis genom cron jobb

  3. Läs från Kinesis ger tomma poster när den körs med tidigare sekvensnummer eller tidsstämpel

  4. Hur man tillämpar BsonRepresentation-attribut enligt konvention när man använder MongoDB