I am currently using Mongoose, however all these hidden keys are driving me crazy and is disrupting my workflow when these keys are popping up out of nowhere. Here is my code - it's simply logging the docs from the find function:
const mongoose = require('mongoose')
const Kitten = mongoose.model('Kitten', mongoose.Schema({ name: String }));
mongoose.connect('mongodb://localhost/test')
mongoose.connection.on('error', console.log)
mongoose.connection.once('open', function() {
var fluffy = new Kitten({ name: 'fluffy' })
fluffy.save((err, fluffy) => {
if (err) return console.error(err);
Kitten.find({}, (err, docs) => {
for (var i = 0; i < docs.length; ++i) {
const doc = docs[i]
console.log('Object.getOwnPropertyNames ', Object.getOwnPropertyNames(doc))
console.log('Object.keys ', Object.keys(doc))
console.log(doc)
console.log('--')
}
})
})
})
And one of the docs that's logged is
Why are the keys shown by console log in neither .keys nor .getOwnPropertyNames? The console.log output is the one that reflects what's actually in the MongoDB document.
Edit: Edited to use more reasonable code
docs is a list of Mongoose document objects. They don't have fields available for enumeration, there are accessors defined that make them available as doc.fieldName.
There are document toObject and toJSON methods to convert document object to plain object when needed.
The actual problem here is that since document object aren't needed, they shouldn't be queried. Plain objects can be retrieved with lean.
Kitten.find({}).lean().exec((err, docs) => {
for (var i = 0; i < docs.length; ++i) {
const doc = docs[i]
...
}
});
The results you get from "find" are cursers. You need to use "toArray" to load the document to RAM.
const mongoose = require('mongoose')
const Kitten = mongoose.model('Kitten', mongoose.Schema({ name: String }));
mongoose.connect('mongodb://localhost/test')
mongoose.connection.on('error', console.log)
mongoose.connection.once('open', function() {
var fluffy = new Kitten({ name: 'fluffy' })
fluffy.save((err, fluffy) => {
if (err) return console.error(err);
Kitten.find({}).toArray((err, doc) => {
console.log('Object.getOwnPropertyNames ', Object.getOwnPropertyNames(doc))
console.log('Object.keys ', Object.keys(doc))
console.log(doc)
console.log('--')
})
})
})
Related
const Location = require("../models/locations");
getLocation = async(req, res) => {
await Location.findOne(
{ name: req.query.locationName }, // req.query.locationName is "Gurgaon"
{ restaurantIds: 1 },
(err, location) => {
if (err) throw err;
else {
console.log(location);
/*
{
_id: 6004f9cff6ae9921f89f0f81,
restaurantIds: [ 6004fb53f6ae9921f89f0f83, 600792321b229bae25a66497 ]
}
*/
console.log(location._id); // 6004f9cff6ae9921f89f0f81
console.log(location.restaurantIds); // undefined
return location;
}
}
);
}
module.exports = { getLocation };
Screenshot of the output
This is how the locations collection looks like.
{
"_id" : ObjectId("6004f9cff6ae9921f89f0f81"),
"name" : "Gurgaon",
"restaurantIds" : [
ObjectId("6004fb53f6ae9921f89f0f83"),
ObjectId("600792321b229bae25a66497")
]
}
Here is the locations schema.
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const Locations = new Schema({}, { strict: false });
module.exports = mongoose.model("locations", Locations);
I don't know the reason why location.restaurantIds is returning me undefined. Please help me with this. I am new to mongodb.
There will be some reasons might be you have not specified this field in your mongoose schema, try adding field in your schema and you are able to access this field in your query.
Second option if you don't want to specify that field in schema then try lean(),
By default, Mongoose queries return an instance of the Mongoose Document class. Enabling the lean option tells Mongoose to skip instantiating a full Mongoose document and just give you the POJO.
await Location.findOne(.. your query).lean();
restaurantIds is a nested object , you must populate it :
const Location = require("../models/locations");
getLocation = async(req, res) => {
await Location.findOne(
{ name: req.query.locationName },
{ restaurantIds: 1 })
.populate('restaurantIds').then(location => {
console.log(location);
console.log(location._id);
console.log(location.restaurantIds);
return location;
})
.catch(err => {
throw err;
})
);
}
module.exports = { getLocation };
It's look like your restaurantIds is an array, so when you print it, you must use as array. Start by change:
console.log(location.restaurantIds);
to:
console.log(location.restaurantIds[0]);
In this example, you will be printing the first object in the array, and use it in your code as array, it will be OK.
Edit:
After added the restaurantIds, and now we know it's array of ObjectID, and this kind of array cannot be printed.
I'm running a Parse Query on my User class.
I want to retrieve users that are contained in an array of strings (ID).
const usersQuery = new Parse.Query(User).containedIn('objectId', customersArrayId).find({ useMasterKey: true });
Which works, actually. I'm getting a [ParseUser { _objCount: 6, className: '_User', id: 'iYIJ7Zrmms' }], because only 1 user matches.
But well, my issue is that I'm only getting ParseUser { _objCount: 6, className: '_User', id: 'iYIJ7Zrmms' }. This class contains other fields (firstname, lastname, e.g.) that are not returned.
When I performed the same thing, looping on my customersArrayId and performing .get():
const customerId = customersArrayId[index];
promises.push(new Parse.Query(User).get(customerId).then((user) => {
return user.toJSON();
}, (error) => console.error(error)));
I'm getting the full object, as expected. But it doesn't seem to be the right way of querying Parse objects from an array of ids.
I can't find anything in the docs about it, any idea why containedIn only returns a part of the queried objects?
I actually get the difference:
new Parse.Query(User).get(customerId)
=> returns the Parse Object
new Parse.Query(User).containedIn('objectId', customersArrayId)
=> returns the Parse User, subclass of a Parse Object.
And well, then, this thread was useful: Get user from parse in Javascript
I ended up using :
usersQuery.then(customersResponse => {
const customers = [];
for (let index = 0; index < customersResponse.length; index++) {
const customer = {
...customersResponse[index].toJSON(),
...customersResponse[index].attributes
};
...
Still not sure that the best answer.
EDIT
router.get('/:userId/shop/customers/details', checkUserMatch, (req, res, next) => {
if('shops' in req.jwtData.data) {
const shopId = req.jwtData.data.shops[0];
const query = new Parse.Query('OtherStuff');
const Shop = Parse.Object.extend('Shops');
query.equalTo('shop', new Shop({id: shopId})).find().then((otherStuffs) => {
const customersArrayId = otherStuffs.map(otherStuff => otherStuff.toJSON().user.objectId);
const usersQuery = new Parse.Query('_User').containedIn('objectId', customersArrayId).find({ useMasterKey: true });
usersQuery.then(customersResponse => {
const customers = [];
for (let index = 0; index < customersResponse.length; index++) {
let customer = {
...customersResponse[index].toJSON(),
...customersResponse[index].attributes
};
const customerId = customer.objectId;
const stuffQuery = new Parse.Query('Stuff').equalTo('user', new UserModel({objectId: customerId})).find().then((stuff) => {
return stuff;
});
const otherStuffQuery = new Parse.Query('otherStuff').equalTo('user', new UserModel({objectId: customerId})).find().then((otherStuff) => {
return otherStuff;
});
Promise.all([stuffQuery, otherStuffQuery]).then((data) => {
const stuff = data[0];
const otherStuff = data[1];
customer = {
...customer,
stuff,
otherStuff,
}
customers.push(customer);
if(index === customersResponse.length - 1) { // last customer
res.json({
success: true,
data: customers
});
}
})
}
});
});
}
});
I am trying to migrate some data from an old MongoDB schema to a new one. All the schema stuff is working fine. What I cannot understand is why the old documents are not being converted before being saved as new documents. I am reading all the documents, using a map function to convert them to the new schema then saving them. But they are all failing validation because, it turns out, they haven't been modified to the new schema at all. Is this an async issue? Any clues would be great.
let User = require('./api/models/user.model');
let newUser;
let mapUsers = () => {
let makeUser = (u) => {
return {
firstName: u.first_name,
lastName: u.last_name,
avatarUrl: u.avatar_url,
email: u.email,
loginCount: u.login_count,
loginTime: u.login_time,
logoutTime: u.logout_time
}
};
h2User.find({}).limit(1).exec((err, users) => {
if (err) {
console.error(err);
} else {
users.map(user => {
newUser = new User(makeUser(user)); // like this doesn't work
newUser.save((err, nu) => {
if (err) {
console.log(err);
} else {
console.log(nu._id)
}
});
});
}
});
};
mapUsers();
You would have to convert the Mongo document into an object with new User(makeUser(user.toObject())).
As Mongoose returns a document, it will contain other attributes that may not be apparent. When you do console.log(user) it usually prints the output of toObject so it can get confusing.
I'm building a GraphQL Server where I need to do some sort of validation before committing data to database (MongoDB and Mongoose).
One of these checks is related to unique fields. So, a model may have one or more unique fields and I need to be able to check for that before saving into database.
So, I have build some helper functions to do it and the code is below:
Helper code:
import mongoose from "mongoose";
const isFieldUnique = (modelName, fieldName, fieldValue) => {
let model = mongoose.model(modelName);
let query = {};
query[fieldName] = fieldValue;
return model.findOne(query).exec();
};
const executeUniquePromises = (uniques, modelName, data) => {
let promises = [];
uniques.map(name => {
let value = data[name];
if (!value)
throw new Error("Cannot test uniqueness for a null field.");
promises.push(
isFieldUnique(modelName, name, value)
.then(value => {
if (value) {
let error = name + ' is not unique';
console.log(error);
return error;
}
console.log(name + ' is unique');
return null;
})
.catch(error => {
throw new Error(error);
})
)
});
return Promise.all(promises);
};
export const checkUniqueness = (uniques, modelName, data) => {
return new Promise((resolve, reject) => {
executeUniquePromises(uniques, modelName, data).then(result => {
let errors = [];
// Check for errors
result.map((error) => {
if (error)
errors.push(error);
});
if (errors.length > 0)
return reject(errors);
else
resolve();
});
});
}
Mongoose static create function:
import * as helper from './helper';
schema.statics.create = function (data) {
let uniques = ['name', 'email'];
helper.checkUniqueness(uniques,'Company', data)
.then(result => {
let user = new this(data);
return company.save();
})
.catch(error => {
throw new Error(error);
});
}
GraphQL code:
const createUser = {
type: UserType,
description: "Create a user",
args: {
data: {
name: "user",
type: new GraphQLNonNull(UserInputType)
}
},
resolve(root, args) {
return UserModel.create(args.data);
}
};
The helper code seens to be confused and I´m not using my usage of promises with other promises are the correct way of doing it.
Remember that I may need to check several fields for uniqueness, so that is why I´ve created the promise array.
One problem is that when I´m inserting data where there are not uniques matching I get no return in my GraphQL Server.
I want to find out a better way of doing it and discover why I´m not getting back the saved object.
MongoDB already handles unique out of the box. Set the field to unique: true in the Mongoose schema. You can use mongoose-beautiful-unique to make the error messages similar to the validation error messages. And finally, read this when you can't get unique: true to work.
I am trying to insert a subdocument using .create() and also query said document using .id(). I've been following the guide here
I am getting the error: object has no method 'id()' or 'create()'
The following is my code:
/db/schemas/AnnouncementsSchema.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var postSchema = new Schema({
title: String,
dateCreated: String,
dateEdited: { type: Date, default: Date.now() },
summary: String,
body: String
});
var announcementsSchema= new Schema({
categoryName: String,
posts: [postSchema]
});
announcementsSchema.methods.Update = function (callback) {
console.log('Updating object: ' + this);
this.save(function (err, object) {
callback(err, object);
});
}
var announcements = mongoose.model('announcements', announcementsSchema);
var post = mongoose.model('post', postSchema);
module.exports = {
announcements: announcements,
post: post
};
/routes/Announcements.js
var mongoose = require('mongoose');
var announcementsSchema = require('../../db/schemas/AnnouncementsSchema.js');
exports.InsertPost = function (req, res) {
var announcements = announcementsSchema.announcements;
var post = announcementsSchema.post;
var categoryToEdit = req.body.categoryToEdit;
var newPost = req.body.newPost;
announcements.GetById(categoryToEdit._id, function (err, announcment) {
var postToAdd = new post(newPost);
announcment.posts.create(postToAdd);
announcment.Update(function (err, object) {
res.send({ err: err, data: object});
});
});
}
I have the .save method wrapped so I can add extra functionality if needed. I crashes when it calls .create(). The same is true if I am trying to remove a post as well. Here is the code for that:
exports.DeletePost = function (req, res) {
var announcements = announcementsSchema.announcements;
var categoryId = req.body.categoryId;
var postId = req.body.postId;
announcements.findById(categoryId, function (err, object) {
var postToDelete = object.posts.id(postId);
console.log(postToDelete);
res.end();
});
}
Either way they both crash and google is pretty slim on answers. Most people have given a of different ways to expose the schemas and models, and what I have above is pretty much a sum of what they suggested. Any ideas? Thanks!
I found the answer after toying with this forever. It was simple. The order in which you declare your schema matters. So I had to declare my subdocument schema before I declare my parent schema. Such as:
var announcementsSchema= new Schema({
categoryName: String,
posts: [postSchema]
});
var postSchema = new Schema({
title: String,
dateCreated: String,
dateEdited: { type: Date, default: Date.now() },
summary: String,
body: String
});
This worked! I am now able to keep all the rest of the code the same, but now the methods exist!
Even if I recently picked up Express.js, I will do my best in trying to provide a useful answer. Hope it helps!
If you want to access the id of a particular post, it is stored in the ._id property, not id. Moreover, I believe that you first have to loop over object.posts since it is an array of posts:
exports.DeletePost = function (req, res) {
var announcements = announcementsSchema.announcements;
var announcementId = req.body.announcementId;
var postId = req.body.postId; // This is the _id property of an element contained within the posts array
var postToDelete = {};
announcements.findById(announcementId, function (err, object) {
// Get the posts array
var posts = object.posts;
// Loop over it until you find the post with the id that you stored in postId
for (var i = 0; i < posts.length; i ++) {
if (posts[i]._id == postId) {
postToDelete = posts[i];
}
}
console.log(postToDelete);
res.end();
});
}
As for your InsertPost function, you should also take into account the fact that posts is an array. Hence, you could simply push the new post into that array and save it accordingly:
exports.InsertPost = function (req, res) {
var announcements = announcementsSchema.announcements;
var post = announcementsSchema.post;
var announcementId = req.body.announcementId;
var newPost = req.body.newPost;
announcements.findById(announcementId, function (err, announcment) {
var postToAdd = new post(newPost);
announcment.posts.push(postToAdd);
announcment.save(function(err) {
if (err) {
res.send(500, { error: err.stack });
}
console.log('Success');
res.end();
});
});
}