Question

I have a mongodb collection like

{
"_id" : 57,
"value" : {
    "user" : [ 
     {
            "fk_status_id"  : "0",
            "firstname"     : "Ajith",
            "lastname"      : "S",
            "city"          : "known",
            "State"         :"kerala",          
            "location" : {
                "lat" : 34.123456,
                "lon" : -95.123456
            },
     }

    ],
}}

with millions of documents

I want to alter the table structure like this

{
"_id" : 58,
    "user" : [ 
     {
            "fk_status_id"  : "0",
            "firstname"     : "Ajith",
            "lastname"      : "S",
            "city"          : "known",
            "State"         :"kerala",          
            "location" : {
                "lat" : 34.123456,
                "lon" : -95.123456
            },
     }

    ]}

I mean ,I want to omit the 'value{}' from this structure.

  • The data should come outside 'values'. Please resolve my problem.
Était-ce utile?

La solution

Well you didn't answer the comment that was given, but it is important for the reasons that will be explained.

So If you truly have "millions of documents" that you now have to alter and do this quickly then you have a clear problem to face. Since there is no "atomic" operation that can act on a document by "referencing" a value in an existing field to apply to an update the only way to do this is by actually looping the results:

db.collection.find().forEach(function(doc) {
    var user = doc.value.user;

    delete doc.value;

   db.collection.update(
       { "_id": doc._id },
       { "$set": { "user": user } }
   );

})

So that is the basic process and it is kind of horrible for large data sets.

But there is a way to do this for a "one off" transformation, though it is not a really nice way to do this, and it is called the db.eval() method. But please note the documentation (Big Rip from the manual page):

WARNING

  • By default, db.eval() takes a global write lock before evaluating the JavaScript function. As a result, db.eval() blocks all other read and write operations to the database while the db.eval() operation runs. Set nolock to true on the eval command to prevent the eval command from taking the global write lock before evaluating the JavaScript. nolock does not impact whether operations within the JavaScript code itself takes a write lock.

  • Do not use db.eval() for long running operations as db.eval() blocks all other operations. Consider using other server side code execution options.

  • You can not use db.eval() with sharded data. In general, you should avoid using db.eval() in sharded cluster; nevertheless, it is possible to use db.eval() with non-sharded collections and databases stored in a sharded cluster.

  • With authentication enabled, db.eval() will fail during the operation if you do not have the permission to perform a specified task.

Changed in version 2.4: You must have full admin access to run.

With that out of the way and if this actually meets your circumstance, then we need not be ostriches ( and bury our heads in the sand "myth" ) and actually do something that can run on the server to do this:

db.eval(function() {

    db.collection.find().forEach(function(doc) {
        var user = doc.value.user;

        delete doc.value;

        db.collection.update(
            { "_id": doc._id },
            { "$set": { "user": user } }
        );

    });

});

Of course as suggested there are other ways to approach this.

  • mapReduce can sort of do this without the locking issues, but the output is still going to need a lot of reshaping and also collection renaming and replacement.

  • Run actually on the sever ( or closest in network terms ) which is probably really the best option. In this way you can code safely and avoid extended locking issues.

But if you are really "in a jam" as it were, the JavaScript execution on the server will fix that specific problem if that is what you need.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top