355

I have an array of _ids and I want to get all docs accordingly, what's the best way to do it ?

Something like ...

// doesn't work ... of course ...

model.find({
    '_id' : [
        '4ed3ede8844f0f351100000c',
        '4ed3f117a844e0471100000d', 
        '4ed3f18132f50c491100000e'
    ]
}, function(err, docs){
    console.log(docs);
});

The array might contain hundreds of _ids.

Zeinab
  • 389
  • 4
  • 14
ezmilhouse
  • 8,933
  • 7
  • 29
  • 38

9 Answers9

651

The find function in mongoose is a full query to mongoDB. This means you can use the handy mongoDB $in clause, which works just like the SQL version of the same.

model.find({
    '_id': { $in: [
        mongoose.Types.ObjectId('4ed3ede8844f0f351100000c'),
        mongoose.Types.ObjectId('4ed3f117a844e0471100000d'), 
        mongoose.Types.ObjectId('4ed3f18132f50c491100000e')
    ]}
}, function(err, docs){
     console.log(docs);
});

This method will work well even for arrays containing tens of thousands of ids. (See Efficiently determine the owner of a record)

I would recommend that anybody working with mongoDB read through the Advanced Queries section of the excellent Official mongoDB Docs

Community
  • 1
  • 1
Daniel Mendel
  • 9,862
  • 1
  • 24
  • 37
  • 23
    Kind of late to this discussion, but how would you ensure the order of the items returned matches the order of the array of items you provide in the array? Documents are not guaranteed to come out in any order unless you specify a sort. What if you want them sorted in the same order you list them in the array (e.g. ...000c, ...000d, ...000e)? – Kevin Mar 30 '14 at 23:34
  • 10
    This did not work for some reason. I got an empty array of docs – chovy Nov 15 '14 at 09:59
  • 3
    @chovy try [converting them to ObjectIds](https://stackoverflow.com/questions/6578178/node-js-mongoose-js-string-to-objectid-function) first, instead of passing strings. – Georgi Hristozov Dec 02 '14 at 15:28
  • 2
    @Kevin You might be interested in this answer: http://stackoverflow.com/a/22800784/133408 –  Jan 12 '15 at 08:46
  • For anyone getting the empty array of docs like @chovy, you can also try removing the quotes from '_id' in the find statement. Worked for me. – Schybo Mar 28 '15 at 22:13
  • 2
    @Schybo that makes absolutely no difference. `{ _id : 5 }` is the same as `{ '_id' : 5 }`. – royhowie Aug 04 '15 at 03:47
  • ```db.user.find({'_id': {$in : ['561f9f58ae729de84253b8b5','561f9f6aae729de84253b8b7']}})``` does not return any results eventhough data is there. – Shreejibawa Oct 23 '15 at 09:00
  • I believe we can create an array using map method that has ObjectIds as children and pass that to "find" method. – LizardKingLK Mar 12 '22 at 04:49
164

Ids is the array of object ids:

const ids =  [
    '4ed3ede8844f0f351100000c',
    '4ed3f117a844e0471100000d', 
    '4ed3f18132f50c491100000e',
];

Using Mongoose with callback:

Model.find().where('_id').in(ids).exec((err, records) => {});

Using Mongoose with async function:

const records = await Model.find().where('_id').in(ids).exec();

Or more concise:

const records = await Model.find({ '_id': { $in: ids } });

Don't forget to change Model with your actual model.

snnsnn
  • 10,486
  • 4
  • 39
  • 44
  • 10
    This should be the accepted answer as it is the most up to date and coherent one. You don't have to convert the ids to ObjectId as in the accepted answer, and it uses the *mongoose* imperative style queries. Thanks btw! – Javi Marzán Mar 15 '20 at 15:45
  • This is a very clean and updated method, if you don't mind I'd like to ask a few question, if I have an array of referenced `ObjectId`'s like the above (say, I have projects, and I assigned an array of projects to certain users with the project_id referenced on the user model), if I delete a project, how do I make sure the `id` is deleted from the array referenced from the user model ? Thanks mat. – Eazy Apr 28 '20 at 15:02
  • this is what I needed! It's clean and easy to use with ids that are a ref in another model – tarafenton Sep 02 '20 at 03:27
  • This works well ! For an optimized version, you can attach .lean() at the end which will return only POJO ( Plain Old Javascript Object ). You can also add select() and pick only the required fields of the document. – Himanshu Tariyal Feb 16 '21 at 07:20
23

Combining Daniel's and snnsnn's answers:

let ids = ['id1', 'id2', 'id3'];
let data = await MyModel.find({
  '_id': { 
    $in: ids
  }
});

Simple and clean code. It works and tested against:

"mongodb": "^3.6.0",
"mongoose": "^5.10.0",
Tyler2P
  • 2,324
  • 26
  • 22
  • 31
Ahmad Agbaryah
  • 487
  • 5
  • 5
13

Use this format of querying

let arr = _categories.map(ele => new mongoose.Types.ObjectId(ele.id));

Item.find({ vendorId: mongoose.Types.ObjectId(_vendorId) , status:'Active'})
  .where('category')
  .in(arr)
  .exec();
long.luc
  • 1,191
  • 1
  • 10
  • 30
Derese Getachew
  • 440
  • 6
  • 10
10

This code works for me just fine as of mongoDB v4.2 and mongoose 5.9.9:

const Ids = ['id1','id2','id3']
const results = await Model.find({ _id: Ids})

and the Ids can be of type ObjectId or String

fafa.mnzm
  • 561
  • 7
  • 17
6

Both node.js and MongoChef force me to convert to ObjectId. This is what I use to grab a list of users from the DB and fetch a few properties. Mind the type conversion on line 8.

// this will complement the list with userName and userPhotoUrl 
// based on userId field in each item
augmentUserInfo = function(list, callback) {
    var userIds = [];
    var users = [];         // shortcut to find them faster afterwards

    for (l in list) {       // first build the search array
        var o = list[l];

        if (o.userId) {
            userIds.push(new mongoose.Types.ObjectId(o.userId)); // for Mongo query
            users[o.userId] = o; // to find the user quickly afterwards
        }
    }

    db.collection("users").find({
        _id: {
            $in: userIds
        }
    }).each(function(err, user) {
        if (err) {
            callback(err, list);
        } else {
            if (user && user._id) {
                users[user._id].userName = user.fName;
                users[user._id].userPhotoUrl = user.userPhotoUrl;
            } else { // end of list
                callback(null, list);
            }
        }
    });
}
Mr.Singh
  • 1,421
  • 6
  • 21
  • 46
Nico
  • 4,248
  • 1
  • 20
  • 19
6

if you are using the async-await syntax you can use

const allPerformanceIds = ["id1", "id2", "id3"];
const findPerformances = await Performance.find({ 
    _id: { 
        $in: allPerformanceIds 
    } 
});           
Mr.Singh
  • 1,421
  • 6
  • 21
  • 46
MD SHAYON
  • 7,001
  • 45
  • 38
4

I tried like below and it works for me.

var array_ids = [1, 2, 6, 9]; // your array of ids

model.find({ 
    '_id': { 
        $in: array_ids 
    }
}).toArray(function(err, data) {
    if (err) {
        logger.winston.error(err);
    } else {
        console.log("data", data);
    }
});
Chris
  • 4,009
  • 3
  • 21
  • 52
-1

I am using this query to find the files in mongo GridFs. I wanted to get the by its Ids.

For me this solution is working: Ids type of ObjectId.

gfs.files
.find({ _id: mongoose.Types.ObjectId('618d1c8176b8df2f99f23ccb') })
.toArray((err, files) => {
  if (!files || files.length === 0) {
    return res.json('no file exist');
  }
  return res.json(files);
  next();
});

This is not working: Id type of string

gfs.files
.find({ _id: '618d1c8176b8df2f99f23ccb' })
.toArray((err, files) => {
  if (!files || files.length === 0) {
    return res.json('no file exist');
  }
  return res.json(files);
  next();
});