MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Le query geospaziali funzionano sugli array? ($geoWithin, $geoIntersects)

Questa è una di quelle domande a cui rispondere sia sì che no, poiché sì un array è supportato per la corrispondenza dei risultati ma probabilmente non è nemmeno quello che vuoi veramente considerando le restrizioni su come viene eseguita la corrispondenza.

Il cambiamento notevole di cui hai bisogno qui è che gli oggetti stessi non sono definiti in modo tale che MongoDB li riconoscerà come li hai attualmente formati. Esistono due moduli di ricerca di indici e generali con coppie di coordinate legacy (che è solo un punto x,y) o come GeoJSON con oggetti GeoJSON supportati. Il tuo problema è che hai un formato GeoJSON "psuedo" che non è realmente conforme alle specifiche e che stai cercando di accedere direttamente alle "coordinate", dove hai bisogno di un oggetto di livello superiore in questo modo:

{
    "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
                    ]
                ]]
            }
        }
    ]
}

In modo che astragga la parte GeoJSON in modo che sia ben formata e separata dagli altri metadati che non fanno parte delle specifiche. Idealmente, indicizzeresti anche tu, sebbene non sia richiesto per $geoWithin o $geoIntersects aiuta sicuramente:

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

Definizione del percorso completo della definizione GeoJSON all'interno dell'elemento dell'array.

Quindi le query funzionano correttamente:

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

Che corrisponde al documento sopra. Ma ovviamente ci sono più oggetti nell'array, quindi la domanda è:quale di questi corrispondeva? A cui non esiste una risposta supportata, poiché MongoDB corrisponde al "documento" e non indica in alcun modo quale elemento dell'array è stato abbinato.

C'è un'opzione nell'aggregazione $geoNear ciò consente la restituzione dell'oggetto abbinato, dove in questo caso sarebbe "il più vicino". E con dettagli del genere è quindi possibile utilizzare tali informazioni per abbinare quale elemento dell'array con metadati completi contiene l'elemento trovato per "più vicino" ed estrarre quei dati. Ma ancora una volta è solo "vicino" e non può mai restituire più di un risultato da un array.

Ma in generale è meglio solo separare gli oggetti come documenti nella propria collezione, dove la corrispondenza con un oggetto distinto è solo una questione di corrispondenza del documento. Quindi, con l'array sopra nella sua raccolta, emetti semplicemente la query per la geometria corrispondente:

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

Che fornisce gli oggetti corretti poiché in questo caso la forma interseca entrambi:

{
    "_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
            ]
        ]]
    }
}

Quindi puoi usare gli array ma puoi davvero abbinare solo il documento e non i singoli membri dell'array che facevano parte della corrispondenza, quindi questo restituirà ovviamente i documenti del corso nel suo insieme e dovresti capire quali membri corrispondono ai criteri nel codice client .

In un'altra nota, molti dei tuoi tentativi di query cercano di "scomporre" l'array di coordinate dell'oggetto in singoli elementi. Questo non è affatto supportato in quanto l'oggetto può essere trattato solo nel suo insieme e non come parti "Punto".