40

Is there something that I'm missing that would allow item to log as an object with a parameter, but when I try to access that parameter, it's undefined?

What I've tried so far:

  • console.log(item) => { title: "foo", content: "bar" } , that's fine
  • console.log(typeof item) => object
  • console.log(item.title) => "undefined"

I'll include some of the context just in case it's relevant to the problem.

var TextController = function(myCollection) {
  this.myCollection = myCollection
}

TextController.prototype.list = function(req, res, next) {
  this.myCollection.find({}).exec(function(err, doc) {
    var set = new Set([])
    doc.forEach(function(item) {
      console.log(item)         // Here item shows the parameter
      console.log(item.title)   // "undefined"
      set.add(item.title)       
    })
    res.json(set.get());
  })
}

Based on suggestion I dropped debugger before this line to check what item actually is via the node repl debugger. This is what I found : http://hastebin.com/qatireweni.sm

From this I tried console.log(item._doc.title) and it works just fine.. So, this seems more like a mongoose question now than anything.

There are questions similar to this, but they seem to be related to 'this' accessing of objects or they're trying to get the object outside the scope of the function. In this case, I don't think I'm doing either of those, but inform me if I'm wrong. Thanks

0

12 Answers 12

57

Solution

You can call the toObject method in order to access the fields. For example:

var itemObject = item.toObject();
console.log(itemObject.title); // "foo"

Why

As you point out that the real fields are stored in the _doc field of the document.

But why console.log(item) => { title: "foo", content: "bar" }?

From the source code of mongoose(document.js), we can find that the toString method of Document call the toObject method. So console.log will show fields 'correctly'. The source code is shown below:

var inspect = require('util').inspect;

...

/**
 * Helper for console.log
 *
 * @api public
 */
Document.prototype.inspect = function(options) {
  var isPOJO = options &&
    utils.getFunctionName(options.constructor) === 'Object';
  var opts;
  if (isPOJO) {
    opts = options;
  } else if (this.schema.options.toObject) {
    opts = clone(this.schema.options.toObject);
  } else {
    opts = {};
  }
  opts.minimize = false;
  opts.retainKeyOrder = true;
  return this.toObject(opts);
};

/**
 * Helper for console.log
 *
 * @api public
 * @method toString
 */

Document.prototype.toString = function() {
  return inspect(this.inspect());
};
Sign up to request clarification or add additional context in comments.

4 Comments

Didn't work for me on Mongo 3.2. Says it doesn't have toObject method.
@GeorgeRappel By referring the History of Automattic/mongoose, the toObject was added at version 3.4.0. The latest version of mongoose is 4.5.2. I think you need to update your mongoose module...
I have moongose version 4.8.4 and I got the same message error: "TypeError: user.toObject is not a function"
If i do this toObject can I use the obj (foo) as a mongoose object normally e.g. foo.save() ? I soved this problem using the .lean() when querying mongoose
16

Make sure that you have defined title in your schema:

var MyCollectionSchema = new mongoose.Schema({
    _id: String,
    title: String
});

1 Comment

This was my problem!!! The document was correct in the database, but because the schema was incorrect it would not display the variable even though it did print the whole object correctly.
4

Try performing a for in loop over item and see if you can access values.

for (var k in item) {
    console.log(item[k]);
}

If it works, it would mean your keys have some non-printable characters or something like this.

From what you said in the comments, it looks like somehow item is an instance of a String primitive wrapper.

E.g.

var s = new String('test');
typeof s; //object
s instanceof String; //true

To verify this theory, try this:

eval('(' + item + ')').title;

It could also be that item is an object that has a toString method that displays what you see.

EDIT: To identify these issues quickly, you can use console.dir instead of console.log, since it display an interactive list of the object properties. You can also but a breakpoint and add a watch.

8 Comments

Yes! sort of. Except when I do this, it returns the object as a string. ex "{ title: 'foo'\n content: 'bar'}"
You mean console.log(item[k]) displays what you said?
it displays the item as a string, which makes the parameters of the object inaccessible. I've updated the question to describe better what I know about what's happening.
@tippenein, I updated the answer so that you understand the result you were getting. I expect that the object had a custom toString function for printing itself. That would explain the confusion.
I appreciate the explanations, but judging by my recent finds, item._doc.title returns what I expect. This confirms that it's an object but an object that somehow doesn't print out what it actually contains.
|
4

Use findOne() instead of find().

The find() method returns an array of values, even if you have only one possible result, you'll need to use item[0] to get it.

The findOne method returns one object or none, then you'll be able to access its properties with no issues.

1 Comment

This was it, thank you!!! Missed to notice I was dealing with an array!
3

Old question, but since I had a problem with this too, I'll answer it.
This probably happened because you're using find() instead of findOne(). So in the end, you're calling a method for an array of documents instead of a document, resulting in finding an array and not a single document. Using findOne() will let you get access the object normally.

Comments

3

A better way to tackle an issue like this is using doc.toObject() like this

doc.toObject({ getters: true })

other options include:

  • getters: apply all getters (path and virtual getters)
  • virtuals: apply virtual getters (can override getters option)
  • minimize: remove empty objects (defaults to true)
  • transform: a transform function to apply to the resulting document before returning
  • depopulate: depopulate any populated paths, replacing them with their original refs (defaults to false)
  • versionKey: whether to include the version key (defaults to true)

so for example you can say

Model.findOne().exec((err, doc) => {
   if (!err) {
      doc.toObject({ getters: true })
      console.log('doc _id:', doc._id) // or title
   }
})

and now it will work

Comments

2

You don't have whitespace or funny characters in ' title', do you? They can be defined if you've quoted identifiers into the object/map definition. For example:

var problem = {
    ' title': 'Foo',
    'content': 'Bar'
};

That might cause console.log(item) to display similar to what you're expecting, but cause your undefined problem when you access the title property without it's preceding space.

1 Comment

doc is the results of the mongo db query, so I just iterate through those objects. I did try item[' title'] and item['title '] though. No luck
2

I think using 'find' method returns an array of Documents.I tried this and I was able to print the title

for (var i = 0; i < doc.length; i++) {
   console.log("iteration " + i);
   console.log('ID:' + docs[i]._id);
   console.log(docs[i].title);
}

Comments

2

If you only want to get the info without all mongoose benefits, save i.e., you can use .lean() in your query. It will get your info quicker and you'll can use it as an object directly.

https://mongoosejs.com/docs/api.html#query_Query-lean

As says in docs, this is the best to read-only scenarios.

Comments

0

Are you initializing your object?

function MyObject()
{
    this.Title = "";
    this.Content = "";
}

var myo1 = new MyObject();

If you do not initialize or have not set a title. You will get undefined.

2 Comments

The object is returned by the mongoose collection.find({}), but perhaps you're on to something. Maybe there is something wrong with my initializing of TextController
I recommend using chrome or firefox dev tools to place a break point so you can inspect that your object looks like.
0

When you make tue query, use .lean() E.g

const order = await Order.findId("84578437").lean()

Comments

0

find returns an array of object , so to access element use indexing, like doc[0].title

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.