Mongoose statics is not working with async and await - node.js

I have one mongoose model where I declared 1 statics which should return me all features.
const Feature = require('./featureModel')
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const schemaOptions = {
timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' },
};
const ModemSchema = new Schema({
proxy_type: {
type: String,
default: 'None'
},
data_prioritization: {
type: String,
default: 'None'
}
}, schemaOptions);
ModemSchema.statics.possibleProxyTypes = async function () {
features = await Feature.find({})
return features
}
module.exports = mongoose.model('Modem', ModemSchema);
Modem.possibleProxyTypes I am calling it like this (class method)
But await is not waiting and I am getting output [AsyncFunction] . Not sure what is wrong here.

I made it worked like this. ( If you had added all the related codes to the question, I would say where was the problem.)
Modem Schema: ( almost no change, I only added let features, and console.log)
ModemSchema.statics.possibleProxyTypes = async function() {
letfeatures = await Feature.find({});
console.log(features);
return features;
};
And I tried this in a sample get route like this:
const Feature = require("../models/featureModel");
const Modem = require("../models/modemModel");
router.get("/modem", async (req, res) => {
const features = await Modem.possibleProxyTypes();
res.send(features);
});
Maybe the problem was that you didn't use await here in this line:
await Modem.possibleProxyTypes()
This returned me features like this:
[
{
"_id": "5e0207ff4323c7545026b82a",
"name": "Feature 1",
"__v": 0
},
{
"_id": "5e0208054323c7545026b82b",
"name": "Feature 2",
"__v": 0
}
]

Related

Req.body not fetching array value from schema when save data using node

When I'm saving the data using node in my MongoDB schema array would be empty here my code and output could you please explain why this error happening
controller.js
const {name,age,social,songs}=new musicModel(req.body)
const addMusic =await musicModel.create({
name,age,songs,social
})
if (addMusic) {
res.send(addMusic)
} else {
res.status(500).send("unsuccessfull.")
}
}
Here's my schema but it will perfectly work when I send response using postmen but when I saved this the bellow array was undefined or null
model.js
const mongoose = require("mongoose")
const songSchema= mongoose.Schema({
title:{
type:String
},
sales:{
type:String
}
})
const musicSchema = mongoose.Schema({
name: {
type: String
},
age: {
type: String
},
social: {
show: {
type: Number
}, tours: {
type: Number
},
},songs:[songSchema]
})
const musicModel =mongoose.model("Music",musicSchema)
module.exports= musicModel
app.js
const express = require('express')
const logger = require('morgan')
const bodyParser = require('body-parser')
const mongoose = require('mongoose')
require("dotenv/config")
const cors = require('./middleware/cors')
const productsRouter = require('./routes/products')
const customerRouter= require('./routes/customers')
const quotRouter=require('./routes/quots')
const usersRouter = require('./routes/users')
mongoose.connect(
process.env.CONNECTION_URL,
{ useNewUrlParser: true },
(err) => {
if (!err) {
console.log('DB Connected')
}
}
);
const app = express()
app.use(express.static('public'))
app.use(logger('dev'))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended:true}))
app.use(cors)
app.use('/products', productsRouter)
app.use('/customer',customerRouter)
app.use('/', usersRouter)
app.use('/quotation',quotRouter)
module.exports =app;
output
{
"name": "ashit",
"age": "67",
"social": {
"show": 566,
"tours": 47
},
"songs": [],
"_id": "61c57a22a6903d467834d19f",
"__v": 0
}
You have the choice between theses two types of code :
First Solution :
const {name,age,social,songs}=req.body
const addMusic =await musicModel.create({
name,age,songs,social
})
if (addMusic) {
res.send(addMusic)
} else {
res.status(500).send("unsuccessfull.")
}
}
Second solution :
const {name,age,social,songs}=new musicModel(req.body)
const addMusic =await musicModel.save()
if (addMusic) {
res.send(addMusic)
} else {
res.status(500).send("unsuccessfull.")
}
}
Try to do like this:
Declare the songModel and export it as well:
const songSchema= mongoose.Schema({
title:{
type:String
},
sales:{
type:String
}
})
const songModel =mongoose.model("Song",songSchema)
...
module.exports= { musicModel, songModel }
Change the songs attribute in the musicSchema to be a reference:
songs:[
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Song',
}
]
After creating the new song, create each song object as well and add its reference to the musicModel object:
const { name, age, social, songs } = req.body;
const addMusic = await musicModel.create({
name,
age,
social,
});
songs.forEach((song) => {
const newSong = await songModel.create(song);
addMusic.songs.push(newSong._id);
});
await addMusic.save();
if (addMusic) {
res.send(addMusic);
} else {
res.status(500).send("unsuccessfull.");
}

Querying in-memory mongodb with Jest

I am trying to unit test my code, I am using Node.js, Express, MongoDB, Mongoose and trying to test it with Jest.
I am having issues with the in-memory mongo db with Jest. I have read the documentation (https://jestjs.io/docs/en/mongodb).
When trying to test a query for one of my Mongoose models, I believe that my query is not pointing at the correct place, I would like it to run its query against the in-memory collection.
My current test setup:
const { MongoClient } = require("mongodb");
const Preferences = require("./preferences");
const mongoose = require("mongoose");
const ObjectId = mongoose.Types.ObjectId;
describe("Preference helper function", () => {
let connection;
let db;
let userPreferences;
const testUser = new Preferences({
userId: ObjectId("5eef15429e93464b3ccae235"),
wordPreferences: [
{ wordId: 1, active: true},
{ wordId: 2, active: true},
{ wordId: 3, active: true},
{ wordId: 4, active: true},
{ wordId: 5, active: true}
],
});
beforeAll(async () => {
connection = await MongoClient.connect(global.__MONGO_URI__, {
useNewUrlParser: true,
});
db = await connection.db(global.__MONGO_DB_NAME__);
});
afterAll(async () => {
await connection.close();
await db.close();
});
beforeEach(async () => {
userPreferences = await db.collection("userPreferences");
});
afterEach(async () => {
await userPreferences.deleteMany();
});
it("get active words function gives correct result", async () => {
await userPreferences.insertOne(testUser);
const expectedResult = [1, 2, 3, 4, 5];
let queryResult = await Preferences.getActiveWordIds(testUser.userId);
expect(queryResult).toEqual(expectedResult);
});
});
Running this code gives the following error:
Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Error
Changing the timeout for this does not resolve the problem as it appears the query does not resolve. Here is the the Moongoose model for the Preference model used in the test above:
const mongoose = require("mongoose");
const PreferenceMappingSchema = require("../preference-mapping");
var PreferenceSchema = new mongoose.Schema({
userId: {
type: mongoose.Types.ObjectId,
required: true,
},
wordPreferences: {
type: [PreferenceMappingSchema],
required: true,
},
});
// Choose the correct db and set the model
const db = mongoose.connection.useDb("users");
const Preferences = (module.exports = db.model("Preference", PreferenceSchema));
module.exports.getActiveWordIds = async function(userId) {
try {
const user = await Preferences.aggregate([
// Get the current users doc
{ $match: { userId: userId} },
{
$project: {
wordPreferences: {
// Get only active words
$filter: {
input: "$wordPreferences",
as: "pref",
cond: {
$eq: ["$$pref.active", true],
},
},
},
},
}
]).exec();
// Map to list of valid numbers
return user[0]['wordPreferences'].map(x => x.wordId);
} catch (error) {
console.warn(error);
throw error;
}
};
Thanks in advance for any help!

How to return the result of a mongodb pipeline by a get methodo?

I have a pipeline and its result I want to return it by an express method that is get or not i know if it is more advisable to send it by a socket
this is my file pipeline.js:
function getModel(model) {
model.aggregate([{
$group: {
_id: null,
"price": {
$sum: "$price",
}
}
}]).exec((e, d) => {
return JSON.stringify(d)
})
}
module.exports = getModel;
in the model.js file I'm going to call my pipeline.js file and therefore the function
model.js:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const getModel = require('./pipeline');
const mySchema = new Schema({
user: {
type: Schema.ObjectId,
ref: 'User'
},
namepet: String,
type_of_service: String,
characteristic_of_pet: String,
price: Number
});
const model = mongoose.model('Cites', mySchema);
here is the function-> getModel(model);
module.exports = model;
and it works for me as I want the problem is that the result I have to send it by a method get and I have no idea how to do it
How can I send the result indicating the red arrow of the image by a get method?
var express = require('express');
var app = express();
function getModel(model) {
model.aggregate([{
$group: {
_id: null,
"price": {
$sum: "$price",
}
}
}]).exec((e, d) => {
return JSON.stringify(d)
})
}
app.get('/', function(req, res) {
console.log('marhaba');
res.send(getModel( ** Model ** ))) //== > here call the getModel function
});
app.listen(3000, function() {
console.log("Working on port 3000");
});

How to get the array of object instead of just _id in Mongoose

I am very new to mongoose.
I am currently building a backend using Node.js, express.js, GraphQL, and mongoose.
I have a Drink schema and DrinkType Schema. I defined DrinkType schema as "alcohol", "juice", "tea". And I have added many drinks and each drink has DrinkType reference. Then, I would like to reference all the drinks from DrinkType.
This is the schema for drinkType
const drinkTypeSchema = new Schema({
name: {
type: String,
required: true,
},
createdDrinks: [
{
type: Schema.Types.ObjectId,
ref: 'Drink',
},
],
Here is the schema for drink
const drinkSchema = new Schema({
name: {
type: String,
required: true,
},
drinkType: {
type: Schema.Types.ObjectId,
ref: 'DrinkType',
},
})
This is the drink resolver. Whenever a new drink is created, I am pushing it to drinkType.
try {
const result = await drink.save()
createdDrink = transformDrink(result)
const drinkType = await DrinkType.findById(args.addDrinkInput.drinkTypeId)
if (!drinkType) {
throw new Error('DrinkType not found.')
}
drinkType.createdDrinks.push(drink)
await drinkType.save()
const drinkLoader = new DataLoader(drinkIds => {
return drinks(drinkIds)
})
const drinks = async drinkIds => {
try {
const drinks = await Drink.find({ _id: { $in: drinkIds } })
return drinks.map(drink => {
return transformDrink(drink)
})
} catch (err) {
throw err
}
}
const transformDrink = drink => {
return {
...drink._doc,
_id: drink.id,
drinkType: drinkType.bind(this, drink.drinkType),
}
}
const drinkType = async drinkTypeId => {
try {
const drinkType = await drinkTypeLoader.load(drinkTypeId.toString())
return {
...drinkType._doc,
_id: drinkType.id,
createdDrinks: () => drinkLoader.loadMany(drinkType._doc.createdDrinks),
}
I want this createdDrinks part to return the array of drink objects, but it is only returning the array of _ids.
I have been reading the mongoose documentation and it seems that using ObjectId is the correct way. Would you mind helping me out?
Thank you in advance.

Refactoring several mongoose models to similar collections

I have several collections that have the same documents type, except for the language.
Let's say imagesES, imagesEN, imagesFR, and so on....
I just thought about definig just one schema, but also one model that get the proper collection with a parameter:
var mongoose = require('mongoose')
var Schema = mongoose.Schema
let authorSchema = require('./Authors').authorSchema
const imageSchema = new Schema({
authors: [authorSchema],
status: Number, // published (1), unpublished (0)
created: { type: Date, default: Date.now },
lastUpdated: { type: Date, default: Date.now },
license: {
type: String,
enum: ['Creative Commons BY-NC-SA'], //just one license right now
default: 'Creative Commons BY-NC-SA'
},
downloads: {
type: Number,
default: 0
},
tags: [String]
})
module.exports = locale => {
return mongoose.model('Image', imageSchema, `image${locale}`)
}
However in the controller I should require the model inside the controller (when I know the locale):
getImageById: (req, res) => {
const id = req.swagger.params.id.value
const locale = req.swagger.params.locale.value
const Images = require('../models/Images')(locale)
Images.findOne({_id: id}).lean().exec( (err, image) => {
I'm not sure if this is the proper way as each request I get I have to require the model module (syncronously) or should I require all the different models previous to the use in the function.
const locales = ['es', 'en', 'fr']
const Images = []
locales.map(locale=>Images[locale] = require('../models/Images')(locale))
getImageById: (req, res) => {
const id = req.swagger.params.id.value
const locale = req.swagger.params.locale.value
Images[locale].findOne({_id: id}).lean().exec( (err, image) => {
Finally this is how I resolved it. Where it says Pictograms, could be Images as in the question
const setPictogramModel = require('../models/Pictograms')
const languages = [
'ar',
'bg',
'en',
'es',
'pl',
'ro',
'ru',
'zh'
]
const Pictograms = languages.reduce((dict, language)=> {
dict[language]= setPictogramModel(language)
return dict
}, {})
module.exports = {
getPictogramById: (req, res) => {
const id = req.swagger.params.idPictogram.value
const locale = req.swagger.params.locale.value
// Use lean to get a plain JS object to modify it, instead of a full model instance
Pictograms[locale].findOne({id_image: id}).exec( (err, pictogram) => {
if(err) {
return res.status(500).json({
message: 'Error getting pictograms. See error field for detail',
error: err
})
}
if(!pictogram) {
return res.status(404).json( {
message: `Error getting pictogram with id ${id}`,
err
})
}
return res.json(pictogram)
})
},

Resources