MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Funktionieren Geodatenabfragen auf Arrays? ( $geoWithin, $geoIntersects )

Dies ist eine dieser sowohl Ja- als auch Nein-Fragen, die beantwortet werden müssen, da ja ein Array für den Abgleich von Ergebnissen unterstützt wird, aber es ist wahrscheinlich auch nicht das, was Sie wirklich wollen, wenn man die Einschränkungen bei der Durchführung des Abgleichs berücksichtigt.

Die bemerkenswerte Änderung, die Sie hier benötigen, besteht darin, dass die Objekte selbst nicht so definiert sind, dass MongoDB sie so erkennt, wie Sie sie derzeit erstellt haben. Es gibt zwei Index- und allgemeine Suchformulare, entweder mit alten Koordinatenpaaren (was nur ein x,y-Punkt ist) oder als GeoJSON mit unterstützten GeoJSON-Objekten. Ihr Problem ist, dass Sie ein "Pseudo" -GeoJSON-Format haben, das nicht wirklich der Spezifikation entspricht, und dass Sie versuchen, direkt auf die "Koordinaten" zuzugreifen, wo Sie ein Objekt der obersten Ebene wie folgt benötigen:

{
    "regions": [
        {
            "name": "penta",
            "geometry": {
                "type": "Polygon",
                "coordinates": [[
                    [ 
                        -77.0322804898023610, 
                        -12.1271067552781560
                    ], 
                    [ 
                        -77.0336792618036270, 
                        -12.1255133434450870
                    ], 
                    [ 
                        -77.0326449349522590, 
                        -12.1239143495252150
                    ], 
                    [ 
                        -77.0300991833209990, 
                        -12.1238251884504540
                    ], 
                    [ 
                        -77.0299865305423740, 
                        -12.1262000752832540
                    ], 
                    [ 
                        -77.0322804898023610, 
                        -12.1271067552781560
                    ]
                ]]
            }
        },
        {
            "name": "triangle",
            "geometry": {
                "type": "Polygon",
                "coordinates": [[
                    [ 
                        -77.0313568040728570, 
                        -12.1266573492018090
                    ], 
                    [ 
                        -77.0325788855552670, 
                        -12.1246968022373030
                    ], 
                    [ 
                        -77.0300653204321860, 
                        -12.1246233756874440
                    ], 
                    [ 
                        -77.0313568040728570, 
                        -12.1266573492018090
                    ]
                ]]
            }
        }
    ]
}

Dadurch wird der GeoJSON-Teil so abstrahiert, dass er sowohl wohlgeformt als auch von den anderen Metadaten getrennt ist, die nicht Teil der Spezifikation sind. Idealerweise würden Sie auch indexieren, obwohl dies für $geoWithin nicht erforderlich ist oder $geoIntersects es hilft sicherlich:

db.regions.createIndex({ "regions.geometry": "2dsphere" })

Definieren des vollständigen Pfads zur GeoJSON-Definition innerhalb des Array-Elements.

Dann funktionieren Abfragen richtig:

db.regions.find({
    "regions.geometry" : { 
        "$geoIntersects" : { 
            "$geometry" : { 
                "type" : "Polygon" , 
                "coordinates" : [[
                    [ -77.02877718955278 , -12.123750122669545],
                    [ -77.03457042574883 , -12.123750122669545],
                    [ -77.03457042574883 , -12.12736341792724],
                    [ -77.02877718955278 , -12.12736341792724], 
                    [ -77.02877718955278 , -12.123750122669545]
                ]]
            }
        }
    }
})

Was mit dem obigen Dokument übereinstimmt. Aber natürlich gibt es mehrere Objekte im Array, also ist die Frage, welche davon übereinstimmen? Darauf gibt es keine unterstützte Antwort, da MongoDB das "Dokument" abgleicht und in keiner Weise angibt, welches Array-Element abgeglichen wurde.

Es gibt eine Option in der Aggregation $geoNear Dadurch kann das übereinstimmende Objekt zurückgegeben werden, wobei es in diesem Fall "am nächsten" wäre. Und mit solchen Details ist es dann möglich, diese Informationen zu verwenden, um zuzuordnen, welches Array-Element mit vollständigen Metadaten das Element enthält, das für "am nächsten" gefunden wurde, und diese Daten zu extrahieren. Aber auch hier ist es nur "near" und kann auch nie mehr als ein Ergebnis aus einem Array zurückgeben.

Aber im Allgemeinen ist es besser, nur die einzelnen Objekte als Dokumente in ihrer eigenen Sammlung zu speichern, wo der Abgleich mit einem bestimmten Objekt nur eine Frage des Abgleichs des Dokuments ist. Mit dem obigen Array in seiner eigenen Sammlung geben Sie also einfach die Abfrage für die passende Geometrie aus:

db.shapes.find({
    "geometry" : { 
        "$geoIntersects" : { 
            "$geometry" : { 
                "type" : "Polygon" , 
                "coordinates" : [ [ 
                    [ -77.02877718955278 , -12.123750122669545],
                    [ -77.03457042574883 , -12.123750122669545],
                    [ -77.03457042574883 , -12.12736341792724],
                    [ -77.02877718955278 , -12.12736341792724], 
                    [ -77.02877718955278 , -12.123750122669545]
                ]]
            }
        }
    }
})

Was die richtigen Objekte ergibt, da in diesem Fall die Form beide schneidet:

{
    "_id" : ObjectId("55f8d2fa66c2e7c750414b7a"),
    "name" : "penta",
    "geometry" : {
        "type" : "Polygon",
        "coordinates" : [[
            [
                    -77.03228048980236,
                    -12.127106755278156
            ],
            [
                    -77.03367926180363,
                    -12.125513343445087
            ],
            [
                    -77.03264493495226,
                    -12.123914349525215
            ],
            [
                    -77.030099183321,
                    -12.123825188450454
            ],
            [
                    -77.02998653054237,
                    -12.126200075283254
            ],
            [
                    -77.03228048980236,
                    -12.127106755278156
            ]
        ]]
    }
}
{
    "_id" : ObjectId("55f8d2fa66c2e7c750414b7b"),
    "name" : "triangle",
    "geometry" : {
        "type" : "Polygon",
        "coordinates" : [[
            [
                    -77.03135680407286,
                    -12.126657349201809
            ],
            [
                    -77.03257888555527,
                    -12.124696802237303
            ],
            [
                    -77.03006532043219,
                    -12.124623375687444
            ],
            [
                    -77.03135680407286,
                    -12.126657349201809
            ]
        ]]
    }
}

Sie können also Arrays verwenden, aber Sie können nur das Dokument wirklich abgleichen und nicht die einzelnen Array-Mitglieder, die Teil der Übereinstimmung waren. Dies gibt natürlich Dokumente als Ganzes zurück und Sie müssten herausfinden, welche Mitglieder den Kriterien im Client-Code entsprechen .

Außerdem versuchen mehrere Ihrer Abfrageversuche, das Array der Objektkoordinaten in einzelne Elemente zu "aufteilen". Dies wird überhaupt nicht unterstützt, da das Objekt nur als Ganzes behandelt werden kann und nicht als seine "Punkt"-Teile.