GraphQL Mutation Updating Users Followers with Mongoose/MongodDB - $set is empty error - mongodb

I have this mutation set up:
followUser: {
type: UserType,
args: {
_id: { type: GraphQLString },
firebaseUid: { type: GraphQLString },
following: { type: new GraphQLList(GraphQLString)},
},
resolve(parentValue, { firebaseUid, _id, following}) {
const update = {
$set: { "following": [firebaseUid] },
$push: { "following": { firebaseUid } }
}
return UserSchema.findOneAndUpdate(
{ _id },
update,
{new: true, upsert: true}
)
}
},
I'm trying to add new followers into my graphql user's collection. My user model:
const UserSchema = new Schema(
{
firebaseUid: String,
following: [{ type: Schema.Types.ObjectId, ref: 'User' }],
followers: [{ type: Schema.Types.ObjectId, ref: 'User' }],
},
{ timestamps: true }
);
module.exports = mongoose.model("User", UserSchema);
So at first, the user doesn't have any followers, so it won't have that field yet. When user adds someone to their friends list, thats when the field will appear in mongodb. Right now I'm getting this error:
"message": "'$set' is empty. You must specify a field like so: {$set: {<field>: ...}}",
I'm not sure if I'm doing the $set correctly.
The UserType
const UserType = new GraphQLObjectType({
name: "User",
fields: () => ({
_id: { type: GraphQLString },
firebaseUid: { type: GraphQLString },
following: { type: new GraphQLList(GraphQLString) },
followers: { type: new GraphQLList(GraphQLString) },
...
})
});
edit:
current mongodb data collection:
_id: ObjectId("5e5c24111c9d4400006d0001")
name: "Mr. Smith"
username: "mrsmith"
after running the update
_id: ObjectId("5e5c24111c9d4400006d0001")
name: "Mr. Smith"
username: "mrsmith"
following: ["fdsaduybfeaf323dfa"] // <-- this gets added

Currently mongooses validator is rejecting the update. To fix this you need the following:
You only need to $push since it will automatically create an array if the property does not exist
You should remove the extra { } around the firebaseUid in the $push because otherwise the following array will contain objects with a firebaseUid property instead of directly containing the Uid (or would if the schema validator allowed it)
Mongo ObjectIds can only be converted from strings when they are 12-byte hexadecimal, and firebaseUid is not, so the schema should be typed to String instead of ObjectId as the validator will reject the field for update otherwise.

Related

MongoDB - remove many from arrays of all existing elements

I have some simple user data. Here is example for one user:
const userSchema = new Schema({
userName: {
type: String,
},
projectsInput: [
{
type: Schema.Types.ObjectId,
ref: "Project",
},
],
projectsHold: [
{
type: Schema.Types.ObjectId,
ref: "Project",
},
],
});
I want by having ProjectId to be able to remove all records from all users that contains it.
if I get the first one
60f02d21159c4b4110f21a32
how I can perform updateMany function for my UserModel?
return UserModel.updateMany(
{
projectsInput: {
$elemMatch: args.projectId,
},
},
{
projectsInput: {
$slice: [projectsInput.$, 1],
},
}
);
})
Here is my code that is not working.
args.projectId = 60f02d21159c4b4110f21a32 (my id for the project I want to delete)
and UserModel is my mongodb Schema for user.
you can use $pull
{
$pull: {
projectsInputs: "123"
}
}

MongoDB Query from multiple models / schemas and return in one field

I am using Nodejs and MongoDB, mongoose and expressjs, creating a Blog API having users, articles, likes & comments schema. Below are schemas that I use.
const UsersSchema = new mongoose.Schema({
username: { type: String },
email: { type: String },
date_created: { type: Date },
last_modified: { type: Date }
});
const ArticleSchema = new mongoose.Schema({
id: { type: String, required: true },
text: { type: String, required: true },
posted_by: { type: Schema.Types.ObjectId, ref: 'User', required: true },
images: [{ type: String }],
date_created: { type: Date },
last_modified: { type: Date }
});
const CommentSchema = new mongoose.Schema({
id: { type: String, required: true },
commented_by: { type: Schema.Types.ObjectId, ref: 'User', required: true },
article: { type: Schema.Types.ObjectId, ref: 'Article' },
text: { type: String, required: true },
date_created: { type: Date },
last_modified: { type: Date }
});
What I actually need is when I * get collection of articles * I also want to get the number of comments together for each articles. How do I query mongo?
Since you need to query more than one collection, you can use MongoDB's aggregation.
Here: https://docs.mongodb.com/manual/aggregation/
Example:
Article
.aggregate(
{
$lookup: {
from: '<your comments collection name',
localField: '_id',
foreignField: 'article',
as: 'comments'
}
},
{
$project: {
comments: '$comments.commented_by',
text: 1,
posted_by: 1,
images: 1,
date_created: 1,
last_modified: 1
}
},
{
$project: {
hasCommented: {
$cond: {
if: { $in: [ '$comments', '<user object id>' ] },
then: true,
else: false
}
},
commentsCount: { $size: '$comments' },
text: 1,
posted_by: 1,
images: 1,
date_created: 1,
last_modified: 1
}
}
)
The aggregation got a little big but let me try to explain:
First we need to filter the comments after the $lookup. So we $unwind them, making each article contain just one comment object, so we can filter using $match(that's the filter stage, it works just as the <Model>.find(). After filtering the desired's user comments, we $group everything again, $sum: 1 for each comment, using as the grouper _id, the article's _id. And we get the $first result for $text, $images and etc. Later, we $project everything, but now we add hasCommented with a $cond, simply doing: if the $comments is greater than 0(the user has commented, so this will be true, else, false.
MongoDB's Aggregation framework it's awesome and you can do almost whatever you want with your data using it. But be aware that somethings may cost more than others, always read the reference.

Mongoose pull ObjectId from array

i'm trying to do a pretty simple operation, pull an item from an array with Mongoose on a Mongo database like so:
User.update({ _id: fromUserId }, { $pull: { linkedUsers: [idToDelete] } });
fromUserId & idToDelete are both Objects Ids.
The schema for Users goes like this:
var UserSchema = new Schema({
groups: [],
linkedUsers: [],
name: { type: String, required: true, index: { unique: true } }
});
linkedUsers is an array that only receives Ids of other users.
I've tried this as well:
User.findOne({ _id: fromUserId }, function(err, user) {
user.linkedUsers.pull(idToDelete);
user.save();
});
But with no luck.
The second option seem to almost work when i console the lenghts of the array at different positions but after calling save and checking, the length is still at 36:
User.findOne({ _id: fromUserId }, function(err, user) {
console.log(user.linkedUsers.length); // returns 36
user.linkedUsers.pull(idToDelete);
console.log(user.linkedUsers.length); // returns 35
user.save();
});
So it looks like i'm close but still, no luck. Both Ids are sent via the frontend side of the app.
I'm running those versions:
"mongodb": "^2.2.29",
"mongoose": "^5.0.7",
Thanks in advance.
You need to explicitly define the types in your schema definition i.e.
groups: [{ type: Schema.Types.ObjectId, ref: 'Group' }],
linkedUsers: [{ type: Schema.Types.ObjectId, ref: 'User' }]
and then use either
User.findOneAndUpdate(
{ _id: fromUserId },
{ $pullAll: { linkedUsers: [idToDelete] } },
{ new: true },
function(err, data) {}
);
or
User.findByIdAndUpdate(fromUserId,
{ $pullAll: { linkedUsers: [idToDelete] } },
{ new: true },
function(err, data) {}
);
I had a similar issue. I wanted to delete an object from an array, using the default _id from mongo, but my query was wrong:
const update = { $pull: { cities: cityId }};
It should be:
const update = { $pull: { cities: {_id: cityId} }};

Mongo find users that are not blocked

I have a db in Mongo with users. I want to retrieve all user that are not in my blocked Array. tried to filter through the results by retrieving All documents but I wonder if that's the most efficient way. Is there a way in Mongo to find all users that are not part of an array?
Here is my User model:
var UserSchema = mongoose.Schema({
username: {
type: String,
index:true,
unique: true
},
password: {
type: String
},
email: {
type: String,
unique: true
},
name: {
type: String
},
latitude:{
type: String
},
longitude:{
type: String
},
blocked:{
type: Array
},
friends:{
type: Array
},
photo:{
type: String
},
identity:{
type: String
},
age:{
type: String
},
herefor:{
type: String
},
conns:{
type: Number
},
status:{
type: String
}
}, { collection: 'users' });
You are probably looking for something like the following, assuming your blocked users array contains usernames and your mongoose model is called UserModel:
var blockedUsernamesArray = [];
UserModel.find({ username: { $nin: blockedUsernamesArray } }, function(err, docs) {
// Handle result
})

How to save populated Document?

Is it possible to save a populated document?
I am trying to do:
var Group = new Db['Group']();
for (var i=0; i<50; i++)
Db.Members.push({ User: { _id: "521014731e27b1b008000002"}, pseudo: 'John' });
Group.save();
Schemas
var GroupSchemaModel = {
Members: [{
User: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
updated_at: { type: Date, required: true, default: Date.now }
}]
};
I get the error
{ message: 'Cast to ObjectId failed for value "[object Object]" at path "User"',
name: 'CastError',
type: 'ObjectId',
value: { _id: '521014731e27b1b008000002' },
path: 'User' }
This:
User: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
Tells mongoose that User field will be a collection of references of type ObjectId pointing to another collection.
You, on the other hand, are trying to insert an object there:
Db.Members.push({ User: { _id: "521014731e27b1b008000002"}, pseudo: 'John' });
Mongoose tries to cast it to ObjectId and fails. That's apart from the fact that pseudo field isn't in the group schema.
Try this instead:
Db.Members.push({User: mongoose.Types.ObjectId("521014731e27b1b008000002"), updated_at: whatever});