Populate to join two collection in Mongodb gets null data - node.js

I am trying to create an API in node to get nearby users with the same pet_type.
UPDATE: After Reading the first answer and as per the documentation. I update my user model and I Tried using populate but it doesn't get me all the pets data of the user.
User Model
var UserSchema = new mongoose.Schema({
password: String,
first_name: String,
email: String,
location: {
type: { type: String },
coordinates: []
},
pets: {
type: mongoose.Schema.ObjectId,
ref:'Pet', default:null
}
});
Pet Model
var PetSchema = new mongoose.Schema({
name: String,
pet_type: String,
breed: String,
owner_id: {type: mongoose.Schema.ObjectId, ref:'User', default:null},
});
Current Query
var lat = req.query.lat;
var lng = req.query.lng;
User.find({
location: {
$near: {
$geometry: {
type: "Point",
coordinates: [lng, lat]
},
$maxDistance: 10000
}
},
_id:{ $nin :req.decoded._id }}, function(err, users) {
if(err) {
res.json({success:false, message:"No listing found", error:err});
return;
} else {
res.json({success:true, message:"User found", data:users});
return;
}
}).populate('pets');
output
{
"success": true,
"message": "Pet data",
"data": [
{
"location": {
"type": "Point",
"coordinates": [
72.53808,
23.02622
]
},
"pets": null,
"_id": "5f32cb544f6b1a07acd75f48",
"phone": "1234567890",
"email": "qwewqe#qwe.com",
"first_name": "qwertt",
}
]
}

You can use the .populate() method

Solution :
You can use lookup (aggregation) in mongodb
link : https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/
link : https://www.isummation.com/blog/perform-inner-join-in-mongodb-using-lookup-aggregation-operator/

Related

How to resolve mongodb embedded document array of object for query in Graphql nodejs

I am new to Graphql, and I am trying to build an application while I am learning it. Actually I am trying to write a resolver to resolve the query on following schema.
Please tell me how could I resolve the referenced object resides in the array of feedformula: [feeditemSchema]
How to get the corresponding record from farmintake for feeding.
I don't know how clearly I explained my problem to you but i am sure that when you see the structure of schema you will understand my problem. Please help a I am unable to find.
When I query as below it workes.
query{
feeds{
_id
feedname
feedtime
feedformula{
feeding{
_id
}
qty
mesunit
description
remarks
}creator{
email
}
}
}
But when I use below query it raise an error:
query{
feeds{
_id
feedname
feedtime
feedformula{
feeding{
_id
name
}
qty
mesunit
description
remarks
}creator{
email
}
}
}
{
"errors": [
{
"message": "Cannot return null for non-nullable field
Farmintake.name.",
"locations": [
{
"line": 81,
"column": 8
}
],
"path": [
"feeds",
0,
"feedformula",
0,
"feeding",
"name"
]
},
{
"message": "Cannot return null for non-nullable field
Farmintake.name.",
"locations": [
{
"line": 81,
"column": 8
}
],
"path": [
"feeds",
0,
"feedformula",
1,
"feeding",
"name"
]
},
{
"message": "Cannot return null for non-nullable field
Farmintake.name.",
"locations": [
{
"line": 81,
"column": 8
}
],
"path": [
"feeds",
0,
"feedformula",
2,
"feeding",
"name"
]
}
],
"data": {
"feeds": [
{
"_id": "62b417a91797e5053c2d58a3",
"feedname": "Dana",
"feedtime": "Evening",
"feedformula": [
null,
null,
null
],
"creator": {
"email": "auzrimfa#gmail.com"
}
}
]
}
}
Following in Schema:
const feeditemSchema = new Schema({
feeding: {type: Schema.Types.ObjectId,ref: 'Farmintake'},
mesunit: {type: String},
qty: { type: Number },
description:{ type: String },
remarks:{ type: String }
});
const feedSchema = new Schema({
feedname:{ type: String },
feedtime:{ type: String },
feedformula:[ feeditemSchema ],
description:{ type: String },
creator: {type: Schema.Types.ObjectId,ref: 'User'}
},{ timestamps: true });
const farmintakeSchema = new Schema({
name:{type: String,required: true},
itmtype:{type: String,required: true},
madein:{type: String,required: true},
mesunit:{type: String},
usage:{type: String},
remarks:{type: String},
creator: {type: Schema.Types.ObjectId,ref: 'User'}
},{ timestamps: true}
);
module.exports = mongoose.model('Feed', feedSchema );
module.exports = mongoose.model('Farmintake',farmintakeSchema);
I tried following Resolver:
const transformFeed = feed =>{
return {
...feed._doc,
_id: feed.id,
feeding: farmintake.bind(this, feed.feedformula.feeding),
creator: user.bind(this,feed.creator)
};
};
const farmintake = async farmintakeId => {
try{
const farmintake = await Farmintake.findById(farmintakeId);
return {
...farmintake._doc,
_id:farmintake.id
};
}catch (err) {
throw err;
}
};
Instead of waiting for any reply, I struggled and found the solution by myself, below is my solution and it worked for me, experts feedbacks are requested to suggest for any improvement. My Graphql query is working now as below.
query{
feeds {
id
feedname
feedtime
feeditems {
farmintakes {
id
name
remarks
}
id
qty
mesunit
description
remarks
}
}
}
Query Result as I wanted:
{
"data": {
"feeds": [
{
"id": "62b417a91797e5053c2d58a3",
"feedname": "Dana",
"feedtime": "Evening",
"feeditems": [
{
"farmintakes": [
{
"id": "62323fd1109749e4a4f49a02",
"name": "Green Maiz Leaf (Makai Chara)",
"remarks": "Green sliced maiz for evening feed "
}
],
"id": "62b418ad1797e5053c2d58b2",
"qty": 6,
"mesunit": "Kg",
"description": "Testing Goat Feed",
"remarks": "1st Goat Feed"
},
{
"farmintakes": [
{
"id": "62323f06109749e4a4f499ff",
"name": "Millet (Jawar)",
"remarks": "Mix with morning feed of Goat"
}
],
"id": "62b4183b1797e5053c2d58ad",
"qty": 3.5,
"mesunit": "Kg",
"description": "Testing Goat Feed",
"remarks": "1st Goat Feed"
},
{
"farmintakes": [
{
"id": "62323e49109749e4a4f499fc",
"name": "Barley (Jau)",
"remarks": "Mix with morning feed of Goat"
}
],
"id": "62b417d11797e5053c2d58a8",
"qty": 2.5,
"mesunit": "Kg",
"description": "Testing Goat Feed",
"remarks": "1st Goat Feed"
}
]
}
]
}
}
After changes the Schema looking like as:
const feeditemSchema = new Schema({
feedId : {type: Schema.Types.ObjectId,ref: 'Feed'},
farmintakeId : {type: Schema.Types.ObjectId,ref: 'Farmintake'},
mesunit: {type: String},
qty: { type: Number },
description:{ type: String },
remarks:{ type: String }
});
const feedSchema = new Schema({
feedname:{ type: String },
feedtime:{ type: String },
feeditems:[ {type: Schema.Types.ObjectId,ref: 'Feeditem'}],
description:{ type: String },
creator: {type: Schema.Types.ObjectId,ref: 'User'}
},{ timestamps: true });
const farmintakeSchema = new Schema({
name:{type: String,required: true},
itmtype:{type: String,required: true},
madein:{type: String,required: true},
mesunit:{type: String},
usage:{type: String},
remarks:{type: String},
creator: {type: Schema.Types.ObjectId,ref: 'User'}
},{ timestamps: true}
);
module.exports = mongoose.model('Feed', feedSchema );
module.exports = mongoose.model('Feeditem',feeditemSchema);
module.exports = mongoose.model('Farmintake',farmintakeSchema);
TypeDefs:
type Feed {
id: ID!
feedname: String!
feedtime: String!
description: String
feeditems:[Feeditem!]!
}
type Farmintake {
id: ID!
name: String!
itmtype: String
madin: String
mesunit: String
usage: String
remarks: String
}
type Feeditem{
id: ID!
mesunit: String
qty: Float
description: String
remarks: String
farmintakes:[Farmintake!]!
}
Finally the Resolver:
Feed:{
feeditems:(parent,args,context) => {
const { Fid } = parent.id;
return feeditems.filter((feeditem) => feeditem.feedid === Fid );
},
},
Feeditem:{
farmintakes:(parent,args,context) => {
const { Fiid } = parent.farmintakeId;
return farmintakes.filter(( farmintake ) => farmintake.id ===
parent.farmintakeId );
}
}

mongoose add array of ids to a object

i'm trying to save a group with references to users and ranks. users and ranks is arrays of id's. However when i make a post request it seem to say that Group validation failed. What am i doing wrong with my references?
router.post('/:users/:game/:ranks/:age/:quantity/:usercount', function(req, res, next) {
var params = req.params;
var ranks = params.ranks.split(',');
var users = params.users.split(',');
var group = new Group({
users: users,
game: params.game,
ranks: ranks,
quantity: params.quantity,
age_above: params.age,
userCount: params.usercount
});
group.save(function(err, object) {
if (err) {
res.send(err);
} else {
User.findByIdAndUpdate(params.id, {$set:{group:object.id}}, {new: true}, function(err, doc){
if(err){
res.send(err);
} else {
res.send(doc);
}
}).populate('group');
}
});
});
User schema
var userSchema = new Schema({
_id: {
type: SchemaTypes.Long,
required: true,
unique: true,
index: {unique: true}
},
name: String,
birthday: Date,
country: String,
image: String,
group: { type: Schema.Types.ObjectId, ref: 'Group'},
games: [{
game: { type: Schema.Types.ObjectId, ref: 'Game'},
rank: { type: Schema.Types.ObjectId, ref: 'Ladder'},
userName: String
}],
friends: [{ type: Schema.Types.ObjectId, ref: 'User' }],
created_at: Date,
updated_at: Date
});
Error message
{
"message": "Group validation failed",
"name": "ValidationError",
"errors": {
"users": {
"message": "Cast to Array failed for value \"[ '10210697939629739' ]\" at path \"users\"",
"name": "CastError",
"kind": "Array",
"value": [
"10210697939629739"
],
"path": "users",
"reason": {
"message": "Cast to ObjectId failed for value \"10210697939629739\" at path \"users\"",
"name": "CastError",
"kind": "ObjectId",
"value": "10210697939629739",
"path": "users"
}
}
}
}

Mongoose find last messages grouping by two fields

I would like to put together a contact list based on the last messages sent or received from a contact.
To do this, I have to sort the last messages (both sent and received), add them and return the last one.
My response should be in the following format:
{
"success": true,
"chatlist": [
{
"id_user": "54228fe2c0df8d1120ed091b",
"lastMessage": {
"content": "message 6",
"date": "2016-11-09T02:54:41.687Z"
},
"unreadMessages": 3,
"name": "user 1"
},
{
"id_user": "12228fe2c0df8d11204g4d",
"lastMessage": {
"content": "message 3",
"date": "2016-11-09T02:54:23.329Z"
},
"unreadMessages": 2,
"name": "user 2"
},
{
"id_user": "58228fe2c0df8d1120e12sd",
"lastMessage": {
"content": "message 1",
"date": "2016-11-09T02:54:19.313Z"
},
"unreadMessages": 1,
"name": "user 3"
}
],
"pages": 2
}
My User Schema is:
var schema = new Schema({
name: {type: String, required: true},
email: {type: String, required: true, unique: true},
password: {type: String, required: true, select: false},
created_at: {type: Date, required: true, default: Date.now}
});
My Message Schema is:
var schema = new Schema({
content: {type: String, required: true},
type: {type: String, required: true, default: 'text'},
status: {type: String, default: 'not_read'},
created_at: {type: Date, default: Date.now},
read_at: {type: Date},
userFrom: {type: Schema.Types.ObjectId, ref: 'User', required: true},
userTo: {type: Schema.Types.ObjectId, ref: 'User', required: true}
});
I have tryed this:
var itensPerPage = 15;
var skip = page !== undefined ? page * itensPerPage : 0;
pages = Math.ceil(pages / itensPerPage);
Message
.aggregate([
{ '$sort': {
'created_at': -1
}},
{ "$skip": skip },
{ "$limit": itensPerPage },
{ '$match': {
$or: [
{ userFrom: user.id_user },
{ userTo: user.id_user }
]
}},
{ '$group': {
'_id': {
'userFrom': '$userFrom',
'userTo': '$userTo'
},
}
},
])
.exec(function (err, messages) {
res.send({"success": true, "chatlist": messages, "pages": pages});
});
How can I modify my query to get the desired response?
Thank you.
Try this
var itensPerPage = 15;
var skip = page !== undefined ? page * itensPerPage : 0;
pages = Math.ceil(pages / itensPerPage);
Message
.aggregate([
{ '$sort': {
'created_at': -1
}},
{ "$skip": skip },
{ "$limit": itensPerPage },
{ '$match': {
$or: [
{ userFrom: user.id_user },
{ userTo: user.id_user }
]
}},
{$unwind : $chatlist},
{$sort : {chatlist.date : -1}},
{$group : {_id : $_id},chatlist : {$push : $chatlist}},
])
.exec(function (err, messages) {
res.send({"success": true, "chatlist": messages, "pages": pages});
});

mongoose populate naming and structure

I have this "User" model where I store friends, this friends have some option
// Prepare schema
var schema = new db.Schema({
friends: [{
active : Boolean,
user : { type: db.Schema.ObjectId, ref: 'user' },
options : [{ type: db.Schema.ObjectId, ref: 'friend_option' }],
}],
image: { type: db.Schema.ObjectId, ref: 'file' },
password: {
type: String,
select: false
},
email: String,
name: String,
udid: [{
device: String,
udid: String
}],
created_on: Date
});
here I get one user with his friends
var friendQuery = User.find( {_id:req.params.id});
friendQuery.select('friends');
friendQuery.populate({ path: 'friends._id', model: 'user', select:'_id name' });
friendQuery.exec(
function(err, user) {
if (err)
res.send(err);
res.send(user);
});
my question is, why the result have a different name from my model? why friends->_id->_id instead go friends->user->_id?
friends": [
{
"_id": {
"_id": "58025664c154929d3207b232",
"name": "User 1"
},
"options": [
],
"active": false
},
{
"_id": {
"_id": "580257e1e3cd4fc7326fa97b",
"name": "User 2"
},
"options": [
],
"active": false
}
]
another option, this solution is good for an hypothetical big app?
The way You are using this in incorrect just make a simple change in your query
var friendQuery = User.find( {_id:req.params.id});
friendQuery.select('friends');
friendQuery.populate({ path: 'friends.user', model: 'user', select:'_id name' });
friendQuery.exec(
function(err, user) {
if (err)
res.send(err);
res.send(user);
});
as you are taking reference of your user in friends array simply replace _id from query to user that will definitely work for you

How can i use MONGODB aggregation framework with nested external document - Mongoose and NodeJS

I have these Mongoose schemes:
// User schema
exports.User = new Schema({
name: {
type: String,
required: true
},
home: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}]
});
// Post schema
exports.Post = new Schema({
likes: [{
type: Schema.Types.ObjectId,
ref: 'User'
}],
author: {
id: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
name: {
type: String,
required: true
},
shortId: String, // this is User id
},
created: {
type: Date,
default: Date.now,
required: true
}
});
// THE DATA IN THE DATABASE
// User
{"name" : "Mio Nome",
"home" : [
ObjectId("533af14b994c3647c5c97338")
]}
// Post
{ "author" : {
"id" : ObjectId("533af14b994c3647c5c97338"),
"name" : "AutoreDelPost"
},
"likes" : [
ObjectId("533af14b994c3647c5c97337"),
ObjectId("533af14b994c3647c5c97339"),
ObjectId("533af14b994c3647c5c97340")
]
}
And i want to get from users the posts in home field and count how many likehave one user
With this code i can show all posts in home whit populate, but i can't count likes.
req.db.User.find({
_id: req.user._id //req.user is my test user
}, {
home: 1
})
.limit(200)
.populate('home')
.exec(function (err) {
if (!err) {
return res.json(200)
}
return res.json(500, err)
});
// output
[
{
"_id": "533af0ae994c3647c5c97337",
"name" : "Mio Nome"
"home": [
{
"_id": "533b004e6bcb9105d535597e",
"author": {
"id": "533af14b994c3647c5c97338",
"name": "AutoreDelPost"
},
"likes": [] // i can't see like and i can't count they
}
]
I tryed to use aggregate, to count etc but i can't see the posts getting populated but their _id
req.db.User.aggregate({
$match: {
_id: req.user._id
}
}, {
$project: {
home: 1
}
}, {
$unwind: "$home"
}).exec(function (err, home) {
if (!err) {
return res.json(200, home)
}
return res.json(500, err)
});
// output
[
{
"_id": "533af0ae994c3647c5c97337",
"home": "533b004e6bcb9105d535597e"
},
{
"_id": "533af0ae994c3647c5c97337",
"home": "533b004e6bcb9105d5355980"
},
{
"_id": "533af0ae994c3647c5c97337",
"home": "533b004f6bcb9105d5355982"
},
{
"_id": "533af0ae994c3647c5c97337",
"home": "533b004f6bcb9105d5355984"
},
{
"_id": "533af0ae994c3647c5c97337",
"home": "533b00506bcb9105d5355986"
}
]
QUESTION: I want to get from users the posts in home field and count how many like a user has
Perhaps you can store your data more denormalized and add a counter field which is incremented on each new "like". See http://cookbook.mongodb.org/patterns/votes/. Something like:
update = {'$push': {'voters': user_id}, '$inc': {vote_count: 1}}

Resources