13

I have this item in mongo:

[ 
  { 
    title: 'Product Name',
    _id: 5052843e023273693300013c,
    description: 'This is a fake description',
    categories: [ 5052843e023273693300010a ],
  }
]

I want to find products like this that have this category. I have tried:

Product.find({ categories:  mongoose.Types.ObjectId('5052843e023273693300010a')})
Product.find({ categories:  mongoose.mongo.BSONPure.ObjectID.fromString('5052843e023273693300010a')})
Product.find({ categories:  '5052843e023273693300010a'})
Product.find({ 'categories':  '5052843e023273693300010a'})
Product.find({ categories:  {$in: ['5052843e023273693300010a']}})
Product.find({ categories:  Schema.Types.ObjectId('5052843e023273693300010a')})

But nothing works. I can fetch by id just fine using: _id: '5052843e023273693300013c'.

Note that when the products were inserted the category ID was added as a string (meaning I just assigned the ID instead of the category objects but that doesn't explain why none of the above work - it's unquoted in the dump so perhaps Mongo recognizes as an object ID.

Similar questions on SO did not yield an answer.

I am using the latest Mongoose (3 something) and recent Mongo, Node.

Update:

I can fetch just fine from CLI using:

db.products.find({ categories: '5052843e02327369330000fe' }); 

and interestingly I can fetch it by doing the not equal in my code - huh?:

Product.find({ categories: { $ne: '5052843e02327369330000fe' }})

My schema is as follows:

var Product = new Schema({
    title: { type: String, required: true },
    slug: { type: String },
    summary: { type: String }, //browser title
    description: { type: String, required: false },
    excerpt: { type: String },    //for a list and also for the meta description
    publish: { type: Boolean },
    featured: { type: Boolean },
    unavailable: { type: Boolean },
    model: { type: String },
    google: { type: String },
    tags: { type: Array },
    categories:  [{ type: Schema.Types.ObjectId, ref: 'Category' }],
    manufacturer: { type: String },
    variations: { type: Array },
    prices: { type: Array },
    images: { type: Array },
    specs: { type: Array },
    modified: { type: Date, default: Date.now }
});
    
var Category = new Schema({
    title: { type: String, required: true },
    description: { type: String },
    parent: { type: Schema.Types.ObjectId, ref: 'Category' },
    images: { type: Array }
});

Thanks

MORÈ
  • 2,480
  • 3
  • 16
  • 23
cyberwombat
  • 38,105
  • 35
  • 175
  • 251

2 Answers2

17

With Mongoose you have to force cast your string as an ID in some cases. Usually it automatically does this for you, but in your specific case, it doesn't. The following snippet of code will get all connections that contain the passed ID.

var mongoose = require('mongoose');
Node.find({ connections: mongoose.Types.ObjectId("535c5c1b8aa6dc5f021e8a98") }, function (err, results) { 
    console.log(results); 
});
Ash Blue
  • 5,344
  • 5
  • 30
  • 36
  • 2
    mongoose.Types.ObjectId() is exactly what I want! – Ornithopter Oct 18 '14 at 03:57
  • I'm starting to run into this more and more and am having trouble pinning down exactly when it happens and when it doesn't. I'm not sure if I'm running into this in a Mongoose .update with a query with { _id: { $in: [ … ] } }. What are the known cases where it happens. Anyone know why it happens? – Paul Shapiro Jul 31 '16 at 17:43
  • i imported mongoose using `import * as mongoose from 'mongoose'; ` – Shailesh Bhokare Nov 10 '20 at 19:09
0

What's happening is that Mongoose is casting whatever value you're using for a categories value in your Product.find call to an ObjectId as that's how categories is defined in the schema. But the document's categories value you're trying to match has a string type instead of an ObjectId so it's not matching.

To get things to work again you'll need to clean up your existing documents to match your defined schema.

JohnnyHK
  • 305,182
  • 66
  • 621
  • 471
  • How would my document have a string type? Doesn't the schema make sure that when I create my document (something like product.categories = req.params.categories in an expresss form) it turns my provided string into an object ID? Surely I shouldn't have to cast it on creation? – cyberwombat Sep 18 '12 at 00:14
  • @Yashua I thought that's what you were saying here: `when the products were inserted the category ID were added as a string`. If you've always had the schema indicating an array of ObjectIds and you've done all your inserts through Mongoose then they should already be ObjectIds in the collection. – JohnnyHK Sep 18 '12 at 00:18
  • @Yashua From your update, if `db.products.find({ categories: '5052843e02327369330000fe' });` returns your document in the CLI, then `categories` **does** contain a string and not an ObjectId in this case. – JohnnyHK Sep 18 '12 at 00:23
  • I guess what I am asking is how to insert these ID from a form as an object ID when all I have is the ID and not the related object (for example my categories get selected from a drop down in the product edit form). That's what I mean by inserted as a string. What I am finding now is that sometimes my query works which definitely seems to indicate a fluctuation in how things are stored - just not sure how to guarantee the proper insert. I do something such as var p = new Product; p.categories = req.params.categories (an post array of cat IDs) and then p.save. – cyberwombat Sep 18 '12 at 02:09
  • @Yashua Setting `p.categories` to an array of string representations of ObjectIds and then calling `p.save()` should work fine. I just ran a test and the values saved in `categories` were ObjectIds, not strings. – JohnnyHK Sep 18 '12 at 03:26