sql >> Databáze >  >> NoSQL >> MongoDB

Filtrovat dokumenty podle vzdálenosti uložené v dokumentu s $ blízko

Předpokládejme, že jste se již dopracovali k tomu, že budete jednat s daty události, jakmile je dostanete a máte je po ruce (pokud ne, pak je to jiná otázka, ale podívejte se na tabulkové kurzory ), pak byste měli mít objekt s těmito daty, na který se budou uživatelé dotazovat.

Toto tedy není případ hodnocení JavaScriptu pomocí $where , protože nemá přístup k datům dotazu vráceným z $near operace každopádně. Místo toho chcete $geoNear z agregačního rámce. To může promítnout „vzdálenost“ zjištěnou z dotazu a umožnit pozdější fázi „filtrovat“ výsledky podle hodnoty uložené uživatelem pro maximální vzdálenost, kterou chtějí ujet k publikovaným událostem:

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

Nyní musíte být opatrní s vráceným číslem, protože protože se zdá, že ukládáte do "starších souřadnicových párů" místo GeoJSON, pak vzdálenost vrácená z této operace bude v radiánech a ne ve standardní vzdálenosti. Takže za předpokladu, že ukládáte uživatelské objekty v "mílích" nebo "kilometrech", musíte vypočítat pomocí vzorce uvedeného v příručce pod "Vypočítat vzdálenosti pomocí sférické geometrie" jak je uvedeno v návodu.

Základem je, že musíte vydělit rovníkovým poloměrem Země, což je buď 3 963,2 mil nebo 6 378,1 kilometrů, abyste mohli převést pro srovnání s tím, co máte uloženo.

Alternativou je místo toho ukládat do GeoJSON, kde je konzistentní měření v metrech.

Za předpokladu „kilometrů“ se čára „pokud“ změní na:

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

Chcete-li spolehlivě porovnat vaši uloženou hodnotu kilometrů s vráceným výsledkem v radiánech.

Další věc, kterou je třeba si uvědomit, je $geoNear má výchozí „limit“ 100 výsledků, takže tam musíte „napumpovat“ argument „limit“ na počet, aby se očekávaní uživatelé mohli shodovat. Možná to budete chtít udělat v "seznamech rozsahů" uživatelských id pro opravdu velký systém, ale můžete jít tak velký, jak to paměť dovolí, v rámci jedné operace agregace a případně přidat allowDiskUse kde je potřeba.

Pokud tento parametr nevyladíte, bude vráceno pouze nejbližších 100 výsledků (výchozí nastavení), což nemusí vyhovovat ani vaší další operaci filtrování těch, kteří jsou „blízko“ události. Používejte však zdravý rozum, protože určitě máte maximální vzdálenost, abyste dokonce odfiltrovali potenciální uživatele, a to lze také přidat do dotazu.

Jak bylo uvedeno, jde zde o vrácení vzdálenosti pro srovnání, takže další fází je $redact operace, která může porovnat uživatelovu vlastní hodnotu "cestovní vzdálenosti" s vrácenou vzdáleností od události. Konečný výsledek poskytuje pouze těm uživatelům, kteří spadají do jejich vlastní vzdálenosti od události, kteří budou mít nárok na oznámení.

To je ta logika. Promítnete vzdálenost od uživatele k události a poté porovnáte s uloženou hodnotou uživatele, jakou vzdálenost je připraven ujet. Žádný JavaScript a všechny nativní operátory, díky kterým je to docela rychlé.

Jak je také uvedeno v možnostech a obecném komentáři, opravdu doporučuji použít index „2dsphere“ pro přesný výpočet sférické vzdálenosti a také převod na úložiště GeoJSON pro uložení souřadnic v databázových objektech, protože oba jsou obecnými standardy, které dosáhnout konzistentních výsledků.



  1. Vytvořte ISODate pomocí pyMongo

  2. Jak získat přístup k localhost hostitele z clusteru kubernetes

  3. Zkombinujte dvě instance Redis do jedné instance se dvěma databázemi

  4. Měly by dva moduly používat stejné připojení redis? (Pracuji s Flaskem)