Mongoose Async Accessors - node.js

Consider the Schemas
const Person = Schema({
name: String,
pets: {
type: [Schema.Types.ObjectId],
ref: 'Pets'
}
})
const Pet = Schema({
name: String,
color: String
})
How is it possible on Person.findBy(personId) to return the following and not an array of ids?
{
name: 'John Doe',
pets: [
{ name: 'Lilah', color: 'white' },
{ name: 'Hanna', color: 'brown' }
]
}
I Tried to use mongoose async accessors
const Person = Schema ({
...
pets: {
type: [Schema.Types.ObjectId],
ref: 'Pets',
get: async pets => Pets.find({ '_id': { $id: pets} })
}
...
})
But feels like overengineering (and it runs the function everytime the property is accessed)

Related

I have this error Unauthorised admin to execute command mongoose + Graphql

I used mongoose and Graphql to send my queries to the database but for some reason it doesn't let me create documents. I have tried creating a new user with full admin privileges it hasn't worked I tried changing the default user password but it didn't work.
I rechecked my mongoose model no errors so what might be the problem.
FYI the problem arose with the return (author.save()) and the database connects normally
Author Model
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const authorSchema = new Schema({
name: String,
age: Number
});
module.exports = mongoose.model('Author', authorSchema);
schema.js
const graphql = require('graphql');
const Book = require('../models/book');
const Author = require('../models/Author');
const _ = require('lodash');
const {
GraphQLObjectType,
GraphQLString,
GraphQLSchema,
GraphQLID,
GraphQLInt,
GraphQLList
} = graphql;
const BookType = new GraphQLObjectType({
name: 'Book',
fields: ( ) => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
genre: { type: GraphQLString },
author: {
type: AuthorType,
resolve(parent, args){
//return _.find(authors, { id: parent.authorId });
}
}
})
});
const AuthorType = new GraphQLObjectType({
name: 'Author',
fields: ( ) => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
age: { type: GraphQLInt },
books: {
type: new GraphQLList(BookType),
resolve(parent, args){
//return _.filter(books, { authorId: parent.id });
}
}
})
});
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
book: {
type: BookType,
args: { id: { type: GraphQLID } },
resolve(parent, args){
//return _.find(books, { id: args.id });
}
},
author: {
type: AuthorType,
args: { id: { type: GraphQLID } },
resolve(parent, args){
//return _.find(authors, { id: args.id });
}
},
books: {
type: new GraphQLList(BookType),
resolve(parent, args){
//return books;
}
},
authors: {
type: new GraphQLList(AuthorType),
resolve(parent, args){
//return authors;
}
}
}
});
const Mutation = new GraphQLObjectType({
name: 'Mutation',
fields: {
addAuthor: {
type: AuthorType,
args: {
name: { type: GraphQLString },
age: { type: GraphQLInt }
},
resolve(parent, args){
let author = new Author({
name: args.name,
age: args.age
});
return (author.save())
}
}
}
});
module.exports = new GraphQLSchema({
query: RootQuery,
mutation: Mutation
})
;
error message
(node:31482) MongoError: (Unauthorized) not authorized on admin to execute command {
insert: "authors", documents: [[{name gyfdgyiszukjfheusdzyih} {age 88} {_id
ObjectID("60af9c682215ea7afad86f4c")} {__v 0}]], ordered: false, writeConcern: { w:
"majority" }
Found this issue, after trying practice by GraphQL tutorial on Youtube.
To solve it, you need to update your mongoose model to the last version.

Add timestamp to a new subdocument or subschema in mongoose

I have this document in mongo atlas
_id: 5f8939cbedf74e363c37dd86,
firstname: "Person",
lastname: "Person lastname",
sex: "Masculino",
age: "20",
birthDay: 2020-10-07T00:00:00.000+00:00,
vaccines: Array
0:Object
dose: Array
_id: 5f8939cbedf74e363c37dd87
vaccine:5f7023ad96f7ed21e85be521
createdAt:2020-10-16T06:12:27.726+00:00
updatedAt:2020-10-16T06:12:27.726+00:00
1:Object
dose:Array
_id:5f893a9ca98e97188c93fea8
vaccine:5f70259796f7ed21e85be523
2:Object
dose:Array
_id:5f893acda98e97188c93fea9
vaccine:5f7023ad96f7ed21e85be521
This is my mongoose schema
const mySchema = new Schema({
firstname: {
type: String,
required: true,
},
lastname: {
type: String,
required: true,
},
sex: {
type: String,
required: true,
},
age: {
type: String,
required: true,
},
birthDay: {
type: Date,
required: true,
},
vaccines: [
{
type: new Schema(
{
vaccine: {
type: Schema.ObjectId,
ref: "Vaccine",
},
dose: Array,
},
{ timestamps: true }
),
},
],
});
every time I add a new person the vaccines array gets one new object with the timestamp as you can see, in my js file I use this code:
const addPerson = (person) => {
const myPerson= new Model(person);
return myPerson.save();
};
Then when I add a new vaccine for the same person this does not get the timestamp, I'm using this code for that:
const addPersonVaccine = async ({ params, body }) => {
if (!params) return Promise.reject("Invalid ID");
const vaccines = [body];
const foundPerson = await Model.updateOne(
{
_id: params,
},
{
$push: {
vaccines: vaccines,
},
}
);
return foundPerson;
};
This is what my body inside vaccines array has:
[ { vaccine: '5f72c909594ee82d107bf870', dose: 'Primera' } ]
The problem is that I have no results about the next timestamps, as you can see in my mongo atlas document:
1:Object
dose:Array
_id:5f893a9ca98e97188c93fea8
vaccine:5f70259796f7ed21e85be523
2:Object
dose:Array
_id:5f893acda98e97188c93fea9
vaccine:5f7023ad96f7ed21e85be521
Is that the best way to implement timestamps in subdocuments or sub schemas?
I will appreciate your answers, thnks 👏
You can use mongoose schema timestamps options to the inner schemas
const mongoose = require("mongoose");
const forumSchema = new mongoose.Schema(
{
title: { type: String, required: true },
biddings: [
{
type: new mongoose.Schema(
{
biddingId: String,
biddingPoints: Number
},
{ timestamps: true }
)
}
]
},
{ timestamps: true }
);
const Forum = mongoose.model("Forum", forumSchema);
module.exports = Forum;
for more Mongoose schema set timestamp on nested document

Schema with embedded discriminators in arrays not cloned properly

I have scheme with embedded discriminators, and I want to clone it. But when I've create model from this cloned schema, and try to create document, some properties, related to this discriminators are goes away.
Here the code
const mongoose = require('mongoose');
const propertiesSchema = new mongoose.Schema({
name: { type: String },
},
{ discriminatorKey: 'type', _id: false });
const collectionSchema = new mongoose.Schema({
name: { type: String },
properties: [propertiesSchema]
});
const propertiesArray = collectionSchema.path(`properties`);
propertiesArray.discriminator(`type1`,
new mongoose.Schema({ type1: String }, { _id: false })
);
propertiesArray.discriminator(`type2`,
new mongoose.Schema({ type2: String }, { _id: false })
);
const Collection = new mongoose.model('Collection', collectionSchema);
const Clone = new mongoose.model('Clone', Collection.schema.clone());
const data = {
name: "Collection",
properties: [
{ type: "type1", type1: "type1-1" },
{ type: "type2", type2: "type2-2" }
]
}
console.log(new Collection(data));
console.log(new Clone(data));
Result is:
{ _id: 5d1b583e6d2d8b519c8849b8,
name: 'Collection',
properties:
[ { type: 'type1', type1: 'type1-1' },
{ type: 'type2', type2: 'type2-2' } ] }
{ _id: 5d1b583e6d2d8b519c8849b9,
name: 'Collection',
properties: [ { type: 'type1' }, { type: 'type2' } ] }
So question is - why documents are different, and how to correctly clone, or "re-apply" discriminators on cloned scheme ?
node: v10.15.3
mongoose: 5.6.2
Issue will be fixed in version 5.6.4
Github fix commit

Mongoose: Model.save() doesn't save the array of objects on my MongoDB

My MongoDB is not getting the data I'm trying to save specifically from the array, every other properties are being saved normally.
Here's my Schema:
const mongoose = require('mongoose');
const SubSchema = new mongoose.Schema({
servicesTitle: {
title: {
type: String
},
description: {
type: String
}
}
});
const AreaSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
text: {
type: String,
required: true
},
imageUrl: {
type: String,
required: true
},
imageDescription: {
type: String,
required: true
},
services: [SubSchema]
});
const Areas = mongoose.model('Areas', AreaSchema);
module.exports = Areas;
How am I handling it?
const { //Getting the info from the form
title,
text,
imageUrl,
imageDescription,
servicesTitle, //ARRAY
servicesDescription //ARRAY
} = req.body;
Then I created a variable called "services" and its value is an array of objects with the servicesTitle and servicesDescription each, like:
[
{
servicesTitle: 'Example1',
servicesDescription: 'lalalallalalala'
},
{
servicesTitle: 'Example2',
servicesDescription: 'lalalallalalala'
},
{
servicesTitle: 'Example3',
servicesDescription: 'lalalallalalala'
}
]
Then I create a new model with these values and save it:
const newAreas = new Areas({
title,
text,
imageUrl,
imageDescription,
services
});
newAreas.save()
.then(areas => {
res.redirect('/areas');
})
.catch(err => console.log(err));
Here's the output: https://i.imgur.com/kD0qfXh.png
So the exactly amount of items are created but the properties aren't saved. There should be 2 properties inside each object, the servicesTitle and servicesDescription.
Your services object needs to be like below, before passing to save function as per your schema
[
{
servicesTitle: 'Example1',
description: 'lalalallalalala'
},
{
servicesTitle: 'Example2',
description: 'lalalallalalala'
},
{
servicesTitle: 'Example3',
description: 'lalalallalalala'
}
]
And your schema should be like
const SubSchema = {
servicesTitle: {
title: {
type: String
},
description: {
type: String
}
}
};

Why populate returns empty results in mongodb

I want to populate my property (subCategories) but it's return empty although there are results in my database. Did I have miss something? I followed the populate method in mongoose:
const Document = mongoose.model('Document', new mongoose.Schema({
name: { type: String },
description: { type: String },
subCategory: { type: mongoose.Schema.Types.ObjectId }
}));
const Category = mongoose.model('Category', new mongoose.Schema({
name: { type: String },
subCategories: [
{
name: { type: String }
}
]
})
);
var cat1 = await new Category({ name: 'cat1', subCategories: [{ name: 'sub-1-cat-1' }, { name: 'sub-1-cat-2' } ]}).save();
var cat2 = await new Category({ name: 'cat2', subCategories: [{ name: 'sub-2-cat-1' }, { name: 'sub-2-cat-2' } ]}).save();
await new Document({ name: 'doc1', description: 'blabla', subCategory: cat2.subCategories[1] }).save();
const results = Document.find({}).populate('subCategory');
// results[0].subCategory is empty?! why?
The subcategory must be a mongoose model in order to be populated, currently the subCategory that you're trying to populate is just an array item for Category model so I would refactor the code you've posted above to this:
const SubCategory = mongoose.model('SubCategory', new mongoose.Schema({
name: { type: String }
}));
const Document = mongoose.model('Document', new mongoose.Schema({
name: { type: String },
description: { type: String },
subCategory: { type: mongoose.Schema.Types.ObjectId, ref: "SubCategory" }
}));
const Category = mongoose.model('Category', new mongoose.Schema({
name: { type: String },
subCategories: [
{ type: mongoose.Schema.Types.ObjectId, ref: "SubCategory" }
]
})
);
var sub1Cat1 = await new SubCategory({ name: 'sub-1-cat-1' }).save();
var sub1Cat2 = await new SubCategory({ name: 'sub-1-cat-2' }).save();
var sub2Cat1 = await new SubCategory({ name: 'sub-2-cat-1' }).save();
var sub2Cat2 = await new SubCategory({ name: 'sub-2-cat-2' }).save();
var cat1 = await new Category({ name: 'cat1', subCategories: [sub1Cat1, sub1Cat2 ] }).save();
var cat2 = await new Category({ name: 'cat2', subCategories: [sub2Cat1, sub2Cat2 ] }).save();
await new Document({ name: 'doc1', description: 'blabla', subCategory: cat2.subCategories[1] }).save();
const results = Document.find({}).populate('subCategory');
// results[0].subCategory is not empty!

Resources