2016-07-12 7 views
0

Ich habe eine Sammlung, die wie folgt aussieht:

{ 
    "_id" : ObjectId("5783051796a90cd8098fc3e0"), 
    "name" : "Org 1", 
    "locations" : [ 
     { 
      "name" : "loc 1", 
      "_id" : ObjectId("57831ac2febcceaf173e81ab"), 
      "address" : { 
       "coordinates" : [ 
        172.6034932, 
        -43.5333797 
       ] 
      } 
     }, 
     { 
      "name" : "loc 2", 
      "_id" : ObjectId("5783436dc57b8d6248c9c196"), 
      "address" : { 
       "coordinates" : [ 
        172.6034977, 
        -43.5335158 
       ] 
      } 
     }, 
     { 
      "name" : "loc 3", 
      "_id" : ObjectId("5783439bc57b8d6248c9c197"), 
      "address" : { 
       "coordinates" : [ 
        168.6626435, 
        -45.0311622 
       ] 
      } 
     } 
    ] 
} 

Es gibt einen 2dsphere Index auf locations.address.coordinates

I aggreagate mit geonear bin mit denen abfragen Dokumente:

Diese Aggregation funktioniert gut und ich bekomme alle Dokumente mit mindestens 1 Speicherort in der ausgewählte Bereich. Das Problem ist, dass ich nur die relevanten Standorte abrufen möchte, wenn mehr als 1 Übereinstimmung in 1 Dokument vorhanden ist.

MongoDB kann mir sagen, welche ist am nächsten mit includeLocs Option, aber das ist nicht genug, wie ich alle Details möchten.

Zumindest, wenn ich die Entfernung für jeden Ort hätte, könnte ich es später auf die Aggregationsleitung filtern. Mit der distanceField Option kann ich den Abstand für das gesamte Dokument gefunden, aber nicht für jeden passenden Standort

Zur Erinnerung: Mein Ziel ist es, alle Dokumente mit mindestens 1 Treffer Standort zu finden, und weiß, welche die jeweiligen Standorte sind unter den Standorten Array.

Gibt es einen Weg es zu bekommen oder frage ich zu viel nach Mongo?

Antwort

1

Vollständig neu geschrieben Antwort gegeben, um die Klarstellung.

Sie haben richtig, $geoNear muss die erste Stufe sein. Die Lösung besteht in der Verwendung von "$ unwind" und "$ redact", um das übereinstimmende Standortdokument von geoNear mit dem entsprechenden Array-Element zu vergleichen.

Testdaten:

db.test.insert({ "locs" : [ 
{ "name" : "a", "address" : { "type" : "Point", "coordinates" : [ 0, 0 ] } }, 
{ "name" : "b", "address" : { "type" : "Point", "coordinates" : [ 1, 1 ] } }, 
{ "name" : "c", "address" : { "type" : "Point", "coordinates" : [ 2, 2 ] } } 
]}) 

db.test.insert({ "locs" : [ 
{ "name" : "h", "address" : { "type" : "Point", "coordinates" : [ 1.01, 1.01 ] } } 
]}) 

db.test.ensureIndex({ "locs.address" : "2dsphere" }) 

Abfrage:

db.test.aggregate([ 
{ "$geoNear" : { near : { "type" : "Point", "coordinates" : [ 1, 1 ] }, distanceField: "dist.calculated", maxDistance: 5000, includeLocs: "dist.location", num: 5, limit: 200, spherical: true } }, 
{ "$unwind" : "$locs" }, 
{ "$redact" : { 
    "$cond" : { 
    if : { "$eq" : [ { "$cmp" : [ "$locs.address", "$dist.location" ] }, 0 ] }, 
    then : "$$KEEP", 
    else : "$$PRUNE" 
    } 
} 
} 
]) 

Der Wille Ausgang geoNear Stufe der gesamten Dokumente mit einem "dist" -Feld den Abstand und die passenden Standortfeld zeigen:

{ 
    "_id" : ObjectId("5786fa0ddeb382a191a43122"), 
    "locs" : [ { "name" : "h", "address" : { "type" : "Point", "coordinates" : [ 1.01, 1.01 ] } } ], 
    "dist" : { 
    "calculated" : 0, 
    "location" : { "type" : "Point", "coordinates" : [ 1, 1 ] } 
    } 
} 

Wir $unwind das Array "locs" für den Zugriff auf einzelne Array-Elemente. Das Feld dist ist erhalten.

Das Feld $redact kann dann verwendet werden, um alle Array-Elemente zu entfernen, deren Adresse nicht mit der von der Stufe $ geoNear zurückgegebenen Adresse übereinstimmt.

Ergebnisse:

{ 
"_id" : ObjectId("5786fa0ddeb382a191a43121"), 
"locs" : { "name" : "b", "address" : { "type" : "Point", "coordinates" : [ 1, 1 ] } }, 
"dist" : { "calculated" : 0, "location" : { "type" : "Point", "coordinates" : [ 1, 1 ] } } 
} 
{ 
"_id" : ObjectId("5786fa0ddeb382a191a43122"), 
"locs" : { "name" : "h", "address" : { "type" : "Point", "coordinates" : [ 1.01, 1.01 ] } }, 
"dist" : { "calculated" : 1574.1651198970692, "location" : { "type" : "Point", "coordinates" : [ 1.01, 1.01 ] } } 
} 

Auf einer breiteren Notiz können Sie einen einzelnen Geo-Raumpunkt pro Dokument sind zu prüfen, so dass nur das Datenmodell anpassen. Die von geoNear bereitgestellten Entfernungsdaten scheinen sich nur auf ein einzelnes Dokument zu beziehen, sodass in Zukunft Probleme auftreten können, wenn Sie nach mehreren Dokumenten filtern möchten.

+0

Meine Frage ist vielleicht nicht ganz klar, ich habe sie bearbeitet.Mein Ziel ist es, alle Dokumente mit mindestens einem übereinstimmenden Ort zu finden und zu wissen, welche Orte sich unter den Orten befinden. Leider werden Sie mir die Antwort nicht geben, welche Standorte übereinstimmen. BTW, $ GeoNear kann nur auf der ersten Stufe einer Pipeline verwendet werden – Tdy

+0

@Tdy Sie sind absolut korrekt über GeoNear. Re-write Antwort gegeben Ihre zusätzlichen Informationen. –

+0

Danke für diese sehr detaillierte Antwort! Das funktioniert, wenn Sie nur einen übereinstimmenden Speicherort pro Dokument haben oder wenn Sie nur den nächstgelegenen möchten. Mein Problem war, die Entfernung für jeden Ort zu kennen und alle passenden Orte zu finden. Das ist dein Punkt am Ende der Antwort und du hast Recht. Am Ende habe ich mein Datenmodell umgestaltet, die Speicherorte in einer eigenen Sammlung gespeichert und einen Verweis darauf in mein Originaldokument eingefügt. Ich denke, dass MongoDB genau das machen kann, was ich will. Ihre Antwort kann Leuten helfen, die sich mit $ geoNear beschäftigen, aber es wird mein Problem nicht lösen. Danke trotzdem – Tdy

Verwandte Themen