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);
});
Related
I see that sequelize has DataTypes.TSVECTOR for postgres dialect.
I have a column whose definition in raw SQL is as follows
tsvector GENERATED ALWAYS AS (((
setweight(to_tsvector('english'::regconfig, (COALESCE(title, ''::character varying))::text), 'A'::"char") ||
setweight(to_tsvector('english'::regconfig, COALESCE(summary, ''::text)), 'B'::"char")) ||
setweight(to_tsvector('english'::regconfig, (COALESCE(content, ''::character varying))::text), 'C'::"char")))
STORED
How can I define this in my sequelize model
const FeedItem = sequelize.define(
'FeedItem', {
feedItemId: {
type: DataTypes.UUID,
primaryKey: true,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
},
pubdate: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: sequelize.literal('CURRENT_TIMESTAMP'),
validate: {
isDate: true,
},
},
link: {
type: DataTypes.STRING,
allowNull: false,
validate: {
len: [0, 2047],
},
},
guid: {
type: DataTypes.STRING,
validate: {
len: [0, 2047],
},
},
title: {
type: DataTypes.TEXT,
allowNull: false,
validate: {
len: [0, 65535],
},
},
summary: {
type: DataTypes.TEXT,
validate: {
len: [0, 65535],
},
},
content: {
type: DataTypes.TEXT,
validate: {
len: [0, 1048575],
},
},
author: {
type: DataTypes.STRING,
validate: {
len: [0, 63],
},
},
tags: {
type: DataTypes.ARRAY(DataTypes.STRING),
defaultValue: [],
},
// How to do that generated always part here???
searchable: {
type: DataTypes.TSVECTOR
},
}, {
timestamps: false,
underscored: true,
indexes: [
{
name: 'idx_feed_items_searchable',
fields: ['searchable'],
using: 'gin',
},
],
}
);
The model needs to be modified as follows to get this working
const FeedItem = sequelize.define(
'FeedItem',
{
feedItemId: {
type: DataTypes.UUID,
primaryKey: true,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
},
pubdate: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: sequelize.literal('CURRENT_TIMESTAMP'),
validate: {
isDate: true,
},
},
link: {
type: DataTypes.STRING,
allowNull: false,
validate: {
len: [0, 2047],
},
},
guid: {
type: DataTypes.STRING,
validate: {
len: [0, 2047],
},
},
title: {
type: DataTypes.TEXT,
allowNull: false,
validate: {
len: [0, 65535],
},
},
summary: {
type: DataTypes.TEXT,
validate: {
len: [0, 65535],
},
},
content: {
type: DataTypes.TEXT,
validate: {
len: [0, 1048575],
},
},
author: {
type: DataTypes.STRING,
validate: {
len: [0, 63],
},
},
tags: {
type: DataTypes.ARRAY(DataTypes.STRING),
defaultValue: [],
},
// https://stackoverflow.com/questions/67051281/use-postgres-generated-columns-in-sequelize-model
searchable: {
type: `tsvector GENERATED ALWAYS AS (((setweight(to_tsvector('english'::regconfig, (COALESCE(title, ''::character varying))::text), 'A'::"char") || setweight(to_tsvector('english'::regconfig, COALESCE(summary, ''::text)), 'B'::"char")) || setweight(to_tsvector('english'::regconfig, (COALESCE(content, ''::character varying))::text), 'C'::"char"))) STORED`,
set() {
throw new Error('generatedValue is read-only');
},
},
},
{
timestamps: false,
underscored: true,
indexes: [
{
name: 'idx_feed_items_pubdate_feed_item_id_desc',
fields: [
{ attribute: 'pubdate', order: 'DESC' },
{ attribute: 'feed_item_id', order: 'DESC' },
],
},
{
name: 'idx_feed_items_tags',
fields: ['tags'],
using: 'gin',
},
{
name: 'idx_feed_items_searchable',
fields: ['searchable'],
using: 'gin',
},
],
}
);
Does not work with sequelize.sync({alter: true}) you have to force:true or sequelize migrations
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"
}
}
]);
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?
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"
}
...
]
I have a mongoose model:
var schema = new Schema({
loginName: {
type: String,
unique: true,
required: true
},
hashedPassword: {
type: String,
required: true
},
salt: {
type: String,
required: true
},
created: {
type: Date,
default: Date.now
},
rooms: [{ _id: Schema.Types.ObjectId, loginName: [{ type: String }] }]
});
Example result:
{
_id: "56c0a986eeb118741109a45f",
loginName: "MegaDaddgy",
hashedPassword: "*****",
salt: "******",
__v: 10,
rooms: [
{
_id: "56c0a986eeb118741109a461",
loginName: [
"MegaDaddgy"
]
},
{
_id: "56c0d9e332f6ddc80ec7271c",
loginName: [
"MegaDaddgy"
]
}
],
created: "2016-02-14T16:21:26.272Z"
}
What I need:
search on field : rooms._id in every user document,
push new loginName in array loginName in every found user document
delete selected loginName in array
Example params:
rooms._id : 56c0a986eeb118741109a461
loginName: "John"
Result:
{
_id: "56c0a986eeb118741109a45f",
loginName: "MegaDaddgy",
hashedPassword: "*****",
salt: "******",
__v: 10,
rooms: [
{
_id: "56c0a986eeb118741109a461",
loginName: [
"MegaDaddgy", "John"
]
},
{
_id: "56c0d9e332f6ddc80ec7271c",
loginName: [
"MegaDaddgy"
]
}
],
created: "2016-02-14T16:21:26.272Z"
}
Example params:
rooms._id : 56c0a986eeb118741109a461
loginName: "John"
Result:
{
_id: "56c0a986eeb118741109a45f",
loginName: "MegaDaddgy",
hashedPassword: "*****",
salt: "******",
__v: 10,
rooms: [
{
_id: "56c0a986eeb118741109a461",
loginName: [
"MegaDaddgy"
]
},
{
_id: "56c0d9e332f6ddc80ec7271c",
loginName: [
"MegaDaddgy"
]
}
],
created: "2016-02-14T16:21:26.272Z"
}
How can I do this?
You could push John into loginName array through $push
Model.update({'rooms._id': ObjectId('56c0a986eeb118741109a461')},
{$push: {'rooms.$.loginName': 'John'}}, function(...));
delete John from loginName array through $pull
Model.update({'rooms._id': ObjectId('56c0a986eeb118741109a461')},
{$pull: {'rooms.$.loginName': 'John'}}, function(...));