Cannot access properties on a populate()'d object from Mongoose? - node.js

This is very odd... I'm using populate() with a ref to fill in an array within my schema, but then the properties are inaccessible. In other words, the schema is like this:
new Model('User',{
'name': String,
'installations': [ {type: String, ref: 'Installations'} ],
'count': Number,
}
Of course, Insallations is another model.
Then I find & populate a set of users...
model.find({count: 0}).populate('installations').exec( function(e, d){
for(var k in d)
{
var user = d[k];
for(var i in user.installations)
{
console.log(user.installations[i]);
}
}
} );
So far so good! I see nice data printed out, like this:
{ runs: 49,
hardware: 'macbookpro10,1/x86_64',
mode: 'debug',
version: '0.1' }
However, if I try to actually ACCESS any of those properties, they're all undefined! For example, if I add another console log:
console.log(user.installations[i].mode);
Then I see "undefined" printed for this log.
If I try to operate on the object, like this:
Object.keys(user.installations[i]).forEach(function(key) { } );
Then I get a typical "[TypeError: Object.keys called on non-object]" error, indicating that user.installations[i] is not an object (even though it is outputted to the console as if it were). So, I even tried something ugly like...
var install = JSON.parse(JSON.stringify(user.installations[i]));
console.log(install, install.mode);
And, again, the first output (install) is a nice object containing the property 'mode'... but the 2nd output is undefined.
What gives?

Finally, I solved this...
I tried doing a console.log(typeof user.installations[i]); and got "string" as the output. This seemed odd, given that printing the object directly created console output (above) that looked like a normal object, not a string. So, I tried doing a JSON.parse(); on the object, but received the error "SyntaxError: Unexpected token r"
Finally, I realized what was going on. The "pretty console output" I described above was the result of a string formatted with \n (newlines). I had not expected that, for whatever reason. The JSON.parse() error is due to the fact that there is a known necessity with the node.js parser when attempting to parse object keys without quotations; see the SO question here:
Why does JSON.parse('{"key" : "value"}') do just fine but JSON.parse('{key : "value"}') doesn't? .
Specifically, note that the JSON parser in my case is failing on the character 'r', the fist character of "runs," which is the first key in my JSON string (above). At first I was afraid I needed to get a custom JSON parser, but then the problem hit me.
Look back to my original schema. I used a String-type to define the installation ref, because the array field was storing the installations's _id property as a String. I assume the .populate() field is converting the object to a String on output.
Finally, I looked at the Mongoose docs a little closer and realized I'm supposed to be referencing the objects based upon Schema.ObjectID. This explains everything, but certainly gives me some fixing to do in my schemas and code elsewhere...

Related

console.log is omitting some key pairs? [duplicate]

This question already has answers here:
mongoose .find() method returns object with unwanted properties
(5 answers)
Closed 5 years ago.
Working with a strange problem here. This is an array of objects which is pulled from mongodb and passed into the following function.
I tried the following 3 logs sequentially within the forEach on the array pulled from the database:
e (the object element within the array) which returns correctly. as you can see all the properties (keys) exist:
{ paid: false,
hotelWebsite: 'www.testing.com',
_id:5951848a24bb261eed09d638,
hotelAddress: '123 easy street',
...etc }
console.log(Object.keys(e)) is returning things that are not the keys...
[ '__parentArray',
'__parent',
'__index',
'$__',
'isNew',
'errors',
'_doc',
'$init' ]
and finally:
for(key in e){
console.log(key);
}
which returns an absolute mess of data, part of which DOES contain the actual keys of the object:
__parentArray
__parent
__index
$__
isNew
errors
_doc
$init
id
_id
hotelWebsite
hotelAddress
hotelNumber
hotelName
courseCost
courseDate
courseState
courseCity
courseName
paid
studentComments
studentEmail
studentPhone
studentCountry
studentZip
studentState
studentCity
studentAddress
studentCompany
studentName
schema
constructor
$__original_remove
remove
_pres
_posts
$__original_validate
validate
toBSON
markModified
populate
save
update
inspect
invalidate
$markValid
$isValid
ownerDocument
$__fullPath
parent
parentArray
on
once
emit
listeners
removeListener
setMaxListeners
removeAllListeners
addListener
$__buildDoc
init
$hook
$pre
$post
removePre
removePost
_lazySetupHooks
set
$__shouldModify
$__set
getValue
setValue
get
$__path
unmarkModified
$ignore
modifiedPaths
isModified
$isDefault
isDirectModified
isInit
isSelected
isDirectSelected
$__validate
validateSync
$__reset
$__dirty
$__setSchema
$__getArrayPathsToValidate
$__getAllSubdocs
$__handleReject
$toObject
toObject
toJSON
toString
equals
execPopulate
populated
depopulate
And a relevant sample of the code if needed:
studentsArray.forEach( (e, i) => {
if(task === 'nameTag'){
console.log(e);
console.log(Object.keys(e));
for(k in e){
console.log(k);
}
}
....
I need access to the properties (keys) for further processing within the forEach function. I am very confused on what is causing this and have never run into this sort of issue before. For the record the objects exist, using a console.log(typeof e) it IS an object (not a data "string"). I can access the properties using the dot or bracket notation but NOT using Object.keys() or for (keys in obj).
Can anyone help me sort this out please?
for ... in iterates all enumerable properties, both own and inherited. This is not "a strange bug," this is in fact the intended behavior.
As for the Object.keys(), unless it was overwritten by a non-compliant implementation, those are in fact enumerable keys of the object itself, so you are most likely mistaken. The e object has a .toJSON() method in its prototype that is implicitly called when you do console.log(e), so that is probably the output you are seeing there, and is not likely going to reflect exactly the same property keys as the original object. Try calling console.log(e.toJSON()) and I'm guessing it will be the same output as in the first one.
If you want only the object's own properties, use Object.getOwnPropertyNames(e).
If you want the keys printed in the first output, then use Object.keys(e.toJSON()).

Firestore finds undefined value in fully defined JSON, key does not even exist

I am trying to upload a JSON-object to Google Firestore.
When setting the Object to a Firestore Document my code throws the following Error:
Error: Value for argument "data" is not a valid Firestore document.
Cannot use "undefined" as a Firestore value
(found in field audit.`20`.requests.`0`.lrEndTimeDeltaMs).
Now the first thing I did was log the Object right before the upload to check for undefined values:
console.log(JSON.stringify(resultsToUpload));
return database
.collection("foo1")
.doc("bar1")
.set(resultsToUpload);
Not only are all values defined in the object, but the mentioned field audit.`20`.requests.`0`.lrEndTimeDeltaMs does not even exist:
resultsToUpload = {
"audit":{
"20":{
"ronaldScore":3,
"id":"network-requests",
"requests":[
{
"url":"https://www.example.com/",
"startTime":0,
"endTime":62.16699999640696,
"transferSize":15794,
"resourceSize":78243,
"statusCode":200,
"mimeType":"text/html",
"resourceType":"Document"
}
]
}
}
}
The data comes from a Google Lighthouse Audit.
Calculating the UTF-8 string length of the stringified JSON objects results in a size of 30 MB.
1) All values are defined (some are null, which should not be a problem).
2) The mentioned field does not even exist in the JSON.
My question is: How can this happen? How can a field just appear?
Also: How would I fix this issue?
using JSON.stringify on a Javascript-Object hides all "undefined" values (as well as their keys) and then stringifies what is left.
That is because JSON does not have such a thing as "undefined".
So the log showed a fully defined JSON, even though the actual JS-Object did contain "undefined" values.

how to treat special characters in variable names

I have a json which has the a particular format, I wanted to extract data from it, but it contains ':' in its key value which is giving error while printing
metadata:
{
'A:B':'string'
}
I have tried to take it in another varibale stills it gives error:
text='A'+':'+'B';
console.log(metadata.text);
//console.log(metadata.A:B);
You can simply use jsonVariableName["ObjectName"] instead of sonVariableName.ObjectName
var metadata = {
'A:B':'string'
};
console.log(metadata["A:B"]);
Also I found you mention code as:
metadata:{...}
Which I think will be:
metadata = {...}

How to add multiline painless code in nodejs

So in my js-code I have this line:
var _script = {
_script: {
script: {
lang: 'painless',
source: `
"""
if(1>2){
params._source.id;
}
else{
params._source.id;
}
"""
`
},
type: 'string',
order: params._source.id
}
}
This will fail. I see in the log this error message:
,\"reason\":\"unexpected token ['\\\"\\\\n if(1>2){\\\\n params._source.id;\\\\n }\\\\n else{\\\\n params._source.id;\\\\n }\\\\n \\\"'] was expecting one of [{<EOF>, ';'}].\"}}}]},
I have tried first to have without tilde-character. And then it also fails.
I then tried to have tilde at the beginning, something like:
var _script = `{
Thing is that the final json that will be sent to elastic is not shown in the code above. So "_script" is only a little part of all the json.
I was wondering if I added the tilde at the very beginning and end of the whole json. Maybe it could work? I need to work it out where it is.
But just in theory: do you think the problem is there? Putting the tilde around all the json? Or is it something else?
The triple " is not valid JSON, it only works internally to the Elastic stack (i.e. from Kibana Dev Tools to ES).
The way I usually do it from Node.js is to add each line to an array and then I join that array, like this:
const code = [];
code.push("if(1>2){");
code.push("params._source.id;");
code.push("} else {");
code.push("params._source.id;");
code.push("}");
source = code.join(" ");
It's not super legible, I admit. Another way is to use stored scripts so you can simple reference your script by ID in Node.js.

Query value gets quoted automatically before sending it to MongoDB?

The following is confusing me a lot. I have been spending quite a bit of time trying to understand why collection.find() doesn't work with regex passed as an object. The regex match is coming over HTTP wrapped in the body of a POST request. Then I try to gather the query (in string format) and perform the query. The problem seems to be that unless the regex is written inside Node without quotes, it won't work. That is, it must be a literal without quotes.
For example, the following works fine:
var query1 = {
company: {
'$regex': /goog/
}
};
collection.find(query1, {}).toArray(function (err, docs) {
// Got results back. Awesome.
});
However, if the data comes wrapped in an object, it doesn't return anything. I suspect it's because the value gets quoted behind the scenes (i.e. "/goog/"):
// Assume
var query2 = {
company: {
'$regex': query.company
}
};
collection.find(query2, {}).toArray(function (err, docs) {
// Got nothing back.
});
I have tested it with the mongo shell and I can confirm the following:
// Returns 5 results
db.getCollection("contacts").find( { "company": /goog/ } )
// Doesn't match anything
db.getCollection("contacts").find( { "company": "/goog/" } )
Furthermore, I just discovered the following: if I write the value with quotes
// Works fine
var companyRegex = {'$regex': /goog/};
var query3 = {
company: companyRegex
};
So technically, a "literal" regex without quotes wrapped in an object works fine. But if it's a string, it won't work. Even after trying to replace the double-quotes and single-quotes with nothing (i.e. essentially removing them.)
Any idea how can I get the regex match be passed verbatim to find()? I've researched it, finding lots of potential solutions, alas it's not working for me.
Thanks in advance!
Let me focus on one line of your post. This is where the problem might be:
The regex match is coming over HTTP wrapped in the body of a POST request.
This seems problematic because:
The only structures that survive serialization between client/server are:
boolean
number
string
null *
objects and arrays containing these basic types
objects and arrays containing object and arrays [of more obj/array] of these basic types
Regexp, Date, Function, and a host of others require reconstruction, which means
passing a string or pair of strings for the match and option components of the Regexp and running Regexp() on the receiving end to reconstruct.
Regexp gets a bit messy because Regexp.toString() and Regexp() do not appear to be inverses of each others: /someMatch/.toString() is "/someMatch/" but RegExp("/someMatch/") is //someMatch// and what was needed instead to rebuild the regexp was just RegExp("someMatch"), which is /someMatch/. I hope this helps.
JSON.stringify(/someMatch/) is {} (at least on Chrome).
So instead of trying to build a general transport, I recommend re instantiating a particular field as a regexp.
* Irrelevant note: (null is fine but undefined is peculiar. JSON won't stringify undefineds in objects and turns undefined into null in Arrays. I recognize this isn't part of your problem, just trying to be complete in describing what can be serialized.)

Resources