One to many with MongoDB (Mongoose) with .populate() - node.js

What I am doing
I am trying to return a player by playerId with all ref items from each object in it's schema. In this example, I am specifically talking about the players inventory.
How can I return all reference items and their properties?
In my service file, I am getting my player with:
/**
* Get a player by playerId
* #param playerId
* #returns {Promise<*>}
*/
module.exports.getPlayer = async (playerId) => {
return await Player.findOne({ playerId: playerId }).populate('inventory');
};
And this is my returned JSON
{
"success": true,
"data": {
"_id": "63bb1f3ec17d33f4d2a87194",
"playerId": 4183489039,
"name": "AlanGreyjoy",
"currency": {
"copper": 500,
"silver": 10,
"gold": 0
},
"online": false,
"inventory": [
{
"currentDurability": 0,
"item": "63ba0c54407456969ba38615",
"amount": 1,
"_id": "63bc4fa070eaa247288e3573",
"createdAt": "2023-01-09T17:32:16.643Z",
"updatedAt": "2023-01-09T17:32:16.643Z"
}
],
"bank": [],
"questTracker": [],
"friends": [],
"knownRecipes": [],
"jobs": [],
"createdAt": "2023-01-08T19:53:34.903Z",
"updatedAt": "2023-01-09T17:32:16.643Z",
"__v": 1
}
}
As you can see, the item in the inventory array is not populating with the ref item from the items collection.
I have googled and googled and tried so many different things, but nothing is working.\
The item does exist in the db
My Models
My Player.model.js
const mongoose = require('mongoose');
const toJSON = require('../plugins/mongoToJson');
const Schema = mongoose.Schema;
const questTracker = new Schema(
{
quest: { type: mongoose.Schema.Types.ObjectId, ref: 'Quest' },
complete: { type: Boolean, default: false },
},
{ timestamps: true }
);
const friends = new Schema(
{
player: { type: mongoose.Schema.Types.ObjectId, ref: 'Player' },
isFavorite: { type: Boolean, default: false },
},
{ timestamps: true }
);
const knownRecipes = new Schema(
{
recipe: { type: mongoose.Schema.Types.ObjectId, ref: 'Recipe' },
},
{ timestamps: true }
);
const jobs = new Schema(
{
job: { type: mongoose.Schema.Types.ObjectId, ref: 'Job' },
},
{ timestamps: true }
);
const inventoryItems = new Schema(
{
item: { type: mongoose.Schema.Types.ObjectId, ref: 'Item' },
amount: { type: Number, default: 0 },
currentDurability: { type: Number, default: 0 },
},
{ timestamps: true }
);
const bank = new Schema(
{
item: { type: mongoose.Schema.Types.ObjectId, ref: 'Item' },
amount: { type: Number, default: 0 },
},
{ timestamps: true }
);
const playerSchema = new Schema(
{
playerId: {
type: Number,
unique: true,
required: true,
},
name: {
type: String,
required: true,
unique: true,
},
currency: {
type: Object,
default: {
copper: 500,
silver: 10,
gold: 0,
},
},
inventory: [inventoryItems],
bank: [bank],
questTracker: [questTracker],
friends: [friends],
knownRecipes: [knownRecipes],
jobs: [jobs],
online: {
type: Boolean,
default: false,
},
},
{ timestamps: true }
);
const PlayerModel = mongoose.model('player', playerSchema);
module.exports = PlayerModel;
My Item.model.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const toJSON = require('../plugins/mongoToJson');
const vendorPriceSchema = mongoose.Schema({
copper: {
type: Number,
default: 0,
},
silver: {
type: Number,
default: 0,
},
gold: {
type: Number,
default: 0,
},
});
const itemSchema = new Schema(
{
title: {
type: String,
required: true,
unique: true,
},
assetId: {
type: Number,
required: true,
},
description: {
type: String,
},
equipDescription: {
type: String,
},
additionalDescription: {
type: String,
},
consumableUseDescription: {
type: String,
},
itemLevel: {
type: Number,
},
requiredLevel: {
type: Number,
},
type: {
type: String,
required: true,
},
subtype: {
type: String,
required: true,
},
stamina: {
type: Number,
},
intellect: {
type: Number,
},
criticalStrike: {
type: Number,
},
agility: {
type: Number,
},
mastery: {
type: Number,
},
maxDurability: {
type: Number,
},
vendorPrice: { vendorPriceSchema },
minDamage: {
type: Number,
default: 0,
},
maxDamage: {
type: Number,
default: 0,
},
speed: {
type: Number,
},
maxStack: {
type: Number,
},
},
{ timestamps: true }
);
const ItemModel = mongoose.model('Item', itemSchema);
module.exports = ItemModel;

You can use something like this:
module.exports.getPlayer = async (playerId) => {
return await Player.findOne({ playerId: playerId }).populate({
path: "inventory",
populate: {
path: "item",
},
});
};

Related

MongoDB geting an empty array from an aggregation

So I created this controller to get me the sum of all the orders totalPrice made by months
const getMonthlyOrderTotal = async (req, res) => {
try {
const year = req.params.year;
const aggregatePipeline = [
{
$match: {
createdAt: {
$gte: new Date(`${year}-01-01T00:00:00.000`),
$lt: new Date(`${year}-12-31T23:59:59.999`)
}
}
},
{
$group: {
_id: {
$month: "$createdAt"
},
total: {
$sum: "$totalPrice"
}
}
},
{
$sort: {
_id: 1
}
}
];
const orderTotals = await Order.aggregate(aggregatePipeline);
res.json(orderTotals);
} catch (err) {
res.status(500).json({ message: err.message });
}
};
this is the orderModel I am using
import mongoose from "mongoose";
const orderSchema = mongoose.Schema(
{
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "User",
},
client: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "Client",
},
orderItems: [
{
name: { type: String, required: true },
qty: { type: Number, required: true },
image: { type: String },
price: { type: Number, required: true },
product: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "Product",
},
},
],
totalPrice: {
type: Number,
required: true,
default: 0.0,
},
taxPrice: {
type: Number,
required: true,
default: 0.0,
},
isPaid: {
type: Boolean,
required: true,
default: false,
},
paidAt: {
type: Date,
},
isDelivered: {
type: Boolean,
required: true,
default: false,
},
deliveredAt: {
type: Date,
},
},
{
timestamps: true,
}
);
const Order = mongoose.model("Order", orderSchema);
export default Order;
and when I try to test this API in postman "http://localhost:5001/orders/orderstotal/2022" I always get an empty array even though there is stored data in mongoDB orders Collection

NodeJS and mongoose CastError: Cast to [ObjectId] failed for value

I am using molecularjs actions to enter data into my database. Primarily I am using NodeJS with mongoose to define models. So far I am able to create a model but when I try to create an one-to-many relation it gives me this error
errors: { 'deliveryParcels.0': CastError: Cast to [ObjectId] failed
for value "[{"parcelId":"LHE660851871415","address":"24 E muhafiz town
lahore","type":"COD","addressLocation":{"lat":31.4532941,"lng":74.2166403},"customerName":"ALI
MALHI","customerPhone":"3314488396","processingStatus":"pending","amount":2500,"vendorOrderId":"other"}]"
at path "deliveryParcels.0"
I want both collections rider-tasks and delivery-parcels to be created simultaneously but only the rider-tasks collection gets created but not the delivery-parcels. What am I doing wrong in my schema?
Here are my models
rider-tasks model
import mongoose, { Schema } from "mongoose";
import { getDbConnection } from "../../../utils/db/db-connect";
/* eslint-disable id-blacklist */
import { IRiderTaskModel } from "../interfaces/rider-task.interface";
const RiderTaskSchema: Schema<IRiderTaskModel> = new Schema<IRiderTaskModel>(
{
_id: { type: String, required: true, unique: true },
riderId: { type: String, required: true },
totalAmount: { type: Number, required: true },
riderName: { type: String, required: true },
cityId: { type: String, required: true },
cityName: { type: String, required: true },
status: { type: String, required: true },
adminName: { type: String, required: true },
adminId: { type: String, required: true },
createdAt: { type: Date, required: true },
deliveryParcels: [
{
type: Schema.Types.ObjectId,
ref: "delivery-parcels",
},
],
},
{ _id: true }
);
const RiderTask = getDbConnection("deliveryManagement").model<IRiderTaskModel>(
"rider-tasks",
RiderTaskSchema
);
export { RiderTask };
delivery-parcels
import mongoose, { Schema } from "mongoose";
import { getDbConnection } from "../../../utils/db/db-connect";
import { IDeliveryParcelModel } from "../interfaces/delivery-parcels.interface";
const DeliveryParcelsSchema: Schema<IDeliveryParcelModel> = new Schema<IDeliveryParcelModel>(
{
parcelId: { type: String, required: true },
address: { type: String, required: true },
addressLocation: {
lat: { type: Number, required: true },
lng: { type: Number, required: true },
},
customerName: { type: String, required: true },
customerPhone: { type: String, required: true },
processingStatus: { type: String, required: true },
amount: { type: Number, required: true },
riderTaskId: { type: Schema.Types.String, ref: "rider-tasks" },
type: { type: String, required: true, enum: ["COD", "NONCOD"] },
postedStatus: {
status: { type: String },
statusKey: { type: String },
signature: { type: String },
reason: { type: String },
checkBoxData: [{ type: String }],
adminId: { type: String },
adminName: { type: String },
},
}
);
const DeliveryParcels = getDbConnection(
"deliveryManagement"
).model<IDeliveryParcelModel>("delivery-parcels", DeliveryParcelsSchema);
export { DeliveryParcels };
My service
private async createdRiderTaskHandler(ctx: Context<IRiderTaskModel>) {
const { params } = ctx;
const finalData: IParcelData[][] = await Promise.all(
params.deliveryParcels.map((parcel) =>
this.broker.call("parcel-data.getParcels", {
id: parcel.parcelId,
})
)
);
const wrongParcels: string | any[] = [];
const check = finalData[0].filter(
(data) => data.currentStatus.status === "Dispatched"
)[0];
if (check) {
wrongParcels.push(check.parcelId);
const parcel = wrongParcels.join("\r\n");
throw new Error(
'These parcels "' + parcel + '" have already been dispatched'
);
}
const codAmount = params.deliveryParcels
.filter((data, i) => data.type === "COD")
.reduce((total, b) => total + b.amount, 0);
const customId = "RTD-" + Math.floor(100000 + Math.random() * 900000);
try {
const taskData = omit({
...params,
_id: customId,
totalAmount: codAmount,
forceAssignment: false,
createdAt: new Date(),
});
const data = await RiderTask.create(taskData);
return { message: data };
} catch (error) {
this.logger.error(error);
return error;
}
}

Trying to push the value to non-existing field but it won't push using Mongoose

I've been trying updateOne, findOneAndUpdate, and update. Nothing has worked. findOne() operation returns the correct documents.
userProfileModel.updateOne(
{ userEmail },
{
$push: {
userFavLocation: payload,
},
},
(err, result) => {
console.log(err);
console.log(result);
}
);
I get this but no change in my document.
{ ok: 0, n: 0, nModified: 0 }
userEmail and payload have the correct value. When I do findOneAndUpdate, it returns correct document but won't push the value.
This is the Schem for the user profile
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const UserProfileSchema = new Schema(
{
userEmail: {
type: String,
required: true,
unique: true,
},
userProfilePictureUrl: {
type: String,
},
userSignUpDate: {
type: Date,
},
userId: {
type: String,
required: true,
unique: true,
},
userFirstName: {
type: String,
required: true,
},
userLastName: {
type: String,
required: true,
},
userGender: {
type: String,
required: true,
},
userBirthday: {
type: Date,
required: true,
},
userCoordinates: {
type: {
type: String,
default: 'Point',
},
coordinates: {
type: [Number],
},
},
userFavFacilities: {
type: Object,
},
userHometown: {
address: Object,
},
userContact: {
friends: Object,
},
userOrganizations: {
organizations: Object,
},
userMessages: {
type: Object,
},
userAlerts: {
type: Object,
},
userRoles: Object,
userFacilities: Object,
},
{ collection: 'userprofilemodels' }
);
UserProfileSchema.index({ location: '2dsphere' });
module.exports = UserProfile = mongoose.model(
'userprofilemodels',
UserProfileSchema
);
You have to add the userFavLocation field to your schema or mongoose won't perform the update.
const UserProfileSchema = new Schema(
{
userEmail: {
type: String,
required: true,
unique: true,
},
userFavLocation: [PUT_ARRAY_ITEM_TYPE_HERE],
...
}
}

Mongoose won't populate an array or object

Im having an issue where i have a Fixtures schema like so:
const FixtureSchema = mongoose.Schema({
home: {
club: {
type: mongoose.Schema.Types.ObjectId,
ref: 'club'
},
score: {
type: Number,
default: 0
},
scorers: [
{
player: {
type: mongoose.Schema.Types.ObjectId,
ref: 'player'
}
}
]
},
away: {
club: {
type: mongoose.Schema.Types.ObjectId,
ref: 'club'
},
score: {
type: Number,
default: 0
},
scorers: [
{
player: {
type: mongoose.Schema.Types.ObjectId,
ref: 'player'
}
}
]
},
match_num: {
type: Number,
required: true
},
date: {
type: Date,
default: Date.now()
}
});
module.exports = Fixture = mongoose.model('fixture', FixtureSchema);
Club schema:
const ClubSchema = new mongoose.Schema({
name: {
type: String,
required: true
}
});
module.exports = Club = mongoose.model('club', ClubSchema);
Player Schema:
const PlayerSchema = new mongoose.Schema({
name: {
type: String,
require: true
},
club: {
type: mongoose.Schema.Types.ObjectId,
ref: 'club'
},
stats: {
starts: {
type: Number,
default: 0
},
goals: {
type: Number,
default: 0
},
assists: {
type: Number,
default: 0
},
goal_streak: {
type: Number,
default: 0
}
}
});
module.exports = Player = mongoose.model('player', PlayerSchema);
I make a request like this:
router.get('/', async (req, res) => {
try {
const fixture = await Fixture.find()
.populate('player', ['name'])
.populate('club', ['name']);
res.json(fixture);
} catch (err) {
console.error(err.message);
}
});
Postman get request gives me:
[
{
"home": {
"score": 1,
"club": "5e3dfcffb52ec61c30fc44cd",
"scorers": [
{
"_id": "5e4aac5d81d92b368398bd4d",
"player": "5e3dfddd4884f51cb7ee61e9"
}
]
},
"away": {
"score": 0,
"club": "5e3dfd18b52ec61c30fc44ce",
"scorers": []
},
"date": "2020-02-17T15:07:57.229Z",
"_id": "5e4aac5d81d92b368398bd4c",
"match_num": 1,
"__v": 0
}
]
Why is it not populating the name of the player or the club??? It works when it is not stored in an array or object.. Ive tried looking for advice on other topics but cant find anything relating to this..
Thanks for any replies and advice
Turns out it was the same as the other questions!!! lol..
If i change :
.populate('club', ['name']);
to
.populate('home.club', ['name']);
and so on
it solves it...

Populate() Mongoose is not returning joined data

Well, I´am trying to retrieve Name and Lastname from user who created the document but it´s not working, it still returning Mongo´s Id
This is my areas model
var mongo = require('mongoose'),
validator = require('mongoose-unique-validator'),
Schema = mongo.Schema
var model = new Schema({
NAME: { type: String, required: true, unique: true, max: 50, min: 3 },
STATUS: { type: String, default: 'active' },
ADDED_BY: { type: Schema.Types.ObjectId, ref: 'users' },
ADDED_DATE: { type: Date, default: Date.now }
}, {collection :'areas'})
model.plugin( validator, { message: 'The {PATH} is not valid or duplicated' } )
module.exports = mongo.model('Area', model )
This is the user model
var mongo = require('mongoose'),
validator = require('mongoose-unique-validator'),
Schema = mongo.Schema
var userSchema = new Schema({
PERSONAL_DATA: {
NAME: { type: String, required: [ true, 'The name is necessary' ], max: 50 },
LAST_NAME: { type: String, required: [ true, 'the lastname is necessary' ], max: 100 },
PHOTO: { type: String, max: 100 },
BIRTHDAY: { type: Date },
MARITIAL_STATUS: { type: Schema.Types.ObjectId, ref: 'maritial_statuses' },
GENDER: { type: String, max: 1 },
EMAIL: { type: String, required: true },
},
COMPANY_DATA: {
JOB: { type: Schema.Types.ObjectId, ref: 'jobs' },
AREA: { type: Schema.Types.ObjectId, ref: 'areas' },
ROLE: { type: Schema.Types.ObjectId, ref: 'roles' },
BOSS: { type: Schema.Types.ObjectId, ref: 'users' },
}
}, { collection: 'users' } )
model.plugin( validator, { message: 'The {PATH} is not valid or duplicated' } )
module.exports = mongo.model('User', userSchema )
And this is my areas route
var express = require('express'),
model = require('../../models/catalogs/areas'),
app = express()
app.get('/:from', (req, res) => {
var from = parseInt( req.params.from )
model.find()
.sort('NAME').populate({ path: 'users', select: 'NAME LAST_NAME'})
.limit(10).skip(from)
.exec((error, data) => {
if (error) {
return res.status(500).json({
success: false,
error
})
}
res.status(200).json({
success: true,
data
})
})
})
module.exports = app
The response is
{
"success": true,
"data": [
{
"STATUS": "active",
"_id": "5c547f4adadf433914f72c8c",
"NAME": "Contabilidad y Finanzas",
"ADDED_BY": "5c4f562deec6f4defeea759b",
"ADDED_DATE": "2019-02-01T17:18:02.680Z",
"__v": 0
},
{
"STATUS": "active",
"_id": "5c547f3edadf433914f72c8b",
"NAME": "Tecnologías",
"ADDED_BY": "5c4f562deec6f4defeea759b",
"ADDED_DATE": "2019-02-01T17:17:50.579Z",
"__v": 0
}
]
}
As you seen, ADDED_BY is a field joined to Users and I want to retrieve that information. I don´t know what is wrong with my code.

Resources