I need to populate Referenced database in ListView. In this case I need to Show the Designation Name of the User which is the reference in User Model. How do I call it in Flutter to display the Designation name?
My UserSchema is
var UserSchema = new Schema({
'username' : String,
'role_id' : Number,
'designation_id' : {
type: Schema.Types.ObjectId,
ref: 'Designation'
},
'login_id' : String,
'password' : String,
'status' : String,
'company_id' : {
type: Schema.Types.ObjectId,
ref: 'Company'
},
'enable_login' : Number,
'creation_user_id' : {
type: Schema.Types.ObjectId,
ref: 'User'
}, 'update_user_id' : {
type: Schema.Types.ObjectId,
ref: 'User'
},
'creation_dt' : Date,
'update_dt' : Date});
module.exports = mongoose.model('User', UserSchema);
My Destination Model is:
var DesignationSchema = new Schema({
'name' : String,
'designation_id' : Number,
'company_id' : {
type: Schema.Types.ObjectId,
ref: 'Company'
},
'creation_user_id' : {
type: Schema.Types.ObjectId,
ref: 'User'
},
'update_user_id' : {
type: Schema.Types.ObjectId,
ref: 'User'
},
'creation_dt' : Date,
'update_dt' : Date
});
module.exports = mongoose.model('Designation', DesignationSchema);
User Controller List is:
list: function (req, res) {
UserModel.find(function (err, Users) {
if (err) {
return res.status(500).json({
message: 'Error when getting User.',
error: err
});
}
return res.json(Users);
});
},
In Flutter I created a Service for getting all Users
static Future<Users> getAllUsers() async{
try{
final response = await http.get(BASE_URL + USERS);
if(response.statusCode == 200){
return parseUsers(response.body);
}else{
return Users();
}
}catch(e){
print('Error ${e.toString()}');
return Users();
}
}
static Users parseUsers (String responseBody){
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
List<User> users = parsed.map<User>((json) => User.fromJson(json)).toList();
Users _users = Users();
_users.users = users;
return _users;
}
Showing it in Flutter via
Text(users.users[index].username,style: TextStyle(color: Colors.black87, fontWeight: FontWeight.bold, fontFamily: 'Roboto',),)
Text("Email: ${users.users[index].login_id}", style: TextStyle(color: Colors.black87, fontSize: 12,fontFamily: 'Roboto',))
Text("Designation: Estimate Engineer", style: TextStyle(color: Colors.black87,fontSize: 12,fontFamily: 'Roboto',)),
Thank you for your Support.
I guess you need to change the response and implement the schema inside schema concept
here is the link which talks about the schema inside schema on stack overflow
link : Mongoose schema within schema
Your response should look like this.
{
"_id": "5dee58cba34fa96a09d1f049",
"username": "Moin",
"role_id": 1,
"designation_id": {
"_id": "5dee57d4a34fa96a09d1f047",
"name": "Administrator",
"designation_id": 1,
"company_id": "5dee54681fc99665221ec960",
"creation_user_id": "5dee2a859eae3e0a98109d81",
"update_user_id": "5dee2a859eae3e0a98109d81",
"creation_dt": null,
"update_dt": null,
"__v": 0
},
"login_id": "moin#email.com",
"password": "moin123",
"status": "1",
"company_id": "5dee54681fc99665221ec960",
"enable_login": 1,
"creation_user_id": "5dee2a859eae3e0a98109d81",
"update_user_id": "5dee2a859eae3e0a98109d81",
"creation_dt": null,
"update_dt": null,
"__v": 0
},
Insted of storing a ID you should store the whole schema to esaily get the designation,
Hope this helps.
Related
I am trying to create a query in my API endpoint that fetches all documents and sub documents. Currently I have only had luck fetching the ID's of each sub document instead of fetching all of the information within each document. Is it possible to extract the data from each sub document based on each ID using mongoose(along with the data in the parent document)?
I have followed the documentation for deep populating: https://mongoosejs.com/docs/populate.html#deep-populate, but have not had much luck.
This is my current code that "works" but only responds with the ID's of each sub document(and the User data which is fine).
//The get request
router.get("/allUsers", async (req, res) => {
const allUsers = await User.find()
.populate("Post")
.exec();
res.send(allUsers);
});
//User Schema
const userSchema = Schema({
name: { type: String, required: true },
date: { type: Date, default: Date.now },
posts: [{ type: Schema.Types.ObjectId, ref: "Post" }]
});
//Post Schema
const postSchema = new mongoose.Schema({
name: { type: String, required: true },
description: { type: String, required: true }
date: { type: Date, default: Date.now }
});
What am I missing to extract ALL of the information from the sub documents instead of the ID alone?
As JohnnyHK stated you need to use the name of the field to populate.
If you are having empty array of posts, please check your users collection if you have a posts array with the post ids.
Here are the steps I could make it work:
1-) Inserted 2 posts like this to the posts collection.
{
"_id" : ObjectId("5dc6cd65067a61191839ff38"),
"name" : "name 3",
"description" : "description 3",
"date" : ISODate("2019-11-09T17:29:57.249+03:00")
}
{
"_id" : ObjectId("5dc6cd5d067a61191839ff37"),
"name" : "name 2",
"description" : "description 2",
"date" : ISODate("2019-11-09T17:29:49.070+03:00")
}
2-) Inserted a user with these 2 posts id.
{
"_id" : ObjectId("5dc6cf5b67da8a40cc519866"),
"posts" : [
ObjectId("5dc6cd5d067a61191839ff37"),
ObjectId("5dc6cd65067a61191839ff38")
],
"name" : "user 1",
"date" : ISODate("2019-11-09T17:38:19.807+03:00")
}
3-) And used the following route to get posts in user.
router.get("/allUsers", async (req, res) => {
const allUsers = await User.find().populate("posts");
res.send(allUsers);
});
The result is like this:
[
{
"posts": [
{
"_id": "5dc6cd5d067a61191839ff37",
"name": "name 2",
"description": "description 2",
"date": "2019-11-09T14:29:49.070Z",
"__v": 0
},
{
"_id": "5dc6cd65067a61191839ff38",
"name": "name 3",
"description": "description 3",
"date": "2019-11-09T14:29:57.249Z",
"__v": 0
}
],
"_id": "5dc6cf5b67da8a40cc519866",
"name": "user 1",
"date": "2019-11-09T14:38:19.807Z",
"__v": 0
}
]
By the way your user and post modeling is not ideal. Every time user posts the user document must be updated.
I would use the following schemas (parent referencing).
User model ( I removed the posts field, and added virtual feature)
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
date: { type: Date, default: Date.now }
}, {
toJSON: { virtuals: true },
toObject: { virtuals: true }
});
userSchema.virtual("posts", {
ref: "Post",
foreignField: "userId",
localField: "_id"
})
const User = mongoose.model("User", userSchema);
module.exports = User;
Post model ( I added the userId)
const mongoose = require("mongoose");
const postSchema = new mongoose.Schema({
name: { type: String, required: true },
description: { type: String, required: true },
date: { type: Date, default: Date.now },
userId: { type: mongoose.Schema.ObjectId, ref: "User" }
});
const Post = mongoose.model("Post", postSchema);
module.exports = Post;
With this way user document is not affected when a user posts. And you can use the same /allUsers route to get users with their posts included.
Pass the name of the field to populate, not the name of the model:
const allUsers = await User.find()
.populate("posts")
.exec();
use populate inside other populate
.populate({
path: 'document',
populate: { path: 'sub document'}
})
Basically I have a mongodb collection called 'people'
whose schema is as follows:
people: {
name: String,
friends: [{firstName: String, lastName: String}]
}
Now, I have a very basic express application that connects to the database and successfully creates 'people' with an empty friends array.
In a secondary place in the application, a form is in place to add friends. The form takes in firstName and lastName and then POSTs with the name field also for reference to the proper people object.
What I'm having a hard time doing is creating a new friend object and then "pushing" it into the friends array.
I know that when I do this via the mongo console I use the update function with $push as my second argument after the lookup criteria, but I can't seem to find the appropriate way to get mongoose to do this.
db.people.update({name: "John"}, {$push: {friends: {firstName: "Harry", lastName: "Potter"}}});
Assuming, var friend = { firstName: 'Harry', lastName: 'Potter' };
There are two options you have:
Update the model in-memory, and save (plain javascript array.push):
person.friends.push(friend);
person.save(done);
or
PersonModel.update(
{ _id: person._id },
{ $push: { friends: friend } },
done
);
I always try and go for the first option when possible, because it'll respect more of the benefits that mongoose gives you (hooks, validation, etc.).
However, if you are doing lots of concurrent writes, you will hit race conditions where you'll end up with nasty version errors to stop you from replacing the entire model each time and losing the previous friend you added. So only go to the latter when it's absolutely necessary.
The $push operator appends a specified value to an array.
{ $push: { <field1>: <value1>, ... } }
$push adds the array field with the value as its element.
Above answer fulfils all the requirements, but I got it working by doing the following
var objFriends = { fname:"fname",lname:"lname",surname:"surname" };
People.findOneAndUpdate(
{ _id: req.body.id },
{ $push: { friends: objFriends } },
function (error, success) {
if (error) {
console.log(error);
} else {
console.log(success);
}
});
)
Another way to push items into array using Mongoose is- $addToSet, if you want only unique items to be pushed into array. $push operator simply adds the object to array whether or not the object is already present, while $addToSet does that only if the object is not present in the array so as not to incorporate duplicacy.
PersonModel.update(
{ _id: person._id },
{ $addToSet: { friends: friend } }
);
This will look for the object you are adding to array. If found, does nothing. If not, adds it to the array.
References:
$addToSet
MongooseArray.prototype.addToSet()
Use $push to update document and insert new value inside an array.
find:
db.getCollection('noti').find({})
result for find:
{
"_id" : ObjectId("5bc061f05a4c0511a9252e88"),
"count" : 1.0,
"color" : "green",
"icon" : "circle",
"graph" : [
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 2.0
}
],
"name" : "online visitor",
"read" : false,
"date" : ISODate("2018-10-12T08:57:20.853Z"),
"__v" : 0.0
}
update:
db.getCollection('noti').findOneAndUpdate(
{ _id: ObjectId("5bc061f05a4c0511a9252e88") },
{ $push: {
graph: {
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 3.0
}
}
})
result for update:
{
"_id" : ObjectId("5bc061f05a4c0511a9252e88"),
"count" : 1.0,
"color" : "green",
"icon" : "circle",
"graph" : [
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 2.0
},
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 3.0
}
],
"name" : "online visitor",
"read" : false,
"date" : ISODate("2018-10-12T08:57:20.853Z"),
"__v" : 0.0
}
First I tried this code
const peopleSchema = new mongoose.Schema({
name: String,
friends: [
{
firstName: String,
lastName: String,
},
],
});
const People = mongoose.model("person", peopleSchema);
const first = new Note({
name: "Yash Salvi",
notes: [
{
firstName: "Johnny",
lastName: "Johnson",
},
],
});
first.save();
const friendNew = {
firstName: "Alice",
lastName: "Parker",
};
People.findOneAndUpdate(
{ name: "Yash Salvi" },
{ $push: { friends: friendNew } },
function (error, success) {
if (error) {
console.log(error);
} else {
console.log(success);
}
}
);
But I noticed that only first friend (i.e. Johhny Johnson) gets saved and the objective to push array element in existing array of "friends" doesn't seem to work as when I run the code , in database in only shows "First friend" and "friends" array has only one element !
So the simple solution is written below
const peopleSchema = new mongoose.Schema({
name: String,
friends: [
{
firstName: String,
lastName: String,
},
],
});
const People = mongoose.model("person", peopleSchema);
const first = new Note({
name: "Yash Salvi",
notes: [
{
firstName: "Johnny",
lastName: "Johnson",
},
],
});
first.save();
const friendNew = {
firstName: "Alice",
lastName: "Parker",
};
People.findOneAndUpdate(
{ name: "Yash Salvi" },
{ $push: { friends: friendNew } },
{ upsert: true }
);
Adding "{ upsert: true }" solved problem in my case and once code is saved and I run it , I see that "friends" array now has 2 elements !
The upsert = true option creates the object if it doesn't exist. default is set to false.
if it doesn't work use below snippet
People.findOneAndUpdate(
{ name: "Yash Salvi" },
{ $push: { friends: friendNew } },
).exec();
An easy way to do that is to use the following:
var John = people.findOne({name: "John"});
John.friends.push({firstName: "Harry", lastName: "Potter"});
John.save();
In my case, I did this
const eventId = event.id;
User.findByIdAndUpdate(id, { $push: { createdEvents: eventId } }).exec();
Push to nested field - use a dot notation
For anyone wondering how to push to a nested field when you have for example this Schema.
const UserModel = new mongoose.schema({
friends: {
bestFriends: [{ firstName: String, lastName: String }],
otherFriends: [{ firstName: String, lastName: String }]
}
});
You just use a dot notation, like this:
const updatedUser = await UserModel.update({_id: args._id}, {
$push: {
"friends.bestFriends": {firstName: "Ima", lastName: "Weiner"}
}
});
This is how you could push an item - official docs
const schema = Schema({ nums: [Number] });
const Model = mongoose.model('Test', schema);
const doc = await Model.create({ nums: [3, 4] });
doc.nums.push(5); // Add 5 to the end of the array
await doc.save();
// You can also pass an object with `$each` as the
// first parameter to use MongoDB's `$position`
doc.nums.push({
$each: [1, 2],
$position: 0
});
doc.nums;
// This is the my solution for this question.
// I want to add new object in worKingHours(array of objects) -->
workingHours: [
{
workingDate: Date,
entryTime: Date,
exitTime: Date,
},
],
// employeeRoutes.js
const express = require("express");
const router = express.Router();
const EmployeeController = require("../controllers/employeeController");
router
.route("/:id")
.put(EmployeeController.updateWorkingDay)
// employeeModel.js
const mongoose = require("mongoose");
const validator = require("validator");
const employeeSchema = new mongoose.Schema(
{
name: {
type: String,
required: [true, "Please enter your name"],
},
address: {
type: String,
required: [true, "Please enter your name"],
},
email: {
type: String,
unique: true,
lowercase: true,
required: [true, "Please enter your name"],
validate: [validator.isEmail, "Please provide a valid email"],
},
phone: {
type: String,
required: [true, "Please enter your name"],
},
joiningDate: {
type: Date,
required: [true, "Please Enter your joining date"],
},
workingHours: [
{
workingDate: Date,
entryTime: Date,
exitTime: Date,
},
],
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true },
}
);
const Employee = mongoose.model("Employee", employeeSchema);
module.exports = Employee;
// employeeContoller.js
/////////////////////////// SOLUTION IS BELOW ///////////////////////////////
// This is for adding another day, entry and exit time
exports.updateWorkingDay = async (req, res) => {
const doc = await Employee.findByIdAndUpdate(req.params.id, {
$push: {
workingHours: req.body,
},
});
res.status(200).json({
status: "true",
data: { doc },
});
};
https://www.youtube.com/watch?v=gtUPPO8Re98
I ran into this issue as well. My fix was to create a child schema. See below for an example for your models.
---- Person model
const mongoose = require('mongoose');
const SingleFriend = require('./SingleFriend');
const Schema = mongoose.Schema;
const productSchema = new Schema({
friends : [SingleFriend.schema]
});
module.exports = mongoose.model('Person', personSchema);
***Important: SingleFriend.schema -> make sure to use lowercase for schema
--- Child schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const SingleFriendSchema = new Schema({
Name: String
});
module.exports = mongoose.model('SingleFriend', SingleFriendSchema);
Having trouble with the following schema
const FriendRequestSchema = new Schema({
requester: {
_id: {
type: Schema.Types.ObjectId
}
},
profilePicture: String,
pending: {
type: Boolean,
default: true
}
})
the way i create a new friend request
const friendRequest = new FriendRequest({
requester: {
_id: req.user._id
},
profilePicture: req.user.profilePicture
})
results object
{
"pending": true,
"_id": {
"$oid": "5ab4fdb07525fd6880d1a6b9"
},
"profilePicture": "/assets/profile_icons/glasses.png"
}
now the problem is:
im missing the requester field
the id field is always different and dosent save the requester id
how can i fix this ?
Try change you schema like this:
requester: {
type: Schema.Types.ObjectId,
required: true,
ref: 'User', // depends on how you called user schema
},
Usage:
const friendRequest = new FriendRequest({
requester: req.user._id,
profilePicture: req.user.profilePicture
})
let's say there was a User model and a Post model. In this situation User's would have many posts; User would be the parent and Post would be the child. Is it possible to query for posts directly?
For instance if I wanted to do something like
app.get('/post/search/:query', (req,res) => {
Posts.find({title: req.params.query }, (err,post) => {
res.send(JSON.stringify(post))
})
})
or would one have to do:
app.get('/post/search/:query',(req,res) => {
let resultsFromQuery = [];
User.find({'post.title':req.params.query'}, (err,user) => {
user.posts.forEach((post) => {
if(post.title === req.params.query){
resultsFromQuery.push(post);
}
})
})
res.send(JSON.stringify(resultsFromQuery))
})
EDIT: Here is my schema's.
User Schema (Parent)
const mongoose = require('mongoose'),
Schema = mongoose.Schema,
PostSchema = require('./post.js');
let UserSchema = new Schema({
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
posts: [PostSchema]
})
module.exports = mongoose.model('User',UserSchema);
Post Schema (Child)
const mongoose = require('mongoose'),
Schema = mongoose.Schema;
let PostSchema = new Schema({
title: {
type: String
},
description: {
type: String
},
image: {
type: String
},
original_poster: {
id: {
type: String,
required: true
},
username: {
type: String,
required: true
}
},
tags: {
type: [String],
required: true
}
})
module.exports = PostSchema;
EDIT:
Here is a sample document
the result of db.users.find({username: 'john'})
{
"_id" : ObjectId("5a163317bf92864245250cf4"),
"username" : "john",
"password" : "$2a$10$mvE.UNgvBZgOURAv28xyA.UdlJi4Zj9IX.OIiOCdp/HC.Cpkuq.ru",
"posts" : [
{
"_id" : ObjectId("5a17c32d54d6ef4987ea275b"),
"title" : "Dogs are cool",
"description" : "I like huskies",
"image" : "https://media1.giphy.com/media/EvRj5lfd8ctUY/giphy.gif",
"original_poster" : {
"id" : "5a163317bf92864245250cf4",
"username" : "john"
},
"tags" : [
"puppies",
"dogs"
]
}
],
"__v" : 1
}
Yes you can find directly the post title from the user model. like bellow
User.find({"posts.title": "Cats are cool"}, (err, users) => {
if(err) {
// return error
}
return res.send(users)
})
That will return user with all post not only the matching post title. So to return only matching post title can use $ positional operator. like this query
User.find({"posts.title": "Cats are cool"},
{username: 1, "posts.$": 1}, // add that you need to project
(err, users) => {
if(err) {
// return error
}
return res.send(users)
})
that only return matching post
Since you are saving OP data, why not do:
// you'll need to adapt how your are getting the user-id here
const { user } = req
Post.find({ title: 'the title', 'original_poster.id': user.id }, (err, posts) => {
console.log(posts); })
Though I would advise you to adjust your Post-schema:
original_poster: {
type: Schema.Types.ObjectId,
ref: 'User'
}
},
Then you can do Post.find({}).populate('original_poster') to include it in your results.!
I've been trying to get this populate thing to work, but I'm getting issues because I am not getting the expected results, and no errors to work with. Just simply an empty array.
My models look like this. Each their own file
var mongoose = require( 'mongoose' );
var upgradeSchema = new mongoose.Schema({
type: {
type: String,
default: "Any"
},
ability: String,
ability_desc: String,
level: Number,
tag: String
});
mongoose.model('Upgrade', upgradeSchema);
and the other
var mongoose = require( 'mongoose' );
var crypto = require('crypto');
var jwt = require('jsonwebtoken');
var userSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true
},
hero: {
level: Number,
name: String,
type: {
path: String,
heroType: String
},
upgrades: [{
type: mongoose.Schema.Types.ObjectId, ref: 'Upgrade'
}],
unspent_xp: Number,
total_xp: Number,
},
armyTotal: {
type: Number,
default: 0,
max: 5000
},
army:[{
foc_slot: String,
unit_name: String,
unit_cost: Number
}],
username: {
type: String,
required: true,
unique: true,
},
faction: String,
name: {
type: String,
required: true
},
hash: String,
salt: String,
roles: {
type: String,
default: 'player' }
});
And I'm trying to do this
module.exports.profileRead = function(req, res) {
User
.findById(req.payload._id)
.populate('hero.upgrades')
.exec(function (err, user) {
if (err){
console.log(err);
} else {
res.status(200).json(user);
console.log("success");
}
});
}
};
This is an example of a user
{
"_id" : ObjectId("57b4b56ea03757e12c94826e"),
"hash" : "76",
"salt" : "2",
"hero" : {
"upgrades" : [
"57b42773f7cac42a21fb03f9"
],
"total_xp" : 0,
"unspent_xp" : 0,
"type" : {
"heroType" : "Psyker",
"path" : ""
},
"name" : "Jon Doe"
},
"username" : "michaelzmyers",
"faction" : "Grey Knights",
"email" : "email#gmail.com",
"name" : "Michael Myers",
"roles" : "player",
"army" : [],
"armyTotal" : 625,
"__v" : 3
}
Now, I've tried an array of just the strings with ObjectId's in them, similar to the eample, and I've also tried using ObjectId("STRINGHERE") and no luck. They both return just an empty array. However, if i get rid of the populate call (or change the contents inside populate from hero.upgrades to just hero, or upgrades) then it just returns an array of strings. I feel like the problem is with populate and how I'm using it. HOWEVER, when I had just a single upgrade in my databse (the test upgrade), everything worked fine. Now nothing works. Any thoughts? I'd be happy to provide more code if needed.
I found that during my little research that it will work:
User
.findById(req.payload._id)
.populate({
path: 'hero.upgrades',
model: 'Upgrade'
})
.exec(function (err, user) {
if (err){
console.log(err);
} else {
res.status(200).json(user);
console.log("success");
}
});
}
It looks like when user is giving nested object notation i.e. hero.upgrades into populate method, Mongoose got problems with detecting referring model.