db-migrate / mongodb

mongodb driver for db-migrate
Other
25 stars 58 forks source link

Basic example code usage in readme #27

Open jirikrepl opened 6 years ago

jirikrepl commented 6 years ago

Hi I am struggling to get this package working. Seems to me that I have connection to mongo db setup properly, because I could list my collections names from calling db._getCollectionNames

but I am not able to figure out how to get collection, so I can perform update on collection:

exports.up = function (db) {
    // how to use db to get to collection?
    // this won't work
    db.collection('Profile').update({},
        { $set: { "termsOfServiceUpdated": true } },
        {
            upsert: false,
            multi: true
        });
};

thank you very much

--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/59018941-basic-example-code-usage-in-readme?utm_campaign=plugin&utm_content=tracker%2F12293389&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F12293389&utm_medium=issues&utm_source=github).
wzrdtales commented 6 years ago

This is a migration library, not just a wrapper for the driver below. So you first need to access the driver should you want to perform operations not, yet, directly supported by the driver or the framework.

In case of mongodb this is getDbInstance merged from this PR over here https://github.com/db-migrate/mongodb/pull/21

sidharthnayyar commented 6 years ago

Hi, sorry new to this but trying to use getDbInstance, would this be a case of just doing

exports.up = function(db, callback) {
    db.getDbInstance().getCollection(....)....
}

Thanks in advance.

Kind regards Sidharth

jugaltheshah commented 6 years ago

Hi, sorry new to this but trying to use getDbInstance, would this be a case of just doing

exports.up = function(db, callback) {
    db.getDbInstance().getCollection(....)....
}

Thanks in advance.

Kind regards Sidharth

@sidharthnayyar were you able to get db.getDbInstance() working? When I try to run it I get [ERROR] TypeError: db.getDbInstance is not a function. I confirmed I have db-migrate-mongodb@1.4.0 too.

danielabar commented 6 years ago

I also got db.getDbInstance is not a function. Stacktrace was from db-migrate, maybe the db object in exports.up and exports.down only has available functions from db-migrate (createCollection, dropCollection, addIndex etc.).

But debugging db, was able to get this working:

exports.up = function(db) {
  let mClient;
  return db.connection.connect(db.connectionString, { native_parser: true })
    .then((mClientInst) => {
      // mClientInst is an instance of MongoClient
      mClient = mClientInst;
      return mClient.collection('myCollection')...;
    })
    .then(() => mClient.close())
    .catch(() => mClient.close());
};

Migrations can be debugged with VS Code by adding the following to launch.json, then put a breakpoint in any up/down function:

"configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "DB Migrate Down",
      "program": "${workspaceFolder}/node_modules/.bin/db-migrate",
      "args": [
        "down"
      ]
    },
    {
      "type": "node",
      "request": "launch",
      "name": "DB Migrate Up",
      "program": "${workspaceFolder}/node_modules/.bin/db-migrate",
      "args": [
        "up"
      ]
    },
    ...
  ]
}
jugaltheshah commented 6 years ago

Thanks for sharing your trick to getting an instance of Mongo client, @danielabar. It seems like it would open 2 connections to the db though? Thanks also for the debugging tip, that is super useful!

Actually after I posted I found this on the web, so here's what I have for now:

const collections = [{
    name: 'users'
}];

exports.down = async (db) => {
    const dbDriver = await db._run('getDbInstance');
    collections.forEach(async (collection) => {
        const fileName = collection.dataFile || `${collection.name}.json`;
        const data = require(Path.join(__dirname, 'docs', fileName));

        await Promise.all(data.map((doc) => {
            return dbDriver.collection(collection.name).deleteOne({ _id: doc._id });
        }));
    });

    dbDriver.close();
};

I wonder if there's a better way than both of ours though -- it seems risky resorting to using undocumented methods that may change internally.

danielabar commented 6 years ago

I see what you mean about connecting twice. Also good point about undocumented methods.

To project maintainers: Any thoughts on this - what's the recommended way to get access to mongo instance, given that getDBInstance method from PR #21 isn't working (or not sure on correct usage)?

rymut commented 6 years ago

The problem is how the db-migrate reduce driver object to the interface db-migrate/lib/migrator.js:51-52

var Migrator = function (driver, migrationsDir, empty, intern) {
  this.driver = dbmUtil.reduceToInterface(driver, MigratorInterface);

Function reduceToInterface will remove all methods that fall outside the interface, but there is a simple solution - modify the extending object exported by adding empty method getDbInstance in db-migrate\lib\interface\migratorInterface.js module. It can be done by using db-migrate programable API (file db-migrate.js):

const MigratorInterface = require('db-migrate/lib/interface/migratorInterface');

const dummy = () => undefined;
MigratorInterface.extending.getDbInstance = dummy;

const DbMigrate = require('db-migrate');

const dbmigrate = DbMigrate.getInstance();
if (dbmigrate.registerAPIHook) {
  dbmigrate.registerAPIHook()
    .then(() => {
      dbmigrate.run();
    });
} else { dbmigrate.run(); }

Now node db-migrate should be called instead of node_modules/.bin/db-migrate and this way getDbInstance is available to use in migrations scripts.

gmahe commented 5 years ago

This will help people who are still looking for it:

exports.up = function(db) {
  return db._run("update", "collectionToUpdate", {
    query: { id: "1123" }, // What you need to find
    update: { title: "myNewTitle", desc: "It works" }, // What you need to change
    options: {}, // Options like e.g: upsert: false,
  });
};

Have a look here There is also updateMany

wzrdtales commented 5 years ago

please be aware _ methods are private and subject to change at any time without warning. they may completely change their behavior or suddenly disappear. you can use them, but be prepared that you might have to change your migrations or fixate a driver version.

gmahe commented 5 years ago

Is there any other way?

dprentis commented 5 years ago

Hello, I've been using db-migrate with mysql and it's working fine. Now I wanted to use db-migrate with mongodb and am having trouble implementing a migration.

The only documentation I found is here: https://db-migrate.readthedocs.io/en/latest/API/NoSQL/ It would imply there is no officially supported way of updating or deleting records - or am I missing something?

Thanks for any answer - I'll try using @jugaltheshah suggested method for now.

gmahe commented 5 years ago

Not the official way but you can use the bellow code to remove a single entry:

exports.down = function(db, callback) {
  db._run("remove", "pages", { id: "5bb9e79df82c0151fc0cdab2" }, callback);
};

But the code has a bug with deleteMany at the moment so you can use @jugaltheshah method.

exports.down = async function(db) {
  // Remove them all. (bug in their method at the moment)
  const getDbInstance = await db._run("getDbInstance");
  getDbInstance.collection("pets").deleteMany({ type: "cat" }); // Removes all occurrence of that value.
  getDbInstance.close();
};

More details here: https://github.com/db-migrate/mongodb/blob/master/index.js#L337

dprentis commented 5 years ago

thanks - I'm doing that and its working fine 👍