referencing documents only by the mongo objectID? - mongodb

is it possible to reference another value other than the mongo generated _id?
User Model
uid: {type: String, required: true},
channel_pub: {type: String},
channel_groups: [{type: String}],
auth_key: {type: String},
channels: [{
name: {
type: String,
ref: 'channel'
}
}]
Channel Model
name: {type: String, required: true},
uid: [{
type: String,
ref: 'user',
required: true
}]
I am trying to reference the actual channel name in the user document.

You can do this with Populate Virtuals since mongoose 4.5.0 :
var UserSchema = new mongoose.Schema({
uid: { type: String, required: true }
}, {
toJSON: {
virtuals: true
}
});
var ChannelSchema = new mongoose.Schema({
name: { type: String, required: true },
uid: [{
type: String,
ref: 'User',
required: true
}]
});
UserSchema.virtual('channels.data', {
ref: 'Channel',
localField: 'channels.name',
foreignField: 'name'
});
Here the local field is channels.name, the Channel object will be populated in channels.data.
For instance a find with channels.data populated :
User.find({}).populate('channels.data').exec(function(error, res) {
console.log(JSON.stringify(res, null, 4));
});
will give :
[{
"_id": "588a82ff7fe89686fd2210b0",
"uid": "user1",
"channels": [{
"data": {
"_id": "588a80fd7fe89686fd2210a8",
"name": "channel1",
"uid": []
},
"name": "channel1"
}, {
"data": {
"_id": "588a80fd7fe89686fd2210a9",
"name": "channel2",
"uid": []
},
"name": "channel2"
}],
"id": "588a82ff7fe89686fd2210b0"
}
...
]

Related

How do I populate a nested field with array of ID to another collection using Mongoose?

I have two collections. User and Event. In my Event model, I have an object "attendees" with ("hosting", "pending", "accepted", "declined") that have an array of ObjectId's referencing the User. I am trying to grab a single event and have those populated with User information.
My Event Schema
const EventSchema = new mongoose.Schema(
{
title: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
eventDate: {
type: Date,
required: true,
},
dateCreated: {
type: Date,
default: Date.now,
},
dateUpdated: {
type: Date,
default: Date.now,
},
attendees: {
hosting: [
{
type: Schema.ObjectId,
ref: "User",
},
],
pending: [
{
type: Schema.ObjectId,
ref: "User",
},
],
accepted: [
{
type: Schema.ObjectId,
ref: "User",
},
],
declined: [
{
type: Schema.ObjectId,
ref: "User",
},
],
},
My User Schema
const UserSchema = new mongoose.Schema(
{
name: {
first: {
type: String,
required: true,
},
last: {
type: String,
required: true,
},
},
phone: {
type: String,
required: true,
},
email: {
type: String,
match: [/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/, "Please add a valid email"],
unique: true,
required: true,
},
password: {
type: String,
required: true,
},
pictureUrl: String,
allergies: [String],
preferences: [String],
favoriteRecipes: [String],
events: {
hosting: [
{
type: Schema.ObjectId,
ref: "Event",
},
],
pending: [
{
type: Schema.ObjectId,
ref: "Event",
},
],
accepted: [
{
type: Schema.ObjectId,
ref: "Event",
},
],
declined: [
{
type: Schema.ObjectId,
ref: "Event",
},
],
},
I've looked through a ton of stack overflow and can't find something to help. Maybe I need to change my models? Here is my query:
db.Event.find({})
.populate({
path: "attendees.pending",
model: "User",
})
.exec((err, docs) => {
console.log(docs);
process.exit(0);
});

How to query to get all documents referenced to another collection based on one field as username

In my Node API and MongoDB, I'm trying to make an endpoint to get all the posts associated with one username of a profile. In my Profile schema, I have a reference to the Post Schema and in my Post schema, I have a reference to the Username from Profile schema.
My issue I don't know how to get all the posts for that username. I did similarly but with embedded for the Experience schema but I'm not sure how to do that for the referenced collections.
Post model:
const { Connect } = require("../db");
const reactionSchema = {
likedBy: {
type: String,
unique: true,
sparse: true
}
};
const postSchema = {
text: {
type: String,
required: true,
unique: true,
sparse: false
},
username: {
type: Connect.Schema.Types.String,
ref: "Profile"
},
image: {
type: String,
default: "https://via.placeholder.com/150",
required: false
},
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
},
reactions: [reactionSchema],
comments: {
type: Connect.Schema.Types.ObjectId,
ref: "Comment",
required: false
}
};
const collectionName = "post";
const postSchemaModel = Connect.Schema(postSchema);
const Post = Connect.model(collectionName, postSchemaModel);
module.exports = Post;
Profile model:
// Here defining profile model
// Embedded we have the Experience as []
const { Connect } = require("../db");
const { isEmail } = require("validator");
const postSchema = {
type: Connect.Schema.Types.ObjectId,
ref: "Post"
};
const experienceSchema = {
role: {
type: String,
required: true
},
company: {
type: String,
required: true
},
startDate: {
type: Date,
required: true
},
endDate: {
type: Date,
required: false
},
description: {
type: String,
required: false
},
area: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
},
username: {
type: String,
required: false
},
image: {
type: String,
required: false,
default: "https://via.placeholder.com/150"
}
};
const profileSchema = {
firstname: {
type: String,
required: true
},
surname: {
type: String,
required: true
},
email: {
type: String,
trim: true,
lowercase: true,
unique: true,
required: [true, "Email is required"],
validate: {
validator: string => isEmail(string),
message: "Provided email is invalid"
}
},
bio: {
type: String,
required: true
},
title: {
type: String,
required: true
},
area: {
type: String,
required: true
},
imageUrl: {
type: String,
required: false,
default: "https://via.placeholder.com/150"
},
username: {
type: String,
required: true,
unique: true
},
experience: [experienceSchema],
posts: [postSchema],
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
}
};
const collectionName = "profile";
const profileSchemaModel = Connect.Schema(profileSchema);
const Profile = Connect.model(collectionName, profileSchemaModel);
module.exports = Profile;
I was able to make this for the embedded experience but not sure that correct for referenced collection:
const profileWithExperiences = await Student.aggregate([
{ $match: { username: res.username.username } },
{
$unwind: "$experience"
},
{
$match: {
"experience._id": new ObjectID(req.params.experienceId)
}
},
{
$project: {
username: 1,
experience: 1,
_id: 0
}
}
]);
I would like to see an example for referenced collections as it is confusing me how should I do that
[EDIT]
JSON for Profiles and Posts
{
"_id": ObjectId("5e2c98fc3d785252ce5b5693"),
"imageUrl": "https://i.pravatar.cc/300",
"firstname": "Jakos",
"surname": "Lemi",
"email": "lemi#email.com",
"bio": "My bio bio",
"title": "Senior IT developer",
"area": "Copenhagen",
"username": "Jakos",
"experience": [
{
"image": "https://via.placeholder.com/150",
"_id": ObjectId("5e3975f95fbeec9095ff3d2f"),
"role": "Developer",
"company": "Google",
"startDate": ISODate("2018-11-09T23:00:00.000Z"),
"endDate": ISODate("2019-01-05T23:00:00.000Z"),
"area": "Copenhagen",
"createdAt": ISODate("2020-02-04T13:47:37.167Z"),
"updatedAt": ISODate("2020-02-04T13:47:37.167Z")
},
{
"image": "https://via.placeholder.com/150",
"_id": ObjectId("5e3978bf5e399698e20c56d4"),
"role": "Developer",
"company": "IBM",
"startDate": ISODate("2018-11-09T23:00:00.000Z"),
"endDate": ISODate("2019-01-05T23:00:00.000Z"),
"area": "Copenhagen",
"createdAt": ISODate("2020-02-04T13:59:27.412Z"),
"updatedAt": ISODate("2020-02-04T13:59:27.412Z")
}
],
"createdAt": ISODate("2020-01-25T19:37:32.727Z"),
"updatedAt": ISODate("2020-02-04T23:14:37.122Z"),
"__v": NumberInt("0")
}
Post
{
"_id": ObjectId("5e3beb639e072afedd19dcef"),
"username": ObjectId("5e2c98fc3d785252ce5b5693"),
"image": "https://via.placeholder.com/150",
"text": "My awesome post",
"createdAt": ISODate("2020-02-06T10:33:07.22Z"),
"updatedAt": ISODate("2020-02-06T10:33:07.22Z"),
"reactions": [ ],
"__v": NumberInt("0")
}
Output expected:
{
"username": "Jakos",
"postsCount": [1],
"posts": [
{
"_id": ObjectId("5e3beb639e072afedd19dcef"),
"image": "https://via.placeholder.com/150",
"text": "My awesome post",
"createdAt": ISODate("2020-02-06T10:33:07.22Z"),
"updatedAt": ISODate("2020-02-06T10:33:07.22Z"),
"reactions": [ ],
"__v": NumberInt("0")
}
]
}
I want to see all the posts related to that username
You can use the $lookup aggregation to join collections.
db.profiles.aggregate([
{
$match: {
username: "Jakos"
}
},
{
$lookup: {
from: "posts", //the name of the posts collection, change this if it is different
localField: "_id",
foreignField: "username",
as: "posts"
}
},
{
$project: {
username: 1,
posts: 1,
postsCount: {
$size: "$posts"
},
_id: 0
}
}
])
Playground
For mongoose it should be like this in your app:
const profileWithExperiences = await Student.aggregate([
{ $match: { username: res.username.username } },
{
$unwind: "$experience"
},
{
$lookup: {
from: "posts", //the name of the posts collection, change this if it is different
localField: "_id",
foreignField: "username",
as: "posts"
}
},
{
$project: {
username: 1,
posts: 1,
postsCount: {
$size: "$posts"
},
_id: 0
}
}
]);
Try to get like this i am getting data from two table with the use of node and mongodb.
var param = {};
param['user_id']=id;
var search={ user_id:param.user_id,status: [ 'Pending', 'Accepted' ] };
var output={};
output = await JobRequest.find(search);
if(output !=0)
{
var JSon=await JobRequest.aggregate([
{$match: { user_id: {$gte:id}} },
{
"$project": {
"employee_id": {
"$toObjectId": "$employee_id"
},
"service_id": {
"$toObjectId": "$service_id"
},
"createdDate": {
"$toString": "$createdDate"
},
"chat_status": {
"$toString": "$chat_status"
},
"status": {
"$toString": "$status"
},
"delivery_address": {
"$toString": "$delivery_address"
},
"delivery_lat": {
"$toString": "$delivery_lat"
},
"delivery_lang": {
"$toString": "$delivery_lang"
},
}
},
{
$lookup:
{
from: "employees",
localField: "employee_id",
foreignField: "_id",
as: "employee_details"
}
},
{
$lookup:
{
from: "services",
localField: "service_id",
foreignField: "_id",
as: "service_details"
}
}
]);

Query embedded document in mongoDB

I'm trying to query a Group document that has Users sub documents.
But I only want the subdocuments that belong to the user,
but I'm also getting data of other users.
Look at the query result, UserStatus field, only one of them belongs to the user.
The query result -
[
{
"_id": "5b1bcc12d5cdbf2cf78f2133",
"name": "First group ever",
"users": [
{
userId: 1,
"userStatus": "I"
},
{
userId: 2,
"userStatus": "I"
}
]
}
]
Group.js -
const GroupSchema = mongoose.Schema({
name: {
type: String,
required: true
},
users: [{
userId: {
type: mongoose.SchemaTypes.ObjectId,
ref: 'users',
required: true
},
userStatus: {
type: String,
required: true
}
}]
})
Query -
Group.find({
"users": {
$elemMatch: {
userId: req.params.id,
$or: [{
userStatus: "I"
}, {
userStatus: "A"
}]
}
}
}, "_id name users.userStatus",
function (err, groups) {
if (err)
res.send(err)
res.json(groups);
});

Mongo aggregation lookup

I am trying to attempt the following
From a log, find all prizes the user entered
return the order numbers and the title
I am using the mongo aggregation framework.
I match by userId
I group the prizes by the same product and then push order numbers
I want to do a look up to the prizes collection to find the title of the prizes.
The look up is returning no elements
Here is the aggregation code
db.pointslogs.aggregate(
// Pipeline
[
// Stage 1
{
$match: {
"user": ObjectId("5aacff47c67f99103bcbf693")
}
},
// Stage 2
{
$group: {
_id: "$productPurchased",
orderNumber: { $push: "$orderNumber" }
}
},
// Stage 3
{
$unwind: {
path : "$orderNumber",
}
},
// Stage 4
{
$lookup: {
"from" : "prizes",
"localField" : "_id",
"foreignField" : "_id",
"as" : "title"
}
},
]
// Created with Studio 3T, the IDE for MongoDB - https://studio3t.com/
);
Here are the models (points log below)
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var schema = new Schema({
orderNumber: {type: String, required: true},
productPurchased: {type: String, require: true},
answer: {type: String},
user: [{type: Schema.Types.ObjectId, ref: 'User'}]
});
module.exports = mongoose.model('PointsLog', schema);
Prizes model below
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var schema = new Schema({
title: {type: String, required: true},
content: {type: String, required: true},
orderNumber: {type: String, required: true},
imageUrl: {type: String, required: true},
stockQty: {type: Number, required: true},
question: {type: String, required: true},
answers: [{type: String, required: true}],
image: {type: String, required: true},
cost: {type: Number, required: true },
//entries: {type: Int},
//user: {type: Schema.Types.ObjectId, ref: 'User'}
user: [{type: Schema.Types.ObjectId, ref: 'User'}]
});
module.exports = mongoose.model('Prize', schema);
Not sure why lookup is returning nothing
1)You can use the upgraded lookup
Below is an Example->
db.pointslogs.aggregate([
// Stage 1
{
$match: {
"user": ObjectId("5aacff47c67f99103bcbf693")
}
},
// Stage 2
{
$group: {
_id: "$productPurchased",
orderNumber: { $push: "$orderNumber" }
}
},
// Stage 3
{
$unwind: {
path : "$orderNumber",
}
},
// Stage 4
{
$lookup:
{
from: "prizes",
let: { oNumber: "$orderNumber" },
pipeline: [
{ $match:{ $expr: { $eq: [ "$orderNumber", "$$oNumber" ] } }
}
},
{ $project: {
title: 1,
content: 1,
orderNumber: 1,
imageUrl: 1,
stockQty: 1,
question: 1,
answers: 1,
image: 1,
cost: 1,
user: 1
} }
],
as: "orderNumber"
}
}
])

mongoose populate query doesn't show the desired results

I'm new to MongoDB and Mongoose and I'm having some problems with it, to be more specific I've problems with populate, problem displayed below.
My Schema:
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
/* ------------------------------------- Field config ---------------------------------------- */
const UsersSchema = new Schema({
name: {
type: String,
required: true,
min: 3,
set: str => str.replace(/\b\w/g, l => l.toUpperCase())
},
lastname: {
type: String,
required: true,
min: 3,
set: str => str.replace(/\b\w/g, l => l.toUpperCase())
},
email: {
type: String,
required: true,
unique: true,
validade: {
validator: str => regExp.test(str)
}
},
password: {
type: String,
required: true,
min: 8
},
fcm:{
type: String,
required: true
},
birthDate: {
type: Date,
required: true,
get: convertToBR
},
gender: {
type: String,
enum: ['M', 'F'],
required: true
},
profileImage: {
type: String,
required: false,
get: alterUri
},
aboutMe: {
type: String,
max: 500
},
enabled: {
type: Boolean,
required: true,
default: true
},
createdAt: {
type: Date,
required: true,
default: new Date()
},
token: {
type: String,
required: true,
index: true
},
locations: {
type: [mongoose.Schema.Types.ObjectId],
require: false,
ref: 'Location'
},
lastVisitants: {
type: [{
user: mongoose.Schema.Types.ObjectId,
createAt: {
type: Date,
required: true,
default: new Date()
}
}],
required: false,
ref: 'Users'
}
}, {
toObject: {
virtuals: true,
getters: true,
setters: true
},
toJSON: {
virtuals: true,
getters: true,
setters: true
}
});
//Code bellow removed for make to easy know
Generated doc inside MongoDB:
{
"_id": "589288de533c9555163cf263",
"email": "luiz#sene.com",
"password": "CQziXoB6XrBFBTWe5s/kFsIGYqbtDPBBKQgADUZF9co=",
"name": "luiz Fernando",
"lastname": "Sene",
"birthDate": "1988-08-11T00:00:00.000Z",
"gender": "M",
"fcm": "34567890987654345678",
"token": "58979c08b048917ed8b434ac",
"createdAt": "2017-02-02T01:17:38.168Z",
"enabled": true,
"__v": 0,
"aboutMe": "algo sobre mim aqui mudar",
"locations": [
"5893e2e0c8a01b4ed21c8c39",
"5893e305c8a01b4ed21c8c3a",
"5893e32ac8a01b4ed21c8c3b",
"5893e34dc8a01b4ed21c8c3c",
"5893e92838bd205ba42c2c8a",
"5893ea888628c45d2bc6683a"
],
"profileImage": "images/589288de533c9555163cf263/1486330852976.png",
"lastVisitants": [
{
"createAt": "2017-02-05T19:23:17.697Z",
"_id": "58977ba99fc2b7485dbdbf26",
"user": "589778e22a9dd5449e4f92df"
}
]
}
My problem occurs when I try populate locations and lastVisitants.
My consult is below:
UsersSchema.findOne({_id: data.data.id}, {lastVisitants: true, locations: true})
.populate({
path: 'locations',
model: 'Location',
select: 'description',
options: {
limit: 1,
sort: {
createdAt: -1
}
}
})
.populate({
path: 'lastVisitants.user',
model: 'Users',
select: '_id lastVisitants',
options: {
limit: 5
//TODO: Add sort by createdAt
}
})
.exec((err, result) => {
if (err) {
Utils.callback(err, null, res);
return;
}
Utils.callback(null, result, res);
});
Result of my consult:
{
"_id": "589288de533c9555163cf263",
"lastVisitants": [
{
"_id": "58977c1c9fc2b7485dbdbf27",
"user": {
"_id": "589778e22a9dd5449e4f92df",
"lastVisitants": [
{
"_id": "58977c1c9fc2b7485dbdbf27",
"user": "589288de533c9555163cf263",
"createAt": "2017-02-05T19:23:17.697Z"
}
],
"profileImage": "http://192.168.0.19:3000/undefined",
"birthDate": {
"en": "NaN-NaN-NaN",
"br": "NaN/NaN/NaN"
},
"fullname": "undefined undefined",
"age": null,
"id": "589778e22a9dd5449e4f92df"
},
"createAt": "2017-02-05T19:23:17.697Z"
}
],
"profileImage": "http://192.168.0.19:3000/undefined",
"birthDate": {
"en": "NaN-NaN-NaN",
"br": "NaN/NaN/NaN"
},
"fullname": "undefined undefined",
"age": null,
"id": "589288de533c9555163cf263"
}
Result expected:
{
"_id": "589288de533c9555163cf263",
"locations":[
{
"_id": "5893ea888628c45d2bc6683a",
"description": "Cruzeiro - SP, Brasil"
}
]
"lastVisitants": [
{
_id: ""
user: {
"id": "58977ba99fc2b7485dbdbf26",
"fullname": "Fábio Pereira",
"profileImage": "http://192.168.0.19:3000/images/589288de533c9555163cf263/1486319528451.jpeg",
"gender": "M",
// more fields ...
},
createdAt: ""
}
]
}
My question is how can I make this query bring what I really desire?