2

So I have this Mongoose Schema:

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var CommentSchema = new Schema({
  body: {type: String, required: true, max: 2000},
  created: { type: Date, default: Date.now },
  flags: {type: Number, default: 0},
  lastFlag: {type: Date, default: Date.now()},
  imageBanned: {type: Boolean, default: false},
  fileName: {type: String, default: ""}
}, {
  writeConcern: {
    w: 0,
    j: false,
    wtimeout: 200
  }  
});

var PostSchema = new Schema({
  body: {type: String, required: true, max: 2000},
  created: { type: Date, default: Date.now },
  flags: {type: Number, default: 0},
  lastFlag: {type: Date, default: Date.now()},
  fileName: {type: String, default: ""},
  imageBanned: {type: Boolean, default: false},
  board: {type: String, default: ""},
  comments: [{ type: Schema.Types.ObjectId, ref: 'Comment' }]
}, {
  writeConcern: {
    w: 0,
    j: false,
    wtimeout: 200
  }  
});


var Post =  mongoose.model('Post', PostSchema);
var Comment = mongoose.model('Comment', CommentSchema)

module.exports = {Post, Comment}

And I'm trying to query a Comment inside the comment array in post.

This is the endpoint I'm trying:

router.post('/flagComment', (req, res, next)=>{
  console.log('inside /flagComment')
  console.log('value of req.body: ', req.body)
  model.Post.findOne({"comments._id": req.body.id}).exec((err, doc)=>{
    if(err){
      console.log('there was an error: ', err)
    }
    console.log('the value of the found doc: ', doc)
    res.json({dummy: 'dummy'})
  })
})

However, this gives the following terminal output:

value of req.body:  { id: '5c9bd902bda8d371d5c808dc' }
the value of the found doc:  null

That's not correct...I've verified the ID is correct - why is the comment doc not being found?

EDIT:

I've attempted this solution (Can't find documents searching by ObjectId using Mongoose) by setting the objectID like this:

var ObjectId = require('mongoose').Types.ObjectId; 

router.post('/flagComment', (req, res, next)=>{
  console.log('inside /flagComment')
  console.log('value of req.body: ', req.body)
  console.log('value of objid req id : ',  ObjectId(req.body.id))
  model.Post.find({"comments._id": ObjectId(req.body.id)}).exec((err, doc)=>{
    if(err){
      console.log('there was an error: ', err)
    }
    console.log('the value of the found doc: ', doc)
    res.json({dummy: 'dummy'})
  })
})

And I get the following terminal output:

value of req.body:  { id: '5c9bd902bda8d371d5c808dc' }
value of objid req id :  5c9bd902bda8d371d5c808dc
the value of the found doc:  []

So, this is not yet a solution although it appears to be better than what I had.

Peter Weyand
  • 2,159
  • 9
  • 40
  • 72
  • Possible duplicate of [Mongoose String to ObjectID](https://stackoverflow.com/questions/38446346/mongoose-string-to-objectid) – amcgregor Mar 28 '19 at 17:11
  • …or [Mongoose saved _id's as string instead of ObjectId](https://stackoverflow.com/questions/32619699/mongoose-saved-idss-as-a-string-instead-of-objectid) or [Mongoose sometimes save ObjectId as string…](https://stackoverflow.com/questions/54437890/mongoose-sometimes-save-objectid-as-string-some-times-no) — while these relate to the data storage side of the problem, every single one features the solution. – amcgregor Mar 28 '19 at 17:22

2 Answers2

2

You aren't querying for an ObjectId, no matter how much you think you are. You are querying for the ObjectId encoded as a hexidecial string, which is not the same thing. Properly typecast and you will likely have far more success.

Edited to elaborate, from a mongo (JS) REPL shell:

> // Omitting the _id, or generating a new one, are equivalent during insert.
> db.foo.insert({_id: ObjectId()})
WriteResult({ "nInserted" : 1 })

> db.foo.find()  // As expected, we get back our _real_ ObjectId value.
{ "_id" : ObjectId("5c9cfab873724727778c0730") }

> // Can we "insert the record again" using a string version of the ID?
> db.foo.insert({_id: "5c9cfab873724727778c0730"})
WriteResult({ "nInserted" : 1 })  // Sure as heck can! No unique violation!

> db.foo.find()  // Because THESE ARE NOT THE SAME
{ "_id" : ObjectId("5c9cfab873724727778c0730") }
{ "_id" : "5c9cfab873724727778c0730" }

After our IRC discussion, there seems to be difficulty in understanding the "searchable terms" in the answers you are being given. Search here on StackOverflow (or Google, or DDG) for "mongoose typecast ObjectId" (without quotes; or just "mongoose ObjectId"…) and you will find many answers, as this is a particularly common problem for Mongoose users.

amcgregor
  • 1,228
  • 12
  • 29
  • Can you provide a code example? I'm not sure I understand. – Peter Weyand Mar 27 '19 at 20:44
  • @PeterWeyand I do not Mongoose, and I do not JS. The answer is clear: typecast the value to a real `ObjectId` before attempting to query using the value. A hex-encoded string != the binary value, the same as a base64-encoded string != the original binary value. – amcgregor Mar 27 '19 at 20:45
  • Alright, well thanks for your help, but this answer is not yet enough to get me to a solution. Thank you. – Peter Weyand Mar 27 '19 at 20:47
  • @PeterWeyand For giggles, I did update with some code from a `mongo` REPL shell, which **is JS**. This code demonstrates the exact problem you are encountering from the perspective of using the unique index on `_id` to prove dissimilarity and inability/inappropriateness of comparison. The answer given ("properly [typecast](https://en.wikipedia.org/wiki/Type_conversion)") stands. – amcgregor Mar 28 '19 at 16:59
0

Once try checking with populating the reference data first and then querying on the _id of the comment.

*Get the post id also from front end in addition to comment id, in that way it will be easier.

Post.findOne({_id:'postId'})
.populate({
  path  : 'comment',
  match : { _id : 'commentId' }
})
.exec(...);
user1361425
  • 97
  • 2
  • 7
  • Thanks...I mean this works, but...it feels like a backdoor hack a little bit. Why can't I just query the comment by id directly? I think doing it this way requires A) obv. passing another id and B) I think searching Post when I don't have to which is another query. – Peter Weyand Mar 27 '19 at 21:14
  • if you check the database, mongodb doesn't save the data in comment field like you are searching "comments._id", It save in the format something like { "$ref" : "comment", "$id" : ObjectId("commentid"), "$db" : "dbname" } – user1361425 Mar 27 '19 at 21:16
  • I don't believe that is correct. Here is the terminal output of a post: `{ flags: 0, lastFlag: 2019-03-27T21:00:54.316Z, fileName: '1553720454192&&hotdog.png', imageBanned: false, board: 'nsfw', comments: [ { flags: 0, lastFlag: 2019-03-27T21:01:03.540Z, imageBanned: false, fileName: '1553720463408&&hotdog.png', _id: 5c9be48f0556ec5397cb62a2, body: 'dadf', created: 2019-03-27T21:01:03.540Z, __v: 0 } ], _id: 5c9be4860556ec5397cb62a1, body: 'dog', created: 2019-03-27T21:11:15.575Z, __v: 0 }` – Peter Weyand Mar 27 '19 at 21:21
  • If your question has been answered, please make sure to accept answer for further references. – user1361425 Mar 27 '19 at 21:28
  • It has not yet been answered - please refer to my comments, thank you. – Peter Weyand Mar 27 '19 at 21:29
  • The output you provided is fetched from mongoose driver or mongo-shell? Here is the more info about how mongodb saves DBRef in fields https://docs.mongodb.com/manual/reference/database-references/#dbrefs – user1361425 Mar 27 '19 at 21:36
  • try searching modifying the query - Post.find({"comments.$_id": ObjectId(req.body.id)}), let me know if that works for you. – user1361425 Mar 27 '19 at 21:46
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/190798/discussion-between-user1361425-and-peter-weyand). – user1361425 Mar 27 '19 at 21:48