Mongoose query compare ObjectId - node.js

I'm trying to fetch some documents from my db. In each document, there is a field called 'owner' which is an ObjectId of a user. I want to fetch all of the documents of a specific user. I have the user id and when I'm trying to do something like this:
exports.getBoxes = function(req, res) {
const { user } = res.locals;
const query = db.Box.find();
query.where('owner').equals(user._id);
query.exec(function(err, boxes) {
console.log(boxes);
});
}
I get an empty array. I saw in my db and there are many boxes that corresponds to this query. What's wrong with it?
UPDATE
Here is my schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const timestamps = require('mongoose-timestamps');
const BoxSchema = new Schema({
description: {
type: String,
trim: true
},
producer: {
type: String,
trim: true
},
cycle: {
type: String,
trim: true
},
owner: {
type: Schema.ObjectId,
ref: 'Supplier'
},
event: {
type: Schema.ObjectId,
ref: 'Event'
},
type: {
type: String,
enum: []
},
creditTerms: {
type: String,
enum: ['Cash', '30 Days', '60 Days', '90 Days', '120 Days']
},
bids: [{
type: Schema.ObjectId,
ref: 'Bid'
}],
looking: [{
type: Schema.ObjectId,
ref: 'User'
}],
sold: Boolean,
paid: Boolean,
delivered: Boolean,
sealed: Boolean,
initialPrice: Number,
value: Number,
cts: Number,
ppc: Number,
finalPrice: Number
});
BoxSchema.plugin(timestamps);
module.exports = mongoose.model('Box', BoxSchema);
And here is an example of documents that I try to fetch:
https://i.gyazo.com/38f2d16d6831b831adb3cc448ef74d01.png

Okay guys I managed to solve this problem. The problem was that the owner field in the box schema referenced a Supplier object, not a User object. So I solved it like so:
const { user } = res.locals;
return db.Supplier.findOne({ userId: user._id })
.populate('boxes').exec(function(err, supplier) {
if(err || !supplier) return res.sendStatus(404);
res.json(supplier.boxes);
});

Related

POPULATION ISSUE: Mongoose/Express

I'm trying to have each record attached to a user who created it,
and every user have their records attached.
Here are my schemas:
1.The Records schema:
const mongoose = require('mongoose')
const RecordsSchema = new mongoose.Schema(
{
Title: { type: String, required: true },
postedby:[{
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
}],
Author: { type: String, required: true},
ISBN: { type: String, required: true },
Review: { type: String },
SelectedFile: { type: String },
Likes: { type: Number, default:0},
Date: { type: Date, default: Date.now()}
});
module.exports = Records = mongoose.model('record', RecordsSchema', 'record');`
Here is the The user Schema:
const mongoose = require('mongoose')
const userSchema = new mongoose.Schema(
{
username: { type: String},
email: { type: String, required: true ,unique:true},
records:[{
type: [mongoose.Schema.Types.ObjectId],
ref: 'record' }],
password: { type: String, required: true},
Date: { type: Date, default: Date.now(), immutable: true }
});
module.exports = User = mongoose.model('user', userSchema,'user');
The express route for getting a record:
router.get('/postedby/', (req, res) => {
Records.find(req.params.id)
.populate('postedby')
.exec()
.then(post =>{
if (!post) {
return res.status(400).json({ msg: 'Add Posts' });
}
else return res.json(post);
}).catch (err => console.error(err))
});
Result of the route:
{
"postedby": [],
"Likes": 0,
"_id": "5fed8c12a4fb2c1e98ef09f6",
"Title": "New Age",
"Author": "Situma Prisco",
"ISBN": "23422",
"SelectedFile": "",
"Review": "",
"Date": "2020-12-31T08:30:10.321Z",
"__v": 0
},
I'm getting a blank Array on the populated user field(posteddby) .
Please help, What am I doing wrong? And yes, i do have a User Logged in
You are too close.
In your schema definition, postedby field itself is an array. Hence you can simply define it like :
postedby:[{
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
}]
Make sure that the ObjectID is stored properly in the postedby array.
Also, you are trying to find a single record based on the ID, hence you can use findById(req.params.id) or findOne({_id:req.params.id}) which would return a single document.

How to poulate a a field in a mongoose schema which is refrenced to another schema , and this schema is further refrenced to another schema?

The field (viewed_posts) i want to populate in User Schema:
viewed_posts: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Viewed"
}
]
Viewed Schema :
var viewedSchema = new mongoose.Schema({
hitsByUser: {type: Number, default: 0},
viewsByUser: {type: Number, default: 0},
post: {
type: mongoose.Schema.Types.ObjectId,
ref: "Post"
}
});
Post Schema :
var mongoose = require('mongoose');
var passportLocalMongoose = require("passport-local-mongoose");
var uniqueValidator = require('mongoose-unique-validator');
var marked = require('marked');
var slugify = require('slugify');
// this is done for sanitizing html so that user cannot write a script in the input
const createDomPurify = require('dompurify')
const {JSDOM} = require('jsdom')
const dompurify = createDomPurify(new JSDOM().window)
var postSchema = new mongoose.Schema({
postNumber: Number,
title: String,
content: String,
subject: String, // currently we have 4 subjects so one out of 4 subjects will be stored here
likes: {type:Number,default:0},
// likes: {
// id:{
// type: mongoose.Schema.Types.ObjectId,
// ref: "User"
// }
// },
views: {type:Number,default:0},
actualViews: {type:Number,default:0},
shares: Number,
isReviewedByAdmin: {type: Boolean, default: false},
isReviewedByAuditor: {type: Boolean, default: false},
author: {
id:{
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: {
type: String
}
},
publish_date: {
type: String,
default: Date.now
},
publishDay: String,
slug: {
type: String,
required: true,
},
sanitizedHtml: {
type: String,
required: true
},
imagename: String //2002-12-09
});
I wish to see whole structure printed , but i can only populate viewed_posts, how can i populate "post"
which is inside viewed Schema and see here:
User.findById(req.user._id).populate("viewed_posts").exec((err,ans)=>{
if(err) console.log(err)
else{
console.log("this is the answer ",ans)
}})
The output i get:
},
{
hitsByUser: 0,
viewsByUser: 0,
_id: 5f9e85aeec37700f54a4d029,
post: 5f9a93d38d7cf8544ce9cc21,
__v: 0
},
{
hitsByUser: 0,
viewsByUser: 0,
_id: 5f9e85d61841000478c85f8a,
post: 5f82773f1998150024d4c8fc,
__v: 0
},
But i expect this post to be expanded too, instead of just showing id , How can i achieve it. Any Help Would be appreciated.
Mongoose supports nested populating (see in the docs: https://mongoosejs.com/docs/populate.html#deep-populate).
Note that you have to specify your model name of post schema where I´ve put the "post-model-name" placeholder.
So you could try something like this:
User.findById(req.user._id)
.populate({
path: 'viewed_posts',
populate: {
path: 'post',
model: 'post-model-name'
}
})
.exec();

Mongoose(mongoDB) Linking multiple schema's

Im relatively new to MongoDB and Mongoose. Im much used to MySQL so in used to inner joining tables on calls. Ive read a lot that you can link two Mongoose Schemas to achieve the same outcome. How would like like the two schemas together to when I make a call to get a chore by id it'll return the chore and then for the assignedTo & createdBy have the user scheme data for the said userId?
Chore Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ChoreSchema = new Schema({
title: {
type: String,
required: true
},
desc: {
type: String,
required: true
},
time: {
type: Number,
required: true
},
reaccurance: {
type: [{
type: String,
enum: ['Daily', 'Weekly', 'Bi-Weekly', 'Monthly']
}]
},
reward: {
type: Number,
required: true
},
retryDeduction: {
type: Number,
required: false
},
createdDate: {
type: Date,
default: Date.now
},
createdBy: {
type: String,
required: true
},
dueDate: {
type: Date,
required: true
},
status: {
type: [{
type: String,
enum: ['new', 'pending', 'rejected', 'completed', 'pastDue']
}],
default: ['new']
},
retryCount: {
type: Number,
default: 0,
required: false
},
rejectedReason: {
type: String,
required: false
},
familyId: {
type: String,
required: true
},
assignedTo: {
type: String,
required: false,
default: ""
}
});
let Chores = module.exports = mongoose.model('Chores', ChoreSchema);
module.exports.get = function (callback, limit) {
Chores.find(callback).limit(limit);
};
User Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
role: {
type: [{
type: String,
enum: ['Adult', 'Child']
}]
},
birthday: {
type: String,
required: false
},
familyId: {
type: String,
required: true
},
balance: {
type: Number,
required: true,
default: 0.00
}
});
let Users = module.exports = mongoose.model('Users', UserSchema);
module.exports.get = function (callback, limit) {
Users.find(callback).limit(limit);
};
Im trying to link ChoreSchema.createdBy & ChoreScheme.assignedTo by UserSchema._id
How I make the call in Node.js:
exports.index = function(req, res) {
Chore.get(function(err, chore) {
if (err)
res.send(err);
res.json({
message: 'Chore List',
data: chore
});
});
};
Mongoose has a more powerful alternative called populate(),
which lets you reference documents in other collections.
https://mongoosejs.com/docs/populate.html
Here is how you can link ChoreSchema.createdBy and ChoreScheme.assignedTo by UserSchema._id
var mongoose = require('mongoose');
const { Schema, Types } = mongoose;
var UserSchema = new Schema({
firstName: { type: String, required: true },
...
})
var ChoreSchema = new Schema({
title: { type: String, required: true },
...
//The ref option is what tells Mongoose which model to use during population
assignedTo: { type: Types.ObjectId, ref: 'Users' },
createdBy: { type: Types.ObjectId, ref: 'Users' },
})
let Chores = mongoose.model('Chores', ChoreSchema);
let Users = mongoose.model('Users', UserSchema);
Then in your express route handler you can populate assignedTo & createdBy like this
router.get('/chores/:id', function (req, res) {
const choreId = req.params.id;
Chores.find({ _id: choreId })
.populate('createdBy') // populate createdBy
.populate('assignedTo') // populate assignedTo
.exec(function (err, chore) {
if(err) {
return res.send(err)
}
res.json({ message: 'Chore List', data: chore });
});
})

Mongoose: Populate referenced SubDocument

I've the following schemas:
Occurence that references a competence in the CostCenter subdocuments competences.
const OccurrenceSchema = new mongoose.Schema({
date: {
type: Date,
default: Date.now,
},
competence: {
type: mongoose.Schema.Types.ObjectId,
ref: 'CostCenter.competences',
},
...
})
CostCenter where I've an array of subdocuments that can be ref by Occurence.
const CostCenterSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
},
competences: [CompetenceSchema],
});
And finally the Competence.
const CompetenceSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
},
});
When I try to populate the competence, I get "Schema hasn't been registered for model \"CostCenter.competences\".\nUse mongoose.model(name, schema)".
const occurrence_list = (request, response) => {
Occurrence.find()
.populate('occurrence origin tag entity method priority competence')
.then(occurrences => response.send(occurrences))
.catch(e => response.send(e));
};
How can I possibly populate the occurrence when referencing a subdocument?
First, you need to change your Occurrence model to this
const OccurrenceSchema = new mongoose.Schema({
date: { type: Date, default: Date.now },
competence: { type: mongoose.Schema.Types.ObjectId, ref:'CostCenter' }
});
mongoose.model('Occurrence', OccurrenceSchema);
and CostCenter model:
const CostCenterSchema = new mongoose.Schema({
name: { type: String, required: true, trim: true },
competences:[{ type: mongoose.Schema.Types.ObjectId, ref: 'Competence' }],
});
mongoose.model('CostCenter', CostCenterSchema);
finally Competence model:
const CompetenceSchema = new mongoose.Schema({
name: { type: String, required: true, trim: true }
});
mongoose.model('Competence', CompetenceSchema);
To populate the competence from Occurrence, you can do so:
Occurrence.find({ your_query })
.populate('competence')
.then(occurrences => response.send(occurrences))
.catch(e => response.send(e));
Hope it helps!

E11000 duplicate key error with MongoDB/Mongoose

I have a user model schema, a work model schema, and a critique model schema. The relationship between these schema's is a user can submit many works (like blog posts), and can comment/review (which we call critiques) other people's posts (works).
So when a user submits a critique (think of it like a review), this is my post route. I find the work by the id, then create a new critique model object, and pass that to the .create() mongoose function. All goes seemingly well until I hit the foundWork.critiques.push(createdCritique) line. the console log errors out saying:
BulkWriteError: E11000 duplicate key error collection: zapper.critiques index: username_1 dup key: { : null }
Obviously, it is saying that there are two username keys in the objects and they're conflicting with each other, but I'm not familiar enough with this to find the root of the issue and fix it in the mongoose models. The models are below. If anyone could help, that'd be greatly appreciated.
// post route for getting the review
router.post('/:id', isLoggedIn, function(req, res) {
Work.findById(req.params.id, function(err, foundWork) {
if (err) {
console.log(err);
} else {
// create a new critique
var newCritique = new Critique ({
reviewerName: {
id: req.user._id,
username: req.user.username
},
work: {
id: foundWork._id,
title: foundWork.title
},
critique : req.body.critique,
date: Date.now(),
rating: 0
});
// save new critique to db
Critique.create(newCritique, function(err, createdCritique) {
if (err) {
console.log(err)
} else {
console.log("Created critique is ");
console.log(createdCritique);
// push the new critique into array of critiques of the work
foundWork.critiques.push(createdCritique);
// save to db
foundWork.save();
}
});
}
});
User model:
var mongoose = require('mongoose');
var passportLocalMongoose = require('passport-local-mongoose');
var UserSchema = new mongoose.Schema({
firstname: String,
lastname: String,
username: String,
password: String,
email: String,
zip: String,
bio: {
type: String,
default: ''
},
influences: {
type: String,
default: ''
},
favBooks: {
type: String,
default: ''
},
notWriting: {
type: String,
default: ''
},
favHero: {
type: String,
default: ''
},
favVillain: {
type: String,
default: ''
},
works: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Work'
}
],
critiques: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Critique'
}
],
friends: [
{
friendId: String,
friendName : String,
friendPic: String
}
],
friendRequests: [
{
sendingFriendId: String,
sendingFriendName : String,
sendingFriendPic: String
}
],
createdDate: {
type: Date,
default: Date.now
},
lastLogin: {
type: Date,
default: Date.now
}
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", UserSchema);
Work model:
var mongoose = require('mongoose');
var WorkSchema = new mongoose.Schema({
title: String,
genre: String,
workType: String,
length: Number,
ageRange: String,
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
},
manuscriptText: String,
critiques: [
{
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Critique"
}
}
],
ratingNumber: [Number],
ratingSum: {
type: Number,
default: 0
},
date: {
type: Date,
default: Date.now
},
isPublic: {
type: Boolean,
default: true
}
});
module.exports = mongoose.model("Work", WorkSchema);
Critique model:
var mongoose = require('mongoose');
var passportLocalMongoose = require('passport-local-mongoose');
var CritiqueSchema = new mongoose.Schema({
reviewerName: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
},
work: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Work"
},
title: String
},
critique: String,
date: {
type: Date,
default: Date.now
},
rating: [Number]
});
CritiqueSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("Critique", CritiqueSchema);
When you create a unique index in MongoDB, the default behavior is that it will index null values also.
This means if you have a document in your collection with a username of null, you can not add another one with a username of null.
What you need is a sparse index which only indexes actual values (and ignores documents with null for that field).
Check this link It shows how to create a sparse index vs "normal" one in mongoose (index: true, vs spare: true). Most of the time you would want sparse indexes.

Resources