Mongoose returning Array value as "undefined" [duplicate] - node.js

This question already has answers here:
Can't access object property of a Mongoose response
(4 answers)
Closed 2 years ago.
About ready to frisbee my laptop into the drywall here.
I have a Mongo document containing several different objects and arrays. The main ones of interest are the categories array of objects and the bypass array of Strings.
I'm using model.findOne() to obtain the document with success. The value of the document is stored in the res variable and console.log(res) shows the correct values without errors. I can even SEE the contents of the categories and bypass elements.
When I assign res.categories to a variable, no errors occur. Hooray, the basics of assignment are working.
When I assign res.bypass to a variable (immediately after the above assignment), the variable value is undefined even though I can see the contents of res.bypass containing an Array of Strings as designed.
The structure of res.bypass is bypass: [ '123', '456', '78', '90' ]. Assigning that value to a variable sets it as undefined.
Am I missing something here? Am I not allowed to take a single document and assign its different elements to different variables?
EDIT 1:
Code Snippet Request:
await Api.findOne({ id: '1' }).then(async (doc) => {
console.log(doc.categories) // Categories Array of Objects
console.log(doc.bypass) // Bypass Array of Strings
const categories = doc.categories // Value is categories Array of Objects
const bypass = doc.bypass // Value is undefined
...
})

There's no need to wrap the promise callback in an async function, nor to use a then handler on an awaited promise. You should change it to:
const doc = await Api.findOne({ id: '1' });
console.log(doc.categories) // Categories Array of Objects
console.log(doc.bypass) // Bypass Array of Strings
const categories = doc.categories // Value is categories Array of Objects
const bypass = doc.bypass // Value is undefined

Related

Why can't I pass an object to `navigation.navigate()`?

I'm using the UI Kitten library. I can store routes in an array and pass an array index to navigate()
Here's what I'm doing. I store my routes in an array like this:
const routeNames = [
'Customers',
'Employees',
]
And then I reference them on a navigation tab:
<BottomNavigation
selectedIndex={state.index}
onSelect={onSelect}
>
<BottomNavigationTabItem
title={routeNames[0]}
/>
...
And handle on select:
const onSelect = index => {
navigation.navigate(routeNames[index]);
};
But I would rather reference their name for better readability like this:
<BottomNavigationTabItem
title={routeNames.Customer}
/>
And therefore store the route names in an object:
const routeNames = {
CustomerList: 'Customers',
EmployeeList: 'Employees',
}
But doing it this way with the object gives me an error:
You need to specify name or key when calling navigate with an object
as the argument
I feel like it shouldn't matter as long as I pass a string for the route name.
UPDATE: It looks like the docs say a number type is required for onSelect ... but is the array index not referencing a string?
What do I not understand about how array references work?

mongoose - select specific fields in Model.create

const generatedEvent = await Event.create(req.body);
res.send(generatedEvent);
I am getting some data from the request body and I can generate a new Event. And I'm returning the event to client when it has generated. But I don't want to return all fields with event. I want to make filter operation like how we are using select function like this: Event.find().select({title:1,description:1})
How can i use this select func with Model.create?
If you take a look at the mongoose-source code, you can see that Model.create returns a promise with the created/inserted documents. There's no way to specify a filtering-options to return only specific fields.
Of course you could do a .find() in combination with a .select() call after creating/inserting a new record but that would result in one extra DB-query for each insert which does not make a lot of sense.
You could instead just return the desired properties from the returned document, since you know that a new document was inserted successfully with the provided data, when the promise resolved. So you could simply do:
res.send({title: generatedEvent.title, description: generatedEvent.description});
Model.create() internally doesn't fetch the document from the database, rather it actually returns the result whether it's inserted successfully or not. If successful, mongoose will return the original mongoose document that mongoose created before sending to the database.
So you could just select the fields by yourself. Using es2015 Object destructuring assignment and Object shorthand property names would help writing more concise code.
const { title, description } = await Event.create(req.body); // Object destructuring
res.send({ title, description }); // Object shorthand property names

Mongoose - Check if each key of .findOne() doc is a ObjectId (referenced object)

I got a generic GET function that worps for my entire app, considering I am using just absolute documents.
Now I get to a point that I need some properties of some of my documents reference others, and when executed, the GET function populate them (obviously). For that, I need to require the referenced schema, and populate with referenced model.
The point is: I want to my GET function stay generic, so I don't want to reference any of my schemas, unless it is needed. The same goes for the .populate() method.
To achieve that, I am iterating through each key of the resulting object of the .findOne() method, and trying to check if each specific key, is or is not a ObjectId/reference or not. something like this:
require('../schemas/mySchema').findOne({'slug': req.params.slug}, function(err, doc){
console.log(mongoose.Types.ObjectId.isValid(doc[key]));
});
But the only true value it returns is for the "id"and "__v" properties (no idea where these came from... I did not set them. _id is also false), all the rest comes as false (including a given property that IS a reference, tested and working)
Is there any way to do that?
Thanks in advance
I believe mongoose returns references with the objectId nested - in the same structure as a populated object but having only the _id key. Try this:
var item = doc[key];
if (typeof item === 'object') {
console.log(mongoose.Types.ObjectId.isValid(item._id));
}

node.js: compare array of ObjectIds with a string object id or an array of string ObjectIds

My Schema is:
var schema = new Schema({
players: {
type: [Schema.Types.ObjectId]});
I'm using Promises. My simple query to fetch the collection by id is:
MyModel.findById(id).exec()
.then(function (doc) {
//here I'm getting my doc
})
.catch(next)
I'm getting my document but the problem is here I'm getting an array of ObjectIds and I want to compare this array of ObjectIds with an ObjectId which I have in the string form. In one case I want to find the index of this string id in that array of ObjectIds.
I tried by lodash like
var index = _.indexOf(doc.players, idInStringForm);//getting -1 as response
also tried with
var index = _.indexOf(doc.players.toString().split(","), idInStringForm);//it is working
but when I needed to take union of ObjectIds' arrays the above logic fails for example:
var arrayOfIds = _.union(doc.players.toString().split(","), stringArrayOfIds);
the above is not working because when doc.players is empty the arryaryOfIds also contains " " which fails my one of queries.
Does we have any better/common solution for the above cases or we need to go with if-else check?
You can filter out any empty strings before checking the union of the array and the id.
function removeEmptyStrings(value) {
return value != "";
}
array = array.filter(removeEmptyStrings);
Wherever the array value is equal to an empty string it will remove it from the array.
See the documentation for the filter method here: Filter
Why not Array.prototype.map and then union?
_.union(doc.players.map((player) => player._id), stringArrayOfIds)

Mongoose.js conditional populate

I'm working with some old data where some of the schema has a "mixed" type.
Basically sometimes a value will be a referenced ObjectID, but other times it'll be some text (super poor design).
I unable to correctly populate this data because of the times a non-ObjectID appears.
So, for my actual question: Is it possible to create a populate (on a collection) that is conditional; I need to be able to tell the populate to skip those other values.
Yes, you can do that check the middleware function on the Mongoose API reference
http://mongoosejs.com/docs/middleware.html
What you need to do is before you populate those data, you validate the data if is is Object ID or not, if it is Object ID, you call next() to pass the next function, else you just return, this will skip it
Example
xSchema.pre('validate', function(next){
var x = this;
var checkXType = typeof x.id;
if (checkXType === String) {
return;
} else {
next();
}
});

Resources