Mongoose Populate Returns Some Empty Objects - node.js

I have 1 main collection and 1 collection with a ref to the main one. Code looks like :
// Ref schema
const onlineSchema = mongoose.Schema({
_id: {
type: Number,
ref: 'Player',
unique: true
}
}, {
timestamps: true
});
//main schema
const playerSchema = mongoose.Schema({
_id: { // User ID
type: Number,
required: true,
unique: true,
default: 0
},
firstname: {
type: String
},
name: {
type: String,
required: true
},
lastname: {
type: String
},
barfoo: {
type: Boolean
}
...
})
I populate it with this code :
var baz = bar;
...
Online.find().populate({
path: '_id',
match: {
[ baz + 'foo']: true
}
}).exec(function(err, online) {
if (err) {
winston.error(err);
} else {
winston.error(util.inspect(online, {
showHidden: false,
depth: null
}));
}
});
If there are 10 elements in online and only 7 match [ baz + 'foo']: true I get 7 proper arrays and 3 empty arrays that look like this:
{ updatedAt: 2016-12-23T18:00:32.725Z,
createdAt: 2016-12-23T18:00:32.725Z,
_id: null,
__v: 0 },
Why is this happening and how to I filter the final result so it only shows the matching elements?
I can use filter to remove the null arrays after I get the result but I'd like to know how to prevent the the query from passing null arrays in the first place.

Why is this happening ?
This is happening because you get all the documents with Online.find() but the player will be populated only for records that match your condition. Your match is for the populate, not for the find() query.
How do I filter the final result so it only shows the matching
elements ?
You cant find a nested elements of a referenced collections since there is no join in MongoDB. But you can :
keep your schema and use aggregation with $lookup :
Online.aggregate(
[{
$lookup: {
from: "players",
localField: "_id",
foreignField: "_id",
as: "players"
}
}, {
$unwind: "$players"
}, {
$match: {
'players.barfoo': true
}
}],
function(err, result) {
console.log(result);
});
change your schema to include Player as a subdocument :
const playerSchema = new mongoose.Schema({
//...
});
const onlineSchema = new mongoose.Schema({
player: playerSchema
}, {
timestamps: true
});
var Online = mongoose.model('Online', onlineSchema);
Online.find({'player.barfoo':true}).exec(function(err, online) {
console.log(online);
});

Dont make _id the reference of another schema, instead make another field name player and give reference through that.
const onlineSchema = mongoose.Schema({
player: {
type: Number,
ref: 'Player',
unique: true
}
}, {
timestamps: true
});
Population:
Online.find().populate({
path: 'player',
match: {
[ baz + 'foo']: true
}
}).exec(...);

dont use _id to ref field.. because its default filed in mongoDB to create index unique.. change you're field name

Related

MongoDB remove all elements from the array inside one document

I want to delete all elements from an array in my document..
I have an array of object id's
My model looks like this:
const TypeSchema = new mongoose.Schema({
ISOklasifikacija: { type: String, requred: false },
profilUIC: { type: String, requred: false },
certificateId: [
{
type: Schema.Types.ObjectId,
ref: "Certificates",
required: false,
},
],
});
My query for clearing the array is:
let type = await Type.findOneAndUpdate(
{
_id: req.params.typeId,
},
{
$set: { certificateId: [] },
},
{ multi: true }
);
console.log(type);
It nevers clear the array. In console I get: null and this id exists which I am passing in
Thanks for help

Pushing items to array in mongoose

i am trying to create items to specific id of collection using nodejs and mongoose but i am getting CastError which i shared below picture. So goal is, I binded items id in collection schema because collection has many items and when i create items to the specific id of collection i want to push them to items array in the collection schema.
ERROR
ITEMS ROUTE
itemRouter.post("/:collectionId", JWTAuthMiddleware, async (req, res, next) => {
const {name} = req.body
if (req.params.collectionId.length !== 24)
return next(createHttpError(400, "Invalid ID"));
const collection = await CollectionModal.findByIdAndUpdate(
req.params.collectionId,
{
$push : { items: { ...req.body, owner: req.user._id, id: uuidv4() } },
},
{ new: true }
);
if (!collection)
return next(
createHttpError(
400,
`The id ${req.params.collectionId} does not match any collections`
)
);
res.send(collection);
});
Collection schema
import mongoose from "mongoose";
const { Schema, model } = mongoose;
const collectionSchema = new Schema(
{
name: { type: String },
description: { type: String },
topic: { type: String },
image: { type: String },
additionalCustomFields: {
fieldNumber: { type: Number },
fieldName: { type: String },
fieldType: { type: String },
fieldChecked: { type: Boolean },
fieldDate: { type: Date },
},
owner: { type: Schema.Types.ObjectId, ref: "User" },
items: [{ type: Schema.Types.ObjectId, ref: "Item" }],
},
{ timestamps: true }
);
// collectionSchema.index({ "$**": "text" });
export default model("Collection", collectionSchema);
ITEM schema
import mongoose from "mongoose";
const { Schema, model } = mongoose;
const itemSchema = new Schema(
{
name: { type: String },
description: { type: String },
topic: { type: String },
image: { type: String },
comments: [
{
user: { type: Schema.Types.ObjectId, ref: "User", required: true },
text: { type: String },
},
],
tags: { type: String },
owner: { type: Schema.Types.ObjectId, ref: "User" },
likes: [{ type: Schema.Types.ObjectId, ref: "User" }],
collections: { type: Schema.Types.ObjectId, ref: "Collection" },
},
{ timestamps: true }
);
itemSchema.index({ "$**": "text" });
export default model("Item", itemSchema);
Are you sure that using referenced documents is the correct way to go here? Mongoose is complaining because you are trying to push whole objects to the "items" array, instead of ObjectIds of referenced documents.
You have 2 choices here:
1. use embedded documents
This way you can easily store objects directly to the items array, which does not allow you to store the Items in a seperate collection
2. first create Item documents, then push the reference to items array
In this case you have to first create the items in Item collection. Afterwards you can map the result to only ObjectIds and push these to items array of your collection document.

Populating array of collection which contains references to another collections returns empty array

I have two models Vote and Link,I am trying to populate the votes array in link model,The votes array contains id's that references to the collection Vote,which only contains two fields link and User which also refs to same link model mentioned below and a user model respectively
link Schema:-
const linkSchema = new mongoose.Schema(
{
description: {
type: String,
trim: true,
},
url: {
type: String,
trim: true,
},
postedBy: {
type: mongoose.Types.ObjectId,
ref: "User",
},
votes: [{ type: mongoose.Types.ObjectId, ref: "Vote" }],
},
{
timestamps: true,
}
);
linkSchema.index({ description: "text" });
linkSchema.index({ createdAt: -1 });
module.exports = mongoose.model("Link", linkSchema);
Vote schema:-
const mongoose = require("mongoose");
const voteSchema = new mongoose.Schema({
link: { type: mongoose.Types.ObjectId, ref: "Link" },
user: { type: mongoose.Types.ObjectId, ref: "User" },
});
module.exports = mongoose.model("Vote", voteSchema);
but when i try to get the votes of a link,it always return an empty array ,My function:-
const votes = async ({ id }) => {
const linkData = await Link.findById(id).populate("votes").exec();
console.log(linkData);
};
Output Data:-
{
votes: [], //empty always
_id: 5ecb21059a157117c03d4fac,
url: 'https://www.apollographql.com/docs/react/',
description: 'The best GraphQL client for React',
postedBy: 5ec92a58bf38c32b38400705,
createdAt: 2020-05-25T01:36:05.892Z,
updatedAt: 2020-05-25T01:37:52.266Z,
__v: 0
}
Instead of populate(), you can use aggregate() to get your desired output. This should probably work in your case:
Link.aggregate([
{
$match: {
_id: { $in: [mongoose.Types.ObjectId(id)] // as suggested by the questioner
}
},
{
$lookup: {
from: "vote", // collection to join
localField: "votes", // field from the input documents (filtered after _id is matched)
foreignField: "link", // field to compare with, from other collection
as: "linkData" // output array name
}
}
])
Let me know in the comments.

Why mongoose return empty results when I specify the path?

I defined two schema in mongoose: DocSchema has DocTypeSchema reference.
const DocTypeSchema = new Schema({
name: { type: String, unique: true, index: true }
});
export const DocType = mongoose.model('Doc-Type', DocTypeSchema);
const DocSchema = new Schema(
{
name: { type: String },
type: { type: Schema.Types.ObjectId, ref: 'Doc-Type' },
description: { type: String },
}
);
When I try to get the docs with type by the name I gets empty results.
How can I solve this?
docs.find({ 'type.name': 'VideoBook' }, { limit: 30 })
I don't want to get the type object inside the docs array. just to gets the docs that match to the query.
You need tu user .aggregate
Specify the collection:
const DocTypeSchema = new Schema({
name: { type: String, unique: true, index: true }
},{ collection: 'docType' });
Simple example :
const docs = await Doc.aggregate([
{
$lookup: {
from: 'docType',
localField: 'type',
foreignField: 'name',
as: 'magic'
}
},
{$unwind: '$magic'},
{
$match: {
$and: {
"magic.name": 'VideoBook'
}
}
},
{ $limit : 30 }
])

remove item from nested array using $pull

Hey I have one problem with remove nested array from my database, I would like use findOneAndUpdate and $pull. My point is to remove item from reply array. I try find comments item by _id and remove this reply item, but this not working. Please look on my code below.
my schema
var productSchema = new Schema({
description: [{
type: String,
require: true,
}],
comments: [{
body: {
type: String,
trim: true,
},
author: {
type: String,
},
date: {
type: Date,
},
reply: [{
body: {
type: String,
trim: true,
},
author: {
type: String,
}, date: {
type: Date,
}
}]
}]
});
api
router.put('/dashboard/admin/komentarz/odpowiedz/usun/', function(req, res) {
var currentCourse = req.body._id; // main item id
var deleteReply = req.body.reply; // reply item id
var commentId = req.body.commentId // comments item id
Product.findOneAndUpdate({ _id: currentCourse }, { $pull: { reply: req.body.reply }}, function(err, product){
//some code
})
Take ref from Mongoose, pull from subdocument
Product.findOneAndUpdate({
_id: currentCourse,
// make sure sub document have a unique field let take _id
"comments._id" : 'commentId'
},
{
$pull: {
"comments.$.reply": {
_id:'replyId'
}
}
},
{
upsert:false,
new:true
}, function(err, product){
//some code
})

Resources