Cris69
Cris69

Reputation: 590

How to re-structure a MongoDb collection

I need to query a MongoDb collection with this structure:

{
"_id" : ObjectId("53cfc8bf8f11381e28373153"),
"name" : "0700452",
"description" : "",
"styleurl" : "#style00001",
 "point" : {
    "altitudemode" : "clampToGround",
    "coordinates" : "11.8263919657018, 41.2129293739724, 1.02757364822173"
 }
}

The type of query I need to do is based on 2dsphere indexed search, but when I tried this one:

db.coordinatas.find({ $near : { type:'Point', point:{coordinates:"15.8263919657018, 41.2129293739724"}, $maxDistance : 100 }});

I get an error:

error: {
    "$err" : "can't find any special indices: 2d (needs index), 2dsphere (needs index),  for: { $near: { type: \"Point\", point: { coordinates: \"15.8263919657018, 41.2129293739724\" }, $maxDistance: 100.0 } }",
    "code" : 13038
}

Contrary of what the error says the index is there:

[
{
    "v" : 1,
    "key" : {
        "_id" : 1
    },
    "ns" : "test4-dev.coordinatas",
    "name" : "_id_"
},
{
    "v" : 1,
    "key" : {
        "coordinates" : "2dsphere"
    },
    "ns" : "test4-dev.coordinatas",
    "name" : "coordinates"
}

]

This probably happens because the 2dsphere index need an array of two coordinates.
I was thinking maybe I can restructure the collection in this new format:

{
"_id" : ObjectId("53cfc8bf8f11381e28373153"),
"name" : "0700452",
"coordinates": [11.8263919657018, 41.2129293739724]
}

and query the collection in this way :

db.coordinatas.find({ $near : {type:'Point', coordinates:[11.8263919657018, 41.2129293739724], $maxDistance : 100 }});

How can I change the format of the old collection in the new one ?

Upvotes: 1

Views: 231

Answers (2)

Juan Carlos Farah
Juan Carlos Farah

Reputation: 3879

As documented in this page:

The [2dsphere] index supports data stored as both GeoJSON objects and as legacy coordinate pairs. The index supports legacy coordinate pairs by converting the data to the GeoJSON Point type.

If you're going to transform you data, I would recommend you transform it to a GeoJSON point, which would look something like this:

{ loc: { type: "Point", coordinates: [ 11.8263919657018, 41.2129293739724 ] } } 

In order to do so, you would have to run a script in which you do the following:

  1. Iterate through all of the documents.
  2. For each document retrieve the two coordinate values.
  3. Using the new coordinate values, create a GeoJSON field.
  4. Unset the the previous field.

Once all the documents are transformed, you can drop you old index and build the new index using the new field name.

UPDATE:

As a reference, this is a way to update the collection through the mongo shell. You might find it useful.

// Iterate through all the documents and set the new field
// with the GeoJSON point using the old coordinates string. 
db.coords.find().forEach(function(doc) {
    var id = doc._id,
        coords = doc.point.coordinates.split(", ");

    db.coords.update({ "_id" : id }, { $set : { "point.loc.type": "Point", "point.loc.coordinates": [ coords[0], coords[1] ] } });
});

// Unset the old field from all the documents
db.coords.update({}, { $unset: { "point.coordinates" : 1 } }, { "multi": true });

Upvotes: 1

Philipp
Philipp

Reputation: 69663

MongoDBs string operations aren't advanced enough to do this completely on the database, so you will have to do it with a little program in the MongoDB shell. Alternatively, you can do this in your favorite programming language which has a MongoDB driver.

  1. Make an empty db.collection.find() query to get a cursor over every single document in the collection
  2. For each doucment you find, split the string in the coordinates field into an array of floating point values
  3. save the document back to the collection (the save function will search for a document with the same _id and update it, when none is found it creates a new document, which will not happen in your case, because you obtained the _id from the database)

Upvotes: 0

Related Questions