Model find subdocument array using user ID instead of [ [Object] ] - javascript

Objective
I'm trying to pull the qr documents attached to each user (which have a maximum of six) and return both the documents and subdocuments from the my mongoDB using below controller function.
Function
const Qr = require('../models/qr');
const mongoose = require('mongoose');
module.exports.showActivations = async (req, res) => {
const qrCodes = await Qr.find({ user: req.params.id });
console.log(qrCodes);
};
Expected results
An example below shows how I expected the results to be returned by showing the subdocument details within 'redirect'
{
_id: new ObjectId("6317ad944f0f1db567b9608c"),
redirect: [
name: "Test",
url: "https://testurl.com"
qrcode: "data:image/png;base64,iVBORwasdf..."
],
user: new ObjectId("6310a2da50448e982336784a"),
__v: 0
}
Actual behavior
I am receiving [ [Object] ] instead of the redirect subdocuments with each document.
{
_id: new ObjectId("6317ad944f0f1db567b9608c"),
redirect: [ [Object] ],
user: new ObjectId("6310a2da50448e982336784a"),
__v: 0
}
qr.js (model)
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const qrSchema = new Schema({
redirect: [{
name: String,
url: String,
qrcode: String,
}],
user: {
type: Schema.Types.ObjectId,
ref: 'User'
},
event: {
type: Schema.Types.ObjectId,
ref: 'Event'
},
});
module.exports = mongoose.model('Qr', qrSchema);
How do I yield the full qr document and subdocument details from my query?

Related

How can i reference another mongoose model for people who like a certain post?

I am currently trying to track which users like a certain post and based on that information there will be a 'liked' functionality on the post if the user already liked it. How would I go about this with these two current mongoose Schemas that I have?
User.js
const mongoose = require('mongoose')
const userSchema = mongoose.Schema({
username: String,
name: String,
email: String,
passwordHash: String,
dateOfBirth: Date,
location: String,
addictions: Array,
groups: Array,
biography: String,
posts: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}
],
profileImageURL: String,
following: Number,
followers: Number
})
userSchema.set('toJSON', {
transform: (document, returnedObject) => {
returnedObject.id = returnedObject._id.toString()
delete returnedObject.__v
// the passwordHash should not be revealed
delete returnedObject.passwordHash
}
})
const User = mongoose.model('User', userSchema)
module.exports = User
post.js
const mongoose = require('mongoose')
const postSchema = new mongoose.Schema({
text: {
type: String,
required: true
},
images: Array,
video: String,
gif: String,
date: Date,
tags: String,
likes: Number,
comments: Number,
shares: Number,
owner: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String,
replies: {
type: mongoose.Schema.Types.ObjectId,
ref: "Replies"
},
})
const Post = mongoose.model('Post', postSchema)
module.exports = Post
Should i make a separate schema for likes to achieve this or can it be done with the two schemas i already have defined? Thanks!

MongoDB not showing full document when queried [duplicate]

This question already has answers here:
Join two collections using mongoose and get data from both
(1 answer)
Mongoose populate does not populate array
(3 answers)
How to populate array of objects in mongoose
(2 answers)
Closed 11 months ago.
I am trying to pull data (particularly, the memberships array) from a certain user.
_id:623db49b9da63a7758307d68
email:"test#test.com"
username:"test"
salt:"eb83e08efef62630c46b1809c7671db2109ceccff9d66862c93df1e3825c1354"
hash:"e419edb1bc481d5c87dd223a7f1dc20bece679fad1a3c1afe05dfabba341c0a9913a60..."
__v:0
memberships:Array
0:623dc03f72436863b72c396e
1:623dc869b61c3799494b2657
However, when I request the document, it does not retrieve the memberships portion.
{
_id: new ObjectId("623db49b9da63a7758307d68"),
email: 'test#test.com',
username: 'test',
__v: 0
}
The code below is responsible for pulling the information from the DB and then also logs what was pulled.
module.exports.showContent = async(req, res, next) => {
try {
const user = await User.findById(req.user.id);
console.log(user)
res.render('index', { user })
} catch (e) {
console.log(e)
res.render('index')
}
}
Funny enough, if there is only one ref in the array, it will show up in the console.log!
{
_id: new ObjectId("623db49b9da63a7758307d68"),
email: 'test#test.com',
username: 'test',
__v: 0,
memberships: new ObjectId("623dc869b61c3799494b2657")
}
Schema model in question:
const UserSchema = new Schema({
email: {
type: String,
required: true,
unique: true,
},
username: {
type: String,
required: true,
unique: true,
},
roles: String,
memberships: {
type: Schema.Types.ObjectId,
ref: 'Community',
},
posts: {
type: Schema.Types.ObjectId,
ref: 'Posts',
},
comments: {
type: Schema.Types.ObjectId,
ref: 'Comments',
}
})
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model('User', UserSchema);
My other collections do not seem to have the same issue when it comes to refs inside an array.
I have tried using .populate() to populate the data. Tried using path: and populate: within the method. Tried googling the issue, but to no luck.

MongoDB Aggregate is not matching specific field

I'm new to Aggregation in MongoDB and I'm trying to understand the concepts of it by making examples.
I'm trying to paginate my subdocuments using aggregation but the returned document is always the overall values of all document's specific field.
I want to paginate my following field which contains an array of Object IDs.
I have this User Schema:
const UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true,
required: true
},
firstname: String,
lastname: String,
following: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}],
...
}, { timestamps: true, toJSON: { virtuals: true }, toObject: { getters: true, virtuals: true } });
Without aggregation, I am able to paginate following,
I have this route which gets the user's post by their username
router.get(
'/v1/:username/following',
isAuthenticated,
async (req, res, next) => {
try {
const { username } = req.params;
const { offset: off } = req.query;
let offset = 0;
if (typeof off !== undefined && !isNaN(off)) offset = parseInt(off);
const limit = 2;
const skip = offset * limit;
const user = await User
.findOne({ username })
.populate({
path: 'following',
select: 'profilePicture username fullname',
options: {
skip,
limit,
}
})
res.status(200).send(user.following);
} catch (e) {
console.log(e);
res.status(500).send(e)
}
}
);
And my pagination version using aggregate:
const following = await User.aggregate([
{
$match: { username }
},
{
$lookup: {
'from': User.collection.name,
'let': { 'following': '$following' },
'pipeline': [
{
$project: {
'fullname': 1,
'username': 1,
'profilePicture': 1
}
}
],
'as': 'following'
},
}, {
$project: {
'_id': 0,
'following': {
$slice: ['$following', skip, limit]
}
}
}
]);
Suppose I have this documents:
[
{
_id: '5fdgffdgfdgdsfsdfsf',
username: 'gagi',
following: []
},
{
_id: '5fgjhkljvlkdsjfsldkf',
username: 'kuku',
following: []
},
{
_id: '76jghkdfhasjhfsdkf',
username: 'john',
following: ['5fdgffdgfdgdsfsdfsf', '5fgjhkljvlkdsjfsldkf']
},
]
And when I test my route for user john: /john/following, everything is fine but when I test for different user which doesn't have any following: /gagi/following, the returned result is the same as john's following which aggregate doesn't seem to match user by username.
/john/following | following: 2
/kuku/following | following: 0
Aggregate result:
[
{
_id: '5fdgffdgfdgdsfsdfsf',
username: 'kuku',
...
},
{
_id: '5fgjhkljvlkdsjfsldkf',
username: 'gagi',
...
}
]
I expect /kuku/following to return an empty array [] but the result is same as john's. Actually, all username I test return the same result.
I'm thinking that there must be wrong with my implementation since I've only started exploring aggregation.
Mongoose uses a DBRef to be able to populate the field after it has been retrieved.
DBRefs are only handled on the client side, MongoDB aggregation does not have any operators for handling those.
The reason that aggregation pipeline is returning all of the users is the lookup's pipeline does not have a match stage, so all of the documents in the collection are selected and included in the lookup.
The sample document there is showing an array of strings instead of DBRefs, which wouldn't work with populate.
Essentially, you must decide whether you want to use aggregation or populate to handle the join.
For populate, use the ref as shown in that sample schema.
For aggregate, store an array of ObjectId so you can use lookup to link with the _id field.

How to populate an array of model instances inside a subdocument? MongoDB Mongoose

I have a subdocument that is nested as an array. Inside that subdocument I have references to other models. Using the .Find and .Populate methods I can receive the entire objects for single models referenced in the subdocument (check out Stop below) but not an array of model instances, Facts/Recommendations. For Facts/Recommendations I receive an array of object _ids. I can probably just take those _ids and make another query but this seems messy.
Is there a way to populate the array? Do I need to use Aggregate + $lookup? Is there a more Mongo way to restructure the Schema to simplify this?
Thank you for all and any help!
My subdocument called TourStop:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const TourStopSchema = new Schema({
stop: {
type: Schema.Types.ObjectId,
ref: 'Stop',
required:[true, 'must have a Stop'],
},
mandatory: {
type:Boolean,
default: false
},
check_in_time: {
type: Number,
default: 0
},
order: {
type:Number,
required: [true, 'Must have an order']
},
facts: [{
type: Schema.Types.ObjectId,
ref: 'Fact'
}],
recommendations: [{
type: Schema.Types.ObjectId,
ref: 'Recommendation'
}]
});
module.exports = TourStopSchema;
TourStops lives inside of Tour:
const mongoose = require('mongoose');
const TourStopSchema = require('../subdocs/tour_stop');
const Schema = mongoose.Schema;
const tourSchema = new Schema({
name: {
type:String,
required:[true,'Name is required!'],
unique: true
},
city: {
type: String,
required: [true, 'City is required!']
},
tourStops:[TourStopSchema]
});
const Tour = mongoose.model('Tour', tourSchema);
module.exports = Tour;
Stop Schema which is populated just fine.
const mongoose = require('mongoose');
const LocationSchema = require('../subdocs/location');
const ImageSchema = require('../subdocs/image');
const Schema = mongoose.Schema;
const stopSchema = new Schema({
name:{
type: String,
required:[true,'Must have a name!']
},
location:LocationSchema,
categories: {
type: [String],
default:[]
},
images:{
type:[ImageSchema],
default:[]
}
});
const Stop = mongoose.model('Stop', stopSchema);
module.exports = Stop;
And Fact Schema which is not populated correctly, instead returns an array of strings with the _ids
Fact:
const mongoose = require('mongoose');
const ImageSchema = require('../subdocs/image');
const Schema = mongoose.Schema;
const factSchema = new Schema({
stop: {
type: Schema.Types.ObjectId,
ref:'Stop',
required:[true, 'A Fact must have a Stop!'],
},
description: {
type: String,
required: [true,'A Fact must have a description!']
},
images: {
type:[ImageSchema],
default:[]
}
});
const Fact = mongoose.model('Fact', factSchema);
module.exports = Fact;
And I'm running a test to check that the schema is properly hooked up to retrieve all the attributes of a TourStop:
it('saves a full relation graph', (done) => {
User.findOne({ first_name: 'Dean' })
.populate({
// in that user find the tours property and load up all tours
path: 'tours',
// inside of all those tours, find the tourstops property and load all associated stops
populate: {
path: 'tour_stops.facts',
model: 'Fact'
},
populate: {
path: 'tour_stops.stop',
model: 'Stop'
}
})
// .populate('tours.tour_stops[0].facts')
.then((user) => {
// console.log(user.tours[0].tour_stops[0].stop);
console.log(user.tours[0].tour_stops[0]);
// console.log(Array.isArray(user.tours[0].tour_stops[0].facts))
assert(user.first_name === 'Dean' );
assert(user.tours.length === 1);
assert(user.tours[0].name === "Awesome New Tour");
assert(user.tours[0].tour_stops[0].stop.name === 'Jaffa Clock Tower');
// assert(user.tours[0])
// assert(user.blogPosts[0].title === 'JS is Great');
// assert(user.blogPosts[0].comments[0].content === 'Congrats on great post!' );
// assert(user.blogPosts[0].comments[0].user.name === 'Joe' )
done();
})
})
You can use the following code to populate tours, stop, facts and recommendations.
Note in model property, we should not give string value, but the model itself. So you need to import them to your code.
User.findOne({ first_name: "Dean" })
.populate({
path: "tours",
populate: {
path: "tourStops.stop",
model: Stop
}
})
.populate({
path: "tours",
populate: {
path: "tourStops.facts",
model: Fact
}
})
.populate({
path: "tours",
populate: {
path: "tourStops.recommendations",
model: Recommendation
}
})
.then(user => {
console.log(user);
});

How do I find if an Id is present in the array of team members (which stores user ids)?

I have this model of workspace schema in my node js project(model is displayed below)
After the user logs into my application I want to display the information of a workspace only if it is created by him or he is a team member of that workspace
I am able to find the workspaces created by the user by the following query
Workspace.find({creator:req.user._id},function(err,workspaces){
res.render('home',{
wokspacses:workspaces
});
});
similarly, I also want the workspaces in which the user is the team member
Workspace.find({creator:req.user._id},function(err,workspaces){
Workspace.find({team_member:"WHAT SHOULD I WRITE HERE"},function(err,workspaces2){
res.render('home',{
wokspacses:workspaces
wokspacses2:workspaces2
});
});
Since team_members is an array simply passing the user id is not yielding the result and workspaces2 remains empty
Thank you for your time !!
const mongoose = require('mongoose');
const workspaceSchema = mongoose.Schema({
name:{
type:String,
required:true
},
date: {
type: Date,
required: true
},
creator:{
type: Object,
ref: 'User',
required: true
},
team_member: [{ type: Object, ref: 'User' }]
});
module.exports = mongoose.model('Workspace',workspaceSchema);
Use the $in Operator.
const mongoose = require("mongoose")
const Schema = mongoose.Schema
mongoose.connect('mongodb://localhost/stackoverflow', {useNewUrlParser: true});
const workspaceSchema = new Schema({
name:{
type:String,
required:true
},
date: {
type: Date,
required: true
},
creator:{
type: Object,
ref: 'User',
required: true
},
team_member: [{ type: Object, ref: 'User' }]
});
const WorkspaceModel = mongoose.model('Workspace',workspaceSchema);
const sessionUserId = "5d330f3de87ec83f95504c44" //i.e. req.user._id;
WorkspaceModel.find({
$or:[
{ creator: sessionUserId },
{
team_member: {
$in: [sessionUserId]
}
}
]
}).exec((err, result) => {
console.log("result", result)
})

Categories

Resources