Update a document collection in MongoDB using Node.js - node.js

I am working a twitter like follow model, for which my User's schema is as follows.
var UserSchema = new Schema({
username: {type: String, match: /^[a-zA-Z0-9_]+$/, unique: true},
email: { type: String, unique: true },
password: String,
followings : [{ type: ObjectId, ref: 'User' }],
followers : [{ type: ObjectId, ref: 'User' }] });
I need to store just the user's ObjectId in the followings and followers field.
I am not sure how to Insert and Update the followings and followers collection.
I tried with "Update" but it overwrites each time.
Then tried push, but doesn't help.
Please help me.
Thanks in advance.

In your case I would use the operator $addToSet which appends a value to the array if it doesn't exist.
Example in mongodb shell
db.userSchema.update({"username" : USERNAME}, { "$addToSet" : { "followers" : ObjectId}})

Related

I don't want to see the same value repeated in mongodb collection [duplicate]

For example if I have this schema
var userSchema = mongoose.Schema({
username: String,
email: String,
password: String,
_todo: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Todo'}]
});
I would like the username to be a unique key that cannot be duplicated by other users. How can I do this?
You can add a constraint with the unique attribute. This will also add a "unique" index for the field to your collection:
var userSchema = mongoose.Schema({
username: { type: String, unique: true },
email: String,
password: String,
_todo: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Todo'}]
});
I came across the same issue. But can't be solved by the accepted answer.
For my example is very simple, just make the name unique.
var FoolSchema = Schema({
name: {
type: String,
unique: true,
index: true,
required: true
}
})
I can save the duplicate name every time.
Then I find this link:
https://mongoosejs.com/docs/faq.html#unique-doesnt-work
The solution is createIndex from mongoDB, not from mongoose.
start mongo shell
mongo, and use yourdbName
run db.foos.getIndexes() (you can't see the "unique" : true, )
db.foos.createIndex( { "name": 1 }, { unique: true } )
try step 2. The results should contains unique.
Hope it could help someone.
EDIT
If your foo table collections contain the duplicated names, you might get error on step 3:
{
"ok" : 0,
"errmsg" : "E11000 duplicate key error collection: mategoal-dev.categories index: name_1 dup key: { : \"TheName\" }",
"code" : 11000,
"codeName" : "DuplicateKey"
}
Then remove all the data or duplicated one(haven't test) first:
db.foos.remove({})
Then try step 3 again.

Query status of inner array of in MongoDB using mongoose

I have a User schema with followers array like below,
const userSchema = new mongoose.Schema({
firstname: {
type: String,
required: true,
},
lastname: {
type: String,
required: true,
},
followers: [{ type: ObjectId, ref: "User" }],
following: [{ type: ObjectId, ref: "User" }],
});
When i query for list of Users i need two extra fields isFollowing and isFollower
(isFollowing exists in my following array, isFollower exists in followers array)
Looks like you need to populate the followers and following arrays:
const users = await User.findOne({ ... })
.populate('followers')
.populate('following');

Search in a reference text field with Mongoose

I have this Schema
var users = new Schema({
name: {type: String, required: true},
email: {type: String, required: true},
password: {type: String, required: true},
following: [{type: Schema.ObjectId, ref: "users"}]
});
users.index({name: 'text'});
I want to use Mongoose to find users who they have "john" in their name and they are exists in following array of user of _id = x
in other way if it was SQL the query would be (it just example to illustrate the relations)
SELECT * FROM users where _id = x AND users.following.name LIKE '%john%'
I think that if following array was embedded in the user collections it would be easy to make.
How can i handle that in mongoose ?
I found the answer here http://mongoosejs.com/docs/populate.html
I used populate with match
.findById("x", {following: 1}).populate({ path: 'following',match: {$text: {$search: "john"}}})

Can I populate ref's in a mongoose.js model instead of everytime I query?

Intro: I am creating a StackExchange clone using Node and Mongo to learn the language. I am currently working on the API.
I have the following 'questionSchema':
var questionSchema = new Schema({
_id : {type: String, default: shortid.generate},
title : {type: String, required: true},
question : {type: String, required: true},
user : {type: Schema.ObjectId, ref: 'User'},
points : {type: Number, default: 0},
date : {type: Date, default: Date.now},
answers : [answerSchema],
votes : [{
user: {type: Schema.ObjectId, ref: 'User', required: true},
vote: {type: Number, enum: [-1,0,1]}
}],
__v : {type: Number, select: false}
});
The idea is that when a user votes on a question the points field is incremented (or decremented) and the userid and vote added to the votes array. I have the vote array to detect if the user has already voted and prevent additional votes.
The problem: I'm having trouble actually checking if the user has voted (checking if their userid exists in the votes array). I have been playing around with adding the method 'hasVoted' to the questionSchema but:
I'm not sure how to actually make the check happen.
I'm also not sure if there is a way for me to filter the votes array during the query (at MongoDB) instead of after node gets the results.
This is my attempt at the method which I know is wrong:
//Has the user already voted on this question?
questionSchema.methods.hasVoted = function (userid, cb) {
this.votes.filter(function(vote) {
if(userid == vote._id) {
return '1';
} else {
return '0';
}
});
};
I would recommend to make vote schema like so
var voteSchema = new Schema({
user: {type: Schema.ObjectId, ref: 'User', required: true},
vote : {type: Number, required: true}
})
var questionSchema = new Schema({
_id : {type: String, default: shortid.generate},
title : {type: String, required: true},
question : {type: String, required: true},
points : {type: Number, default: 0},
date : {type: Date, default: Date.now},
answers : [answerSchema],
votes : [{type: Schema.ObjectId, ref: 'Vote', required: false}]
});
Then just get your question and go through all the votes.
QuestionSchema.findById(question.id)
.populate('votes')
.exec(function (err, question) {
// go through all the votes here
}
or query if there is an question with your user id inside the votes
QuestionSchema.find()
.and([{_id:questionId,},{votes.user:userId}])
.populate('votes') //dunno if you really have to populate i think you don't have to
.exec(function (err, user) {
// check if(user)
}
or do it like described here findOne Subdocument in Mongoose
//EDIT
or if you don't change your schema
QuestionSchema.find({votes.user:userId})
.exec(function (err, user) {
// should return ALL questions where the user id is in the votes your want a specific question do it in a and like in the example above
}
and if you only want that one element from the array you have to make a projection like described here How to find document and single subdocument matching given criterias in MongoDB collection

MongoDB performances $ref vs embedded

I recently started a project using mongodb and nodejs to build a restful web service. Unfortunately mongodb is very new to me, and coming from the relational databases world I'm asking my self a lot of questions.
Let me explain you my problem :
The goal is to build a sort of content management system with social features like a user can post topics that can be shared and commented.
I have 2 possibilities to do this the one using a reference to get topics posted by a user, the second using topics as embedded document of user instead of reference.
So basically I can have these 2 schemas :
var UserSchema = new Schema({
username: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true
},
name: {
type: String
},
first_name: String,
phone: String,
topics: [Topic.schema]
});
var TopicSchema = new Schema({
_creator: {
type: String,
ref: 'User'
},
description: String,
comments: [Comments.schema],
shared_with: [{
type: Schema.ObjectId,
ref: 'User'
}] //[{ type: String, ref: 'User'}]
});
var CommentSchema = new Schema({
_creator: {
type: String,
require: true
},
text: {
type: String,
required: true
},
});
and
var UserSchema = new Schema({
username: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true
},
name: {
type: String
},
first_name: String,
phone: String,
topics: [{ type: Schema.ObjectId, ref: 'Topics'}]
});
var TopicSchema = new Schema({
_creator: {
type: String,
ref: 'User'
},
description: String,
comments: [Comments.schema],
shared_with: [{
type: Schema.ObjectId,
ref: 'User'
}] //[{ type: String, ref: 'User'}]
});
var CommentSchema = new Schema({
_creator: {
type: String,
require: true
},
text: {
type: String,
required: true
},
});
So the first schema uses 1 collection of user document and the second use 1 collection for the user and 1 collection for the topics, this implies to make for example, 2 finds queries to retrieve a user and it's topics but it is also easyer to query directly the topics.
Here is the request I use to retrieve a specific topic with some user info with the first schema :
User.aggregate([
{$match: {
"topics._id":{$in:[mongoose.Types.ObjectId('56158c314861d2e60d000003')]}
}},
{ $unwind:"$topics" },
{$match: {
"topics._id":{$in:[mongoose.Types.ObjectId('56158c314861d2e60d000003')]}
}},
{ $group: {
_id: {
_id:"$_id",
name:"$name",
first_name:"$first_name"
},
topics:{ "$push": "$topics"}
}}
]);
So the question is, what do youh think ? Which is the good schema in your opinion ?
Thanks in advance.
Better solution: using a reference to get topics posted by a user
For this database use, one typically needs to consider the MMAPV1 document size limit (16MB). Putting user, topic, and comments in one document allows the document to grow without bound. If each topic is a page of text (1K), then each user could have about 16,000 topics before the limit is reached. That seems huge, but what happens if you decide to put images, videos, sounds in the topic as the product matures? Converting from an embedded to a normalized schema later would be a lot more work than a simple design choice today.
Similarly, if the comments could grow to cause a topic to exceed the 16MB limit, they should be in a separate collection. Unlikely? Probably. But if you are writing something that will become, say, the Huffington Post - check out comments on their popular articles.
Here is mongo's advice on data model design

Resources