mongoose document filtering properties - javascript

I have defined a schema like
var UserSchema = new Schema({
firstName: { type: String, required: true },
lastName: { type: String, required: true },
email: { type: String, required: true },
location: { type: String, required: true },
picture: { type: String, required: true },
passwordHash: { type: String, required: true },
resetPasswordToken: String,
resetPasswordExpired: Boolean
});
I have a REST Endpoint which return list of all users. In that list I want to hide some properties i.e, passwordHash, resetPasswordToken, resetPasswordExpired
I defined a custom filter function like below
var doFilterUser = function(user) {
_.omit(user, ['passwordHash', 'resetPasswordToken', 'resetPasswordExpired']);
user.id = user._id;
delete user._id;
delete user.__v;
return user;
};
_ is lodash
When I check my API is responding with all user properties
This filter function is defined in common helper module and I am calling it like
User.findOne({_id: id}, function(err, user) {
var filtered = helper.doFilterUser(user);
});
How to resolve this issue?

Try this:
You are allowed to access certain values through mongoose.
User.findOne({_id: id}, 'firstName lastName email location picture', function(err, user){
console.log(user);
});
You just mention the fields needed, after the query.
Hope it helps....

The problem here is that you still have a mongoose document that conforms to s strict schema. If you want to change that document, then you need to make it a "raw" object without all the additional controls:
User.findOne({_id: id}, function(err, user) {
var filtered = helper.doFilterUser(user.toObject());
});
So the .toObject() method here will return an object in it's raw form. That allows you to manipulate the keys how you wish.

You can also explicitly direct it not to serve back certain properties. Useful if you don't want to render a hashed password over the wire. The find method would look like this:
User.find({}, '-id -__v',function(err,users){
})
or
User.findOne({_id: id}, '-id -__v',function(err,user){
})

Related

Friend Request System - Express, MongoDB, EJS

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)

How to use a mongoose model in another model?

I have the two following models. In the user model I want to use an array of Requests and in Request Model I want to use User as an attribute(without the password). How can I do it?
var userSchema = new Schema({
cartaoCidadao: {
type: String,
required: true,
index: {
unique: true,
},
match: /[0-9]{8}/,
},
password: { type: String, required: true },
role: { type: String },
estado: { type: String, enum: ["Infetado", "Suspeito"] },
});
var requestSchema = new Schema({
encaminhado: { type: String },
pessoaRisco: { type: String },
trabalhoRisco: { type: String },
estadoPedido: { type: String },
resultado: { type: String },
});
You can use the schema you defined as a type itself:
var userSchema = new Schema({
// ...
requests: {
type: [requestSchema] // this property type is: array of requests
}
// ...
});
If both models are stored in database and you probably want to go for their association. You can reference one model from another. (see the answer of Muhammad Lahin)
Then you query your parent model and associate the children models with it (https://mongoosejs.com/docs/populate.html)
And here is an example of how you can exclude some fields during the population:
https://mongoosejs.com/docs/populate.html#query-conditions
It will be something like:
User.
find(/* some query */).
populate({
path: 'requests',
select: 'fieldToSelect1 fieldToSelect2' // You can control which fields to include
}).
exec();
you can do something like this
var userSchema = new Schema({
requests: [
{
type: Schema.Types.ObjectId,
ref: "Request",
}
]
});
var requestSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "User"
}
})
you can set the type of the user in the request model like this
type: schema.Types.ObjectID,
ref: 'users'
which users id schema of users and when you want to send the whole user data in the response, use populate and in populate you can omit the password too
now if you see the mondodb database in the request database the whole user object is not saved and only the user_id is saved so you dont have access to the user. you have to send another request to get the user data from the gived user_id which is not good. the solution is you set the schema as I said and when you are sending a response which have the data of the request use populate to add the userdata to the response
res.json({request: requests.find(//options to find a specified request).populate('users','name')})
first argument is the model which is users and the second argument is the wanted field of the users model for example this will return the name of the user. you can add more arguments to add all of them except the password
const mongoose = require('mongoose')
const productSchema = new mongoose.Schema({
user: {
type: mongoose.Types.ObjectId,
ref: 'User'
}
});

Mongoose auto fill data by searching in reference

const UserSchema = new Schema(
{
referrals: {
ref: 'User',
type: [mongoose.Schema.Types.ObjectId],
},
referredBy: {
ref: 'User',
type: mongoose.Schema.Types.ObjectId,
},
}
);
I want Mongoose to find users who have current user _id in referredBy reference.
In other words, eg: find all users who have '_IDOfSpecificUser' in their referredBy field and put all the found users in the array of referrals where user's _id is '_IDOfSpecificUser'.
How can I handle that in mongoose?
Simplest is using find
User.
find({ "referredBy" : "xxxxxxxxxxxx" }).
exec(function (err, users) {
if (err) return handleError(err);
console.log('The users are an array: ', users);
});
Refer to https://mongoosejs.com/docs/populate.html
If you want to convert bellow function to static method inside UserSchema, please refer to this https://mongoosejs.com/docs/api.html#schema_Schema-static and https://mongoosejs.com/docs/2.7.x/docs/methods-statics.html

Mongoose/Mongodb: Exclude fields from populated query data

I use the following mongoose query in a MEAN-environment to find and output a particular author and his corresponding books.
Author
.findOne({personcode: code})
.select('-_id')
.select('-__v')
.populate('bookids') //referencing to book documents in another collection (->array of bookids)
.select('-_id') //this doens't affect the data coming from the bookids-documents
.select('-__v') //this doens't affect the data coming from the bookids-documents
.exec(function (err, data) {
//foo
});
I would also like to exclude the "_id" and "__v" fields from the populated data coming from the external documents. How can that be achieved?
The second parameter of populate is a field selection string, so you can do this as:
Author
.findOne({personcode: code})
.select('-_id -__v')
.populate('bookids', '-_id -__v')
.exec(function (err, data) {
//foo
});
Note that you should combine your field selections into a single string.
Thanks JohnnyHK, and for object parameter this works:
Entity.populate({
path: 'bookids',
// some other properties
match: {
active: true
},
// some other properties
select: '-_id -__v' // <-- this is the way
}).then(...) // etc
To exclude individually
User.findOne({_id: userId}).select("-password")
To exclude using the schema
var userSchema = mongoose.Schema({
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
select: false,
},
});
or this will also work
db.collection.find({},{"field_req" : 1,"field_exclude":0});
I came searching for something slightly different. just in case someone needs same as me.
you can specify specific fields to auto-populate during creation of schema as shown below
const randomSchema = mongoose.Schema({
name: {type: String,trim: true},
username: {type: String,trim: true},
enemies: {
type: ObjectId,
ref: randomMongooseModel,
autopopulate:{
select: '-password -randonSensitiveField' // remove listed fields from selection
}
},
friends: {
type: ObjectId,
ref: randomMongooseModel,
autopopulate:{
select: '_id name email username' // select only listed fields
}
}
});
I am using mongoose-autopopulate plugin for this example

Relational database design to mongoDB/mongoose design

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.

Categories