For example I have a page with url "/foo", and I need to display my authenticated users nicknames on this page. Is there any default node.js/passport function for this, or I need to add every user of this page to database and display it with websockets?
User schema:
var userSchema = new Schema({
email: {type: String, required: true, unique: true },
password: {type: String, required: true },
nickname: { type: String, default: 'User'},
createdAt: { type: Date, default: Date.now }
});
Check if user is logged in:
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
req.session.oldUrl = req.url;
res.redirect('/');
}
This is how I solved it in a service that hosts stories. I had to display the number of users and their names on the current story page. I wanted to keep the solution simple enough so I decided to use a simple table in SQL.
In an SQL table with a single column, I stored PAGE_{PAGEID}_USER_{USERID}.
For every page when fetched by the user I added an entry on this table and queried the table column with the regex PAGE_{PAGEID}_USER_(.*) with updatedAt less than 1 minute of the current time.
This gives you the IDs of the users on the current page in the past 1 minute.
PS: The correct way to do this would be to do this very same thing in a Wide Column Store
Related
I want to create a social network thus allowing users to send and interact with frind requests. As of now I have created the register, log-in and "search for other users function".
When I find and select another user, I display their user-info and have created a "Add friend" button.
Can anyone help me in a direction of the creation of the "Add friend" option? I have looked around for some time now, and not been able to find the correct solution. Below I have attached my UserSchema and route for finding users:
//User Schema
const UserSchema = new mongoose.Schema({
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
password: {
type: String,
required: true
},
},{ collection: 'Users' });
//Get single user based on ID
router.get('/user/get:id', ensureAuthenticated, function (req, res) {
MongoClient.connect(DBUri,{useUnifiedTopology: true }, function (err, db) {
let dbo = db.db(DBName);
const query = {_id: objectId(req.params.id)}
dbo.collection("Users").find(query).toArray(function(err, resultTasks) {
if (err) throw err;
res.render('../View/findFriend', {
resultTasks: resultTasks
});
db.close();
});
});
});
You can add something like this in your user schema:
friends: [{ type : ObjectId, ref: 'User' }],
OR
friends: userSchema
Take the one which suits you.
What that will do is add an array to the user, Then you can store IDs of friends.(Who are other users, hence the ref: 'User')
Then, When you have to fetch users you can do:
User.find(<ID or whatever you have to find Users>).populate('friends')
Also, To push a new friend simply use: user.friends.push(newFriend._id)
I would like to know how to keep track of the values of a document in MongoDB.
It's a MongoDB Database with a Node and Express backend.
Say I have a document, which is part of the Patients collection.
{
"_id": "4k2lK49938d82kL",
"firstName": "John",
"objective": "Burn fat"
}
Then I edit the "objective" property, so the document results like this:
{
"_id": "4k2lK49938d82kL",
"firstName": "John",
"objective": "Gain muscle"
}
What's the best/most efficient way to keep track of that change? In other words, I would like to know that the "objective" property had the value "Burn fat" in the past, and access it in the future.
Thanks a lot!
Maintaining/tracking history in the same document is not all recommended. As the document size will keep on increasing leading to
probably if there are too many updates, 16mb document size limit
Performance degrades
Instead, you should maintain a separate collection for history. You might have use hibernates' Javers or envers for auditing for your relational databases. if not you can check how they work. A separate table (xyz_AUD) is maintained for each table (xyz). For each row (with primary key abc) in xyz table, there exist multiple rows in xyz_AUD table, where each row is version of that row.
Moreover, Javers also support MongoDB auditing. If you are using java you can directly use it. No need to write your own logic.
Refer - https://nullbeans.com/auditing-using-spring-boot-mongodb-and-javers/
One more thing, Javers Envers Hibernate are java libraries. But I'm sure for other programming languages also, similar libraries will be present.
There is a mongoose plugin as well -
https://www.npmjs.com/package/mongoose-audit (quite oudated 4 years)
https://github.com/nassor/mongoose-history#readme (better)
Maybe you can change the type of "objective" to array and track the changes in it. the last one of the array is the latest value.
Maintain it as a sub-document like below
{
"_id": "4k2lK49938d82kL",
"firstName": "John",
"objective": {
obj1: "Gain muscle",
obj2: "Burn fat"
}
}
You can also maintain it as an array field but remember, mongodb doesn't allow you to maintain uniqueness in an array field and if you plan to index the "objective" field, you'll have to create a multi key index
I think the simplest solution would be to use and update an array:
const patientSchema = new Schema({
firstName: { type: String, required: true },
lastName: { type: String, required: true },
objective: { type: String, required: true }
notes: [{
date: { type: Date, default: Date.now() },
note: { type: String, required: true }
}],
});
Then when you want to update the objective...
const updatePatientObjective = async (req, res) => {
try {
// check if _id and new objective exist in req.body
const { _id, objective, date } = req.body;
if (!_id || !objective) throw "Unable to update patient's objective.";
// make sure provided _id is valid
const existingPatient = await Patient.findOne({ _id });
if (!existingPatient) throw "Unable to locate that patient.";
// pull out objective as previousObjective
const { objective: previousObjective } = existingPatient;
// update patient's objective while pushing
// the previous objective into the notes sub document
await existingPatient.updateOne({
// update current objective
$set { objective },
// push an object with a date and note (previouseObjective)
// into a notes array
$push: {
notes: {
date,
note: previousObjective
},
},
}),
);
// send back response
res
.status(201)
.json({ message: "Successfully updated your objective!" });
} catch (err) {
return res.status(400).json({ err: err.toString() });
}
};
Document will look like:
firstName: "John",
lastName: "Smith",
objective: "Lose body fat.",
notes: [
{
date: 2019-07-19T17:45:43-07:00,
note: "Gain muscle".
},
{
date: 2019-08-09T12:00:38-07:00,
note: "Work on cardio."
}
{
date: 2019-08-29T19:00:38-07:00,
note: "Become a fullstack web developer."
}
...etc
]
Alternatively, if you're worried about document size, then create a separate schema for patient history and reference the user's id (or just store the patient's _id as a string instead of referencing an ObjectId, whichever you prefer):
const patientHistorySchema = new Schema({
_id: { type: Schema.Types.ObjectId, ref: "Patient", required: true },
objective: { type: String, required: true }
});
Then create a new patient history document when the objective is updated...
PatientHistory.create({ _id, objective: previousObjective });
And if you need to access to the patient history documents...
PatientHistory.find({ _id });
I am building an app usine Node/EJS/Mongo where the user is building a capability survey, and needs to set the desired level for each question. The form that they use to pick the levels has a series of selects that look like:
<select class="form-control col-sm-4" id="<%=capability.capabilityId%>" name="<%=capability.capabilityId%>">
<option value=1>Developing</option>
<option value=2>Intermediate</option>
<option value=3>Advanced</option>
<option value=4>Role Model</option>
</select>
When the user then submits this form, I want to update the assessment to load in these expected levels.
The schema for assessments in my mongodb looks like:
var assessmentSchema = new mongoose.Schema ({
title: String,
startDate: Date,
endDate: Date,
behaviours: [{
behaviourName: String,
behaviourId: String,
order: Number,
capabilities: [{
orderCap: Number,
capabilityId: String,
capabilityName: String,
capabilityDesc: String,
developing: String,
intermediate: String,
advanced: String,
roleModel: String,
expectedLevel: Number,
motivation1: String,
motivation2: String,
motivation3: String,
motivation4: String,
motivation5: String
}] //capabilities object
}],
targetEmployees:[{
type: mongoose.Schema.Types.ObjectId,
ref: "Users"
}] //behaviours object
});
What I am thinking is I want to loop through all the capabilities, find the entry in req.body that has a name that matches capabilityId, and then update desiredLevel. I just can't see how to make it work. My route code currently looks like:
router.put(':id/levels', function(req, res) {
Assessment.findById(req.params.id, function(err, foundAssessment) {
foundAssessment.behaviours.forEach(function(b) {
b.capabilities.forEach(function(c) {
c.expectedLevel = req.body.SOMETHINGHERE
});
});
foundAssessment.save(function(err) {
if (err) {
console.log(err);
req.flash("error", err.message);
res.redirect("back");
} else {
// Send json back to xhr request
res.json(foundAssessment);
}
});
});
});
You can read request body dynamic attributes like this:
req.body[variable];
I am just learning how to build websites using MEANJS, and I am structuring my data but unsure on the best practice, I am very new to the NoSql concept.
I need to store:
questions
answers
likes
saved_questions
In my app I enable the user to save questions to be viewed later, as well as they can access any answer they posted. And I provide some stats for each question (i.e. number of likes, number of answers, etc)
Should I create one document for "question" and everything inside of it:
{_id: <ObjectId>,
user_id: <ObjectId>,
question: 'how can we....',
answers: [{user_id: <ObjectId>, answer: ''}],
likes: [{user_id: <ObjectId>}],
saves: [{user_id: <ObjectId>}]
}
Or should I make multiple documents for each? Or should I use both methods?
I would have at least two database models, maybe one for the User and the other for the Question. One of the great things about the MEAN.JS boiler plate is that it already comes with a User module complete with sign-up, login/logout functionality. So you have that out of the way as soon as you deploy your new project.
With that already out of the way, I would use the Yo Generator to create a new CRUD module called Question. You can add the files manually, but Yo helps you do it quickly and accurately by automatically placing the files in the correct place, complete with sample code to help you set it up. To learn how to use the Yo Generator I would look at the Yo Generator Section of the MEAN.JS docs.
From your app's root directory, run yo meanjs:crud-module Question. This will create all of the necessary files you need for the database model, as well as a new module on the front & back ends with samples of how to create/read/update/delete the questions. Now, if you log in, you will see the new module in your menu bar.
Then open app/controllers/models/question.server.model.js. This is the file that you can define your new database model. Depending on how complex/relational you want the data to be, you'd want your Mongoose model to look something like this:
var QuestionSchema = new Schema({
created: {
type: Date,
default: Date.now
},
title: {
type: String,
default: '',
trim: true,
required: 'Title cannot be blank'
},
question: {
type: String,
default: '',
trim: true
},
user: {
type: Schema.ObjectId,
ref: 'User'
},
answers: {
type: Array,
default: []
},
likes: {
type: Array,
default: []
},
saves: {
type: Array,
default: []
}
});
Obviously this is very simplified. You may want to create separate mongoose models for the likes, saves, and reports so you can store more data about each (ie: user_id, date, reason for reporting/saving/liking, etc.) To read more about how to make the perfect mongoose model for your needs, I would definitely check out the docs about Mongoose Schemas at mongoosejs.com.
I hope that helps!
Continued...
To get a list of a given user's actions, I would go ahead and make a new Mongoose Schema for each type of action (comments, likes, saves), and store the details of user's actions there. For instance, in Answers schema you could store the actual comment, who said it, when they said it, what question it was for etc. Then simply query the action tables for a given user ID to retrieve your list of actions.
So..
var QuestionSchema = new Schema({
created: {
type: Date,
default: Date.now
},
title: {
type: String,
default: '',
trim: true,
required: 'Title cannot be blank'
},
question: {
type: String,
default: '',
trim: true
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
var AnswerSchema = new Schema({
created: {
type: Date,
default: Date.now
},
question: {
type: Schema.ObjectId,
ref: 'Question'
},
user: {
type: Schema.ObjectId,
ref: 'User'
},
answer: {
type: String,
default: '',
trim: true
}
});
I have recently started using mongoDB and mongoose for my new node.js application. Having only used relational databases before I am struggling to adapt to the mongoDB/noSQL way of thinking such as denormalization and lack of foreign key relationships. I have this relational database design:
**Users Table**
user_id
username
email
password
**Games Table**
game_id
game_name
**Lobbies Table**
lobby_id
game_id
lobby_name
**Scores Table**
user_id
game_id
score
So, each lobby belongs to a game, and multiple lobbies can belong to the same game. Users also have different scores for different games. So far for my user schema I have the following:
var UserSchema = new mongoose.Schema({
username: {
type: String,
index: true,
required: true,
unique: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
}
});
So my question is, how would I go about structing the relational design into mongoDB/mongoose schemas? Thanks!
EDIT 1
I have now tried to create all the schemas but I have no idea if this is the right approach or not.
var UserSchema = new mongoose.Schema({
_id: Number,
username: {
type: String,
index: true,
required: true,
unique: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
scores: [{ type: Schema.Types.ObjectId, ref: 'Score' }]
});
var GameSchema = new mongoose.Schema({
_id: Number,
name: String
});
var LobbySchema = new mongoose.Schema({
_id: Number,
_game: { type: Number, ref: 'Game' },
name: String
});
var ScoreSchema = new mongoose.Schema({
_user : { type: Number, ref: 'User' },
_game : { type: Number, ref: 'Game' },
score: Number
});
Mongoose is designed in such a way that you can model your tables relationally with relative ease and populate relational data based on the ref you defined in the schema. The gotcha is that you need to be careful with populating. If you populate too much or nest your populations you will run into performance bottle necks.
Your approach in Edit 1 is largely correct however you usually don't want to populate a remote ref based on a Number or set the _id of a model to a Number since mongo uses it's own hashing mechanism for managing the _id, this would usually be an ObjectId with _id implied. Example as shown below:
var ScoreSchema = new mongoose.Schema({
user : { type: Schema.Types.ObjectId, ref: 'User' },
game : { type: Schema.Types.ObjectId, ref: 'Game' },
score: Number
});
If for some reason you need to maintain a number id for your records consider calling it uid or something that won't conflict with mongo / mongoose internals. Good luck!
First of all, you are hitting on some good points here. The beauty of Mongoose is that you can easily connect and bind schemas to a single collection and reference them in other collections, thus getting the best of both relational and non-relational DBs.
Also, you wouldn't have _id as one of you properties, Mongo will add it for you.
I've made some changes to your schemas using the mongoose.Schema.Types.ObjectId type.
var UserSchema = new mongoose.Schema({
username: {
type: String,
index: true,
required: true,
unique: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
scores: [{ type: Schema.Types.ObjectId, ref: 'Score' }]
});
var GameSchema = new mongoose.Schema({
name: String
});
var LobbySchema = new mongoose.Schema({
_game: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Game'
},
name: String
});
var ScoreSchema = new mongoose.Schema({
_user : {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
_game : {
type: mongoose.Schema.Types.ObjectId,
ref: 'Game'
},
score: Number
});
This will allow you to query your database and populate any referenced collections and objects.
For example:
ScoreSchema.find({_id:##userIdHere##})
.populate('_user')
.populate('_game')
.exec(function(err, foundScore){
if(err){
res.send(err)
} else {
res.send(foundScore)
}
}
This will populate the related user and game properties.
As you edited the post, I think it would be good. At least not bad :)
Check Mongoose Query Population. It's very useful to get related data.
var mongoose = require('mongoose'),
ObjectId = mongoose.Schema.Types.ObjectId
// code, code, code
function something(req, res) {
var id = req.params.id
// test id
return Lobby.findOne({_id: new ObjectId(id)})
.populate('_game')
.exec(function(error, lobby) {
console.log(lobby._game.name);
});
}
Two ways (that I know of). You store an id (that is indexed) and once you query the first table, you then query the second table to grab info from that, as there are no joins. This means that if you grab say, user id's from one table, you will then need to make multiple queries to the user table to get the user's data.
The other way is to store it all in one table, even if it's repetitive. If all you need to store is for example, a user's screen name with something else, then just store it with the other data, even if it's already in the user table. I'm sure others will know of better/different ways.