I am working on a NodeJS and a Mongoose Project and I have the following two schemas.
UserSchema.js
const UserSchema = mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
},
incharge: {
type: String,
enum: ['Adhihariharan', 'Anuja', 'Dhivya', 'Govind', 'Joann'],
required: true
},
)}
ContactSchema.js
const ContactSchema = new mongoose.Schema(
{
name: {
type: String,
trim: true,
required: [true, 'Please add a name'],
},
status: {
type: String,
enum: [
'Not Called',
'Wrong Number',
'Called/Declined',
'Called/Not Reachable',
'Called/Postponed',
'Called/Accepted',
'Emailed/Awaiting Response',
'Emailed/Declined',
'Emailed/Confirmed',
],
default: 'Not Called',
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true,
},
I am looking for a query which would give me a result which looks as the following:
[
{
_id: "5d7a514b5d2c12c7449be048",
name: "Benita",
incharge: "Joann",
statuses: [
{ status: "Not Called", count: 1 },
{ status: "Called/Accepted", count: 1 },
{ status: "Called/Declined", count: 1 },
{ status: "Called/Not Reachable", count: 1 },
]
},
{
_id: "5d7a514b5d2c12c7449be048",
name: "Febia",
incharge: "Dhivya",
statuses: [
{ "Not Called": 2 },
{ "Called/Postponed": 2 },
{ "Called/Declined": 3 },
{ "Called/Not Reachable": 1 },
]
},
... and so on
]
Here, the integer, is the number of times that status appears for a particular user and in charge is the manager in charge of the user. The _id mentioned is the ID of the user.
The _id, user, in charge belong to the UserSchema and the status belongs to the ContactSchema
I have tried the following query:
teams = await Contact.aggregate([
{
$group: {
_id: { user: '$user', status: '$status' },
count: { $sum: '$count' },
},
},
{
$lookup: {
from: 'members',
localField: '_id.user',
foreignField: '_id',
as: 'user',
},
},
{
$unwind: { path: '$user' },
},
{
$project: {
'user.name': 1,
'user.incharge': 1,
count: 1,
},
},
{
$sort: { 'user.incharge': 1, 'user.name': 1 },
},
]);
And the following was the output:
{
_id: { user: 5ff52b10fa237b001c93ef18, status: 'Not Called' },
count: 1,
user: { name: 'Benita', incharge: 'Joann' }
},
{
_id: { user: 5ff4ca05fa237b001c93ef15, status: 'Not Called' },
count: 2,
user: { name: 'Febia', incharge: 'Dhivya' }
},
{
_id: { user: 5ff4ca05fa237b001c93ef15, status: 'Called/Accepted' },
count: 4,
user: { name: 'Febia', incharge: 'Dhivya' }
}
Can someone please help me get the desired result?
Thanks in advance.
EDIT:
I did try #turivishal's approach but this is what I got:-
{
_id: 5ff52b10fa237b001c93ef18,
name: 'Sadana',
incharge: 'Joann',
statuses: [ [Object] ]
},
{
_id: 5ff4ca05fa237b001c93ef15,
name: 'Sudarshan B',
incharge: 'Joann',
statuses: [ [Object], [Object] ]
}
Can you please tell me how I can access the [Object] inside the status array in mongoose so that I can get a result as below...
{
_id: "5ff4ca05fa237b001c93ef15",
name: "Sudarshan B",
incharge: "Joann",
statuses: [
{ "Not Called": 2 },
{ "Called/Postponed": 2 },
]
},
You can try lookup with aggregation pipeline,
$lookup with contact pass _id in let,
$match user id condition
$group by status and get total count
$project to change name of the key and value
$addFields to convert statuses array to object using $arrayToObject
teams = await User.aggregate([
{
$lookup: {
from: "contact",
let: { user: "$_id" },
pipeline: [
{ $match: { $expr: { $eq: ["$$user", "$user"] } } },
{
$group: {
_id: "$status",
count: { $sum: 1 }
}
},
{
$project: {
_id: 0,
k: "$_id",
v: "$count"
}
}
],
as: "statuses"
}
},
{ $addFields: { statuses: { $arrayToObject: "$statuses" } } }
])
Playground
I'm new to the aggregate feature with mongodb and mongoose and have been having difficulty getting the desired results after passing data through my pipeline.
Below I have simplified the using a fictional example models
The scenario
I have 3 Models (Ship, Yatch, and Sailboat) that share the interface and extend from a base class. A 4th model, Captain, which has an array watercraftContexts that contain objects used to reference the types of watercrafts associated to each Captain.
Example Mongo Data/Schema setup
// Model Name: 'Captain', Collection Name: 'captains'
{
name: 'Jack Sparrow', // Captian name
license: 'SYS-123', // License Number
classes: ['sail', 'yatch', 'ship'], // Array of allowed operational vessel types
watercraftContexts: [
{
_id: ObjectId('xxxxxxxxxxxxxxx'), // Foreign Model ID
type: 'Sailboat', // Model Name
ref: 'sailboats'. // Collection Name
},
{
_id: ObjectId('xxxxxxxxxxxxxxx'), // Foreign Model ID
type: 'Yatch', // Model Name
ref: 'yatches'. // Collection Name
},
{
_id: ObjectId('xxxxxxxxxxxxxxx'), // Foreign Model ID
type: 'Ship', // Model Name
ref: 'ships'. // Collection Name
}
]
}
As you can see, the array of objects has been setup to use the mongoose.populate() method with the ref and _id fields and I've implemented a virtual getter watercrafts to hydrate with the populate() feature (code not posted).
A new field is created as watercrafts with an array of all objects from the 3 different associated collections when using mongoose.Model queries.
The Problem
I also need a way to aggregate against this data to produce similar results since the Model methods are not available in the aggregate pipline.
Here is the query generated from my programmatic mongo aggregate:
[ { '$match':
{ _id:
{ '$in':
[ ObjectId('5f77bc653887221a703415e1'),
ObjectId('5f77bc653887221a703415df'),
ObjectId('5f77bc653887221a703415e0'),
ObjectId('5f77bc653887221a703415e5') ] } } },
{ '$unwind': '$watercraftContexts' },
{ '$lookup':
{ from: 'ships',
localField: 'watercraftContexts._id',
foreignField: '_id',
as: 'watercrafts.ships' } },
{ '$unwind': '$watercraftContexts' },
{ '$lookup':
{ from: 'yatches',
localField: 'watercraftContexts._id',
foreignField: '_id',
as: 'watercrafts.yatches' } },
{ '$unwind': '$watercraftContexts' },
{ '$lookup':
{ from: 'sailboats',
localField: 'watercraftContexts._id',
foreignField: '_id',
as: 'watercrafts.sailboats' } },
{ '$group':
{ _id: '$_id',
watercrafts:
{ '$addToSet':
{ '$concatArrays':
[ '$watercrafts.ships',
'$watercrafts.yatches',
'$watercrafts.sailboats' ] } }
I'm constructing a mongoose aggregate like so:
const Captain = mongoose.model('Captain')
const aggregate = Captain.aggregrate()
// Dynamically create Aggregate Pipeline in another function
const captains = await Captain.find({})
const captainIds = captains.map(capt => capt._id)
// Match sub-set of documents (in actual project)
aggregate.match({ _id: { $in: captainIds } })
// Collection names to apply $lookup aggregate
const collectionNames = ['sailboats', 'yatches', 'ships']
// Unwind and Lookup for each polymorphic child class's collection
collectionNames.forEach(collection => {
// Separate watercraftContexts into individual records for lookup
aggregate.unwind('watercraftContexts')
// Inner Join collection data on record
aggregate.lookup({
from: collection,
localField: '$watercrafContexts._id',
foreignField: '_id',
// Object keyed by collection name with array of collection records
// to avoid overwrite of previous collection aggregate lookup
as: `watercrafts.${collection}`
})
})
// Re-group the records by Captain Object Id
const aggregateAssociationPaths = collectionNames.map(collection =>
// Mongo Path to each collection $lookup
`$watercrafts.${collection}`
)
// Re-assemble $unwind and $group by Captain's ObjectId
aggregate.group({
_id: '$_id',
$addToSet: {
//
$concatArrays: aggregateAssociationPaths
}
})
/*** !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ***
* *
* WHAT DO I DO NEXT TO GET ALL THE CAPTAIN DATA WITH THE AGGREGATED `watercrafts`
* *
*** !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! ***/
// Execute Aggregation
const captiansWithWatercraftsAssociations = await aggregate
My data is up to this point looks like this and the group isn't working with mongoose:
[ { _id: 5f77bc653887221a703415df,
watercrafts:
[ { _id: 5f77bc653887221a703415d3,
class: 'sail',
name: 'Gone with the Wind',
__v: 0 } ] },
{ _id: 5f77bc653887221a703415e0,
watercrafts:
[ { _id: 5f77bc653887221a703415d4,
class: 'yatch',
name: 'Liquid Gold',
__v: 0 } ] },
{ _id: 5f77bc653887221a703415e1,
watercrafts:
[ { _id: 5f77bc653887221a703415d5,
class: 'ship',
name: 'Jenny',
__v: 0 } ] },
{ _id: 5f77bc653887221a703415e5,
watercrafts:
[ { _id: 5f77bc653887221a703415dd,
class: 'yatch',
name: 'Audrey',
__v: 0 } ] },
{ _id: 5f77bc653887221a703415e5,
watercrafts:
[ { _id: 5f77bc653887221a703415dc,
class: 'sail',
name: 'Swell Shredder',
__v: 0 } ] },
{ _id: 5f77bc653887221a703415e5,
watercrafts:
[ { _id: 5f77bc653887221a703415de,
class: 'ship',
name: 'Jenny IV',
__v: 0 } ] } ]
Thanks for the support
This was a tricky one for someone new to MongoDb's aggregate. I'll break down my answer into steps to demonstrate to other attempting to aggregate an array with referencing multiple collections.
Step 1 - $match to filter on collection
The $match is accepts the same queries as db.collection.find({}) and returns an array of matching results in the case below, I select 4 specific records here
{ '$match':
{ _id:
{ '$in':
[
ObjectId('5f7bdb3eea134b5a5c976285'),
ObjectId('5f7bdb3eea134b5a5c976283'),
ObjectId('5f7bdb3eea134b5a5c976284'),
ObjectId('5f7bdb3eea134b5a5c976289')
]
}
}
}
$match Result
[
{ _id: ObjectId('5f7be0b37e2bdf5b19e4724d'),
name: 'CAPTAIN_SAIL',
classes: [ 'sail' ],
license: 'WC-1',
watercraftContexts:
[ { _id: ObjectId('5f7be0b37e2bdf5b19e47241'),
watercraftType: 'Sailboat',
ref: 'sailboats' } ],
__v: 0 },
{ _id: ObjectId('5f7be0b37e2bdf5b19e4724e'),
name: 'CAPTAIN_YATCH',
classes: [ 'yatch' ],
license: 'WC-2',
watercraftContexts:
[ { _id: ObjectId('5f7be0b37e2bdf5b19e47242'),
watercraftType: 'Yatch',
ref: 'yatches' } ],
__v: 0 },
{ _id: ObjectId('5f7be0b37e2bdf5b19e4724f'),
name: 'CAPTAIN_SHIP',
classes: [ 'ship' ],
license: 'WC-3',
watercraftContexts:
[ { _id: ObjectId('5f7be0b37e2bdf5b19e47243'),
watercraftType: 'Ship',
ref: 'ships' } ],
__v: 0 },
{ _id: ObjectId('5f7be0b37e2bdf5b19e47253'),
name: 'CAPTAIN_SAIL_YATCH_SHIP',
classes: [ 'sail', 'yatch', 'ship' ],
license: 'WC-7',
watercraftContexts:
[ { _id: ObjectId('5f7be0b37e2bdf5b19e4724a'),
watercraftType: 'Sailboat',
ref: 'sailboats' },
{ _id: ObjectId('5f7be0b37e2bdf5b19e4724b'),
watercraftType: 'Yatch',
ref: 'yatches' },
{ _id: ObjectId('5f7be0b37e2bdf5b19e4724c'),
watercraftType: 'Ship',
ref: 'ships' } ],
__v: 0 }
]
Step 2 - $unwind so we can iterate with $loopup
In this result set there is an array of objects with { _id: <ObjectId>, watercraftType: <ModelName> } to loop over the array and join each of these objects with there respective collection record, we have to break up the array into individual independent records. The $unwind feature will create a new data set for the next aggregate stage
{ '$unwind': '$watercraftContexts' },
$unwind Result
As you can see $unwind now creates a record with a single watercraftContext we are now set to use the $lookup
[ { _id: ObjectId('5f7be2231da37c5b5915bf9b'),
name: 'CAPTAIN_SAIL',
classes: [ 'sail' ],
license: 'WC-1',
watercraftContexts:
{ _id: ObjectId('5f7be2231da37c5b5915bf8f'),
watercraftType: 'Sailboat',
ref: 'sailboats' },
__v: 0 },
{ _id: ObjectId('5f7be2231da37c5b5915bf9c'),
name: 'CAPTAIN_YATCH',
classes: [ 'yatch' ],
license: 'WC-2',
watercraftContexts:
{ _id: ObjectId('5f7be2231da37c5b5915bf90'),
watercraftType: 'Yatch',
ref: 'yatches' },
__v: 0 },
{ _id: ObjectId('5f7be2231da37c5b5915bf9d'),
name: 'CAPTAIN_SHIP',
classes: [ 'ship' ],
license: 'WC-3',
watercraftContexts:
{ _id: ObjectId('5f7be2231da37c5b5915bf91'),
watercraftType: 'Ship',
ref: 'ships' },
__v: 0 },
{ _id: ObjectId('5f7be2231da37c5b5915bfa1'),
name: 'CAPTAIN_SAIL_YATCH_SHIP',
classes: [ 'sail', 'yatch', 'ship' ],
license: 'WC-7',
watercraftContexts:
{ _id: ObjectId('5f7be2231da37c5b5915bf98'),
watercraftType: 'Sailboat',
ref: 'sailboats' },
__v: 0 },
{ _id: ObjectId('5f7be2231da37c5b5915bfa1'),
name: 'CAPTAIN_SAIL_YATCH_SHIP',
classes: [ 'sail', 'yatch', 'ship' ],
license: 'WC-7',
watercraftContexts:
{ _id: ObjectId('5f7be2231da37c5b5915bf99'),
watercraftType: 'Yatch',
ref: 'yatches' },
__v: 0 },
{ _id: ObjectId('5f7be2231da37c5b5915bfa1'),
name: 'CAPTAIN_SAIL_YATCH_SHIP',
classes: [ 'sail', 'yatch', 'ship' ],
license: 'WC-7',
watercraftContexts:
{ _id: ObjectId('5f7be2231da37c5b5915bf9a'),
watercraftType: 'Ship',
ref: 'ships' },
__v: 0 } ]
Step 4 $lookup - Joins each record from the foreign collection
It is important to note that we must $unwind before calling $lookup for each different collection we need to join. Since we want to join multiple collections, we need to store the result in an objected keyed by the collection for later aggregation.
// Only performs $lookup on 'ships' collection
{ '$lookup':
{ from: 'ships', // Collection Name - Note: repeat for each collection
localField: 'watercraftContexts._id', // The field with id to link
foreignField: '_id', // The field on the foreign collection to match
as: 'watercrafts.ships' // The path where to store the lookup result
}
}
Step 5 - Repeat the $unwind and $lookup for the other joins
Repeat the above to steps for the additional joins, and key by the collection name. I have combined the aggregate stages to demonstrate the repetition.
{ '$unwind': '$watercraftContexts' },
{ '$lookup':
{ from: 'yatches',
localField: 'watercraftContexts._id',
foreignField: '_id',
as: 'watercrafts.yatches' } },
{ '$unwind': '$watercraftContexts' },
{ '$lookup':
{ from: 'sailboats',
localField: 'watercraftContexts._id',
foreignField: '_id',
as: 'watercrafts.sailboats' } }
Step 4 & 5 Results
If you look carefully, you notice that one of the Captain records exists 3 times with a different watercraftType. $lookup will only return records matching specific collection name. This is why why store them in an Object keyed by collectionName
[
{ _id: ObjectId('5f7be7145320a65b942bb450'),
name: 'CAPTAIN_SAIL',
classes: [ 'sail' ],
license: 'WC-1',
watercraftContexts:
{ _id: ObjectId('5f7be7145320a65b942bb444'),
watercraftType: 'Sailboat',
ref: 'sailboats' },
__v: 0,
watercrafts:
{ ships: [],
yatches: [],
sailboats:
[ { _id: ObjectId('5f7be7145320a65b942bb444'),
class: 'sail',
name: 'Gone with the Wind',
__v: 0 } ] } },
{ _id: ObjectId('5f7be7145320a65b942bb451'),
name: 'CAPTAIN_YATCH',
classes: [ 'yatch' ],
license: 'WC-2',
watercraftContexts:
{ _id: ObjectId('5f7be7145320a65b942bb445'),
watercraftType: 'Yatch',
ref: 'yatches' },
__v: 0,
watercrafts:
{ ships: [],
yatches:
[ { _id: ObjectId('5f7be7145320a65b942bb445'),
class: 'yatch',
name: 'Liquid Gold',
__v: 0 } ],
sailboats: [] } },
{ _id: ObjectId('5f7be7145320a65b942bb452'),
name: 'CAPTAIN_SHIP',
classes: [ 'ship' ],
license: 'WC-3',
watercraftContexts:
{ _id: ObjectId('5f7be7145320a65b942bb446'),
watercraftType: 'Ship',
ref: 'ships' },
__v: 0,
watercrafts:
{ ships:
[ { _id: ObjectId('5f7be7145320a65b942bb446'),
class: 'ship',
name: 'Jenny',
__v: 0 } ],
yatches: [],
sailboats: [] } },
{ _id: ObjectId('5f7be7145320a65b942bb456'),
name: 'CAPTAIN_SAIL_YATCH_SHIP',
classes: [ 'sail', 'yatch', 'ship' ],
license: 'WC-7',
watercraftContexts:
{ _id: ObjectId('5f7be7145320a65b942bb44d'),
watercraftType: 'Sailboat',
ref: 'sailboats' },
__v: 0,
watercrafts:
{ ships: [],
yatches: [],
sailboats:
[ { _id: ObjectId('5f7be7145320a65b942bb44d'),
class: 'sail',
name: 'Swell Shredder',
__v: 0 } ] } },
{ _id: ObjectId('5f7be7145320a65b942bb456'),
name: 'CAPTAIN_SAIL_YATCH_SHIP',
classes: [ 'sail', 'yatch', 'ship' ],
license: 'WC-7',
watercraftContexts:
{ _id: ObjectId('5f7be7145320a65b942bb44e'),
watercraftType: 'Yatch',
ref: 'yatches' },
__v: 0,
watercrafts:
{ ships: [],
yatches:
[ { _id: ObjectId('5f7be7145320a65b942bb44e'),
class: 'yatch',
name: 'Audrey',
__v: 0 } ],
sailboats: [] } },
{ _id: ObjectId('5f7be7145320a65b942bb456'),
name: 'CAPTAIN_SAIL_YATCH_SHIP',
classes: [ 'sail', 'yatch', 'ship' ],
license: 'WC-7',
watercraftContexts:
{ _id: ObjectId('5f7be7145320a65b942bb44f'),
watercraftType: 'Ship',
ref: 'ships' },
__v: 0,
watercrafts:
{ ships:
[ { _id: ObjectId('5f7be7145320a65b942bb44f'),
class: 'ship',
name: 'Jenny IV',
__v: 0 } ],
yatches: [],
sailboats: [] } } ]
Step 6 $project - Use project to flatten the Object Map of joins
We can use project to select all the existing data and flatten the Object Map of join results into a single Array.
{ '$project':
// keys with the value 'true' will be included
{ name: true,
license: true,
classes: true,
_id: true,
watercraftContexts: true,
__v: true,
watercrafts: // Re-assigns value of watercrafts
{ '$setUnion': // Accepts an array of arrays to flatten
[
'$watercrafts.ships',
'$watercrafts.yatches',
'$watercrafts.sailboats'
]
}
}
}
$project Result
The results of the above $project will replace the watercrafts object with an flatten array of watercrafts, but it is important to note that there are still duplicate records of Captain where matching many different lookups. We will re-piece them together in the next step.
[ { _id: ObjectId('5f7bea8d79dfe25bf3cb9695'),
name: 'CAPTAIN_SAIL',
classes: [ 'sail' ],
license: 'WC-1',
watercraftContexts:
{ _id: ObjectId('5f7bea8d79dfe25bf3cb9689'),
watercraftType: 'Sailboat',
ref: 'sailboats' },
__v: 0,
watercrafts:
[ { _id: ObjectId('5f7bea8d79dfe25bf3cb9689'),
class: 'sail',
name: 'Gone with the Wind',
__v: 0 } ] },
{ _id: ObjectId('5f7bea8d79dfe25bf3cb9696'),
name: 'CAPTAIN_YATCH',
classes: [ 'yatch' ],
license: 'WC-2',
watercraftContexts:
{ _id: ObjectId('5f7bea8d79dfe25bf3cb968a'),
watercraftType: 'Yatch',
ref: 'yatches' },
__v: 0,
watercrafts:
[ { _id: ObjectId('5f7bea8d79dfe25bf3cb968a'),
class: 'yatch',
name: 'Liquid Gold',
__v: 0 } ] },
{ _id: ObjectId('5f7bea8d79dfe25bf3cb9697'),
name: 'CAPTAIN_SHIP',
classes: [ 'ship' ],
license: 'WC-3',
watercraftContexts:
{ _id: ObjectId('5f7bea8d79dfe25bf3cb968b'),
watercraftType: 'Ship',
ref: 'ships' },
__v: 0,
watercrafts:
[ { _id: ObjectId('5f7bea8d79dfe25bf3cb968b'),
class: 'ship',
name: 'Jenny',
__v: 0 } ] },
{ _id: ObjectId('5f7bea8d79dfe25bf3cb969b'),
name: 'CAPTAIN_SAIL_YATCH_SHIP',
classes: [ 'sail', 'yatch', 'ship' ],
license: 'WC-7',
watercraftContexts:
{ _id: ObjectId('5f7bea8d79dfe25bf3cb9692'),
watercraftType: 'Sailboat',
ref: 'sailboats' },
__v: 0,
watercrafts:
[ { _id: ObjectId('5f7bea8d79dfe25bf3cb9692'),
class: 'sail',
name: 'Swell Shredder',
__v: 0 } ] },
{ _id: ObjectId('5f7bea8d79dfe25bf3cb969b'),
name: 'CAPTAIN_SAIL_YATCH_SHIP',
classes: [ 'sail', 'yatch', 'ship' ],
license: 'WC-7',
watercraftContexts:
{ _id: ObjectId('5f7bea8d79dfe25bf3cb9693'),
watercraftType: 'Yatch',
ref: 'yatches' },
__v: 0,
watercrafts:
[ { _id: ObjectId('5f7bea8d79dfe25bf3cb9693'),
class: 'yatch',
name: 'Audrey',
__v: 0 } ] },
{ _id: ObjectId('5f7bea8d79dfe25bf3cb969b'),
name: 'CAPTAIN_SAIL_YATCH_SHIP',
classes: [ 'sail', 'yatch', 'ship' ],
license: 'WC-7',
watercraftContexts:
{ _id: ObjectId('5f7bea8d79dfe25bf3cb9694'),
watercraftType: 'Ship',
ref: 'ships' },
__v: 0,
watercrafts:
[ { _id: ObjectId('5f7bea8d79dfe25bf3cb9694'),
class: 'ship',
name: 'Jenny IV',
__v: 0 } ] } ]
Step 7 $unwind and $group
We $unwind so that we can now group all watercrafts belonging to the same Captain. We also have to use $mergeObjects to temporarily store the additional data from the Captain collection under a new temporary variable to prepare for the final stages.
{ '$unwind': '$watercrafts' },
{ '$group':
{ _id: '$_id',
data:
{ '$mergeObjects':
{ name: '$name',
license: '$license',
classes: '$classes',
watercraftContexts: '$watercraftContexts',
__v: '$__v' } },
watercrafts: { '$push': '$watercrafts' } } }
$unwind and $group Result
Now we're really getting somewhere. We have reduced our transformation to our initial 4 Captains and flattened our joins into a single array.
[ { _id: ObjectId('5f7bed5e271dd95c306c25a4'),
data:
{ name: 'CAPTAIN_SHIP',
license: 'WC-3',
classes: [ 'ship' ],
watercraftContexts:
{ _id: ObjectId('5f7bed5e271dd95c306c2598'),
watercraftType: 'Ship',
ref: 'ships' },
__v: 0 },
watercrafts:
[ { _id: ObjectId('5f7bed5e271dd95c306c2598'),
class: 'ship',
name: 'Jenny',
__v: 0 } ] },
{ _id: ObjectId('5f7bed5e271dd95c306c25a8'),
data:
{ name: 'CAPTAIN_SAIL_YATCH_SHIP',
license: 'WC-7',
classes: [ 'sail', 'yatch', 'ship' ],
watercraftContexts:
{ _id: ObjectId('5f7bed5e271dd95c306c25a1'),
watercraftType: 'Ship',
ref: 'ships' },
__v: 0 },
watercrafts:
[ { _id: ObjectId('5f7bed5e271dd95c306c259f'),
class: 'sail',
name: 'Swell Shredder',
__v: 0 },
{ _id: ObjectId('5f7bed5e271dd95c306c25a0'),
class: 'yatch',
name: 'Audrey',
__v: 0 },
{ _id: ObjectId('5f7bed5e271dd95c306c25a1'),
class: 'ship',
name: 'Jenny IV',
__v: 0 } ] },
{ _id: ObjectId('5f7bed5e271dd95c306c25a2'),
data:
{ name: 'CAPTAIN_SAIL',
license: 'WC-1',
classes: [ 'sail' ],
watercraftContexts:
{ _id: Object('5f7bed5e271dd95c306c2596'),
watercraftType: 'Sailboat',
ref: 'sailboats' },
__v: 0 },
watercrafts:
[ { _id: ObjectId('5f7bed5e271dd95c306c2596'),
class: 'sail',
name: 'Gone with the Wind',
__v: 0 } ] },
{ _id: ObjectId('5f7bed5e271dd95c306c25a3'),
data:
{ name: 'CAPTAIN_YATCH',
license: 'WC-2',
classes: [ 'yatch' ],
watercraftContexts:
{ _id: ObjectId('5f7bed5e271dd95c306c2597'),
watercraftType: 'Yatch',
ref: 'yatches' },
__v: 0 },
watercrafts:
[ { _id: ObjectId('5f7bed5e271dd95c306c2597'),
class: 'yatch',
name: 'Liquid Gold',
__v: 0 } ] } ]
Step 8 $replaceRoot and $project
All we have left is to merge our data into the root of each record and remove the temporary variable data
// Merges 'data' into the root of each record
{ '$replaceRoot': { newRoot: { '$mergeObjects': [ '$data', '$$ROOT' ] } } },
// Use $project to remove data (include only the fields we want)
{ '$project':
{ name: true,
license: true,
classes: true,
_id: true,
watercraftContexts: true,
__v: true,
watercrafts: true }
}
$replaceRoot & $project Result
Now we have the result we set out for...A Captain with an array of mixed associated types watercrafts
[
{ name: 'CAPTAIN_SAIL_YATCH_SHIP',
license: 'WC-7',
classes: [ 'sail', 'yatch', 'ship' ],
watercraftContexts:
{ _id: ObjectId('5f7bf3b3680b375ca1755ea6'),
watercraftType: 'Ship',
ref: 'ships' },
__v: 0,
_id: ObjectId('5f7bf3b3680b375ca1755ead'),
watercrafts:
[ { _id: ObjectId('5f7bf3b3680b375ca1755ea4'),
class: 'sail',
name: 'Swell Shredder',
__v: 0 },
{ _id: ObjectId('5f7bf3b3680b375ca1755ea5'),
class: 'yatch',
name: 'Audrey',
__v: 0 },
{ _id: ObjectId('5f7bf3b3680b375ca1755ea6'),
class: 'ship',
name: 'Jenny IV',
__v: 0 } ] },
{ name: 'CAPTAIN_SAIL',
license: 'WC-1',
classes: [ 'sail' ],
watercraftContexts:
{ _id: ObjectId('5f7bf3b3680b375ca1755e9b'),
watercraftType: 'Sailboat',
ref: 'sailboats' },
__v: 0,
_id: ObjectId('5f7bf3b3680b375ca1755ea7'),
watercrafts:
[ { _id: ObjectId('5f7bf3b3680b375ca1755e9b'),
class: 'sail',
name: 'Gone with the Wind',
__v: 0 } ] },
{ name: 'CAPTAIN_YATCH',
license: 'WC-2',
classes: [ 'yatch' ],
watercraftContexts:
{ _id: ObjectId('5f7bf3b3680b375ca1755e9c'),
watercraftType: 'Yatch',
ref: 'yatches' },
__v: 0,
_id: ObjectId('5f7bf3b3680b375ca1755ea8'),
watercrafts:
[ { _id: ObjectId('5f7bf3b3680b375ca1755e9c'),
class: 'yatch',
name: 'Liquid Gold',
__v: 0 } ] },
{ name: 'CAPTAIN_SHIP',
license: 'WC-3',
classes: [ 'ship' ],
watercraftContexts:
{ _id: ObjectId('5f7bf3b3680b375ca1755e9d'),
watercraftType: 'Ship',
ref: 'ships' },
__v: 0,
_id: ObjectId('5f7bf3b3680b375ca1755ea9'),
watercrafts:
[ { _id: ObjectId('5f7bf3b3680b375ca1755e9d'),
class: 'ship',
name: 'Jenny',
__v: 0 } ] } ]
And there you have it...only took 2 days to figure this out. I hope it saves you some time if you're attempting a similar aggregate association. Happy coding!
Final Pipeline
[
{ '$match':
{ _id:
{ '$in':
[ ObjectId('5f7bf3b3680b375ca1755ea9'),
ObjectId('5f7bf3b3680b375ca1755ea7'),
ObjectId('5f7bf3b3680b375ca1755ea8'),
ObjectId('5f7bf3b3680b375ca1755ead')
]
}
}
},
{ '$unwind': '$watercraftContexts' },
{ '$lookup':
{ from: 'ships',
localField: 'watercraftContexts._id',
foreignField: '_id',
as: 'watercrafts.ships' } },
{ '$unwind': '$watercraftContexts' },
{ '$lookup':
{ from: 'yatches',
localField: 'watercraftContexts._id',
foreignField: '_id',
as: 'watercrafts.yatches' } },
{ '$unwind': '$watercraftContexts' },
{ '$lookup':
{ from: 'sailboats',
localField: 'watercraftContexts._id',
foreignField: '_id',
as: 'watercrafts.sailboats' } },
{ '$project':
{ name: true,
license: true,
classes: true,
_id: true,
watercraftContexts: true,
__v: true,
watercrafts:
{ '$setUnion':
[ '$watercrafts.ships',
'$watercrafts.yatches',
'$watercrafts.sailboats' ] } } },
{ '$unwind': '$watercrafts' },
{ '$group':
{ _id: '$_id',
data:
{ '$mergeObjects':
{ name: '$name',
license: '$license',
classes: '$classes',
watercraftContexts: '$watercraftContexts',
__v: '$__v' } },
watercrafts: { '$push': '$watercrafts' } } },
{ '$replaceRoot': { newRoot: { '$mergeObjects': [ '$data', '$$ROOT' ] } } },
{ '$project':
{ name: true,
license: true,
classes: true,
_id: true,
watercraftContexts: true,
__v: true,
watercrafts: true } }
]
I would like to create the data for blog post with list of posts, its associated comments with replies along with user details.
I tried nested populate but could do for only one level. Like I get the comments object and in this I need to populate replies and userId. Inside replies I need to populate userId and repliesCount. In this, only the populate that is given at last returns the populated data other fields list only the Ids.
posts.model.js:
const mongoose = require('mongoose')
var ObjectId = mongoose.Schema.Types.ObjectId
let PostsSchema = new mongoose.Schema({
userId: {
type: ObjectId,
ref: 'User'
},
celebrityId: {
type: ObjectId,
ref: 'CelebrityDetails'
},
type: {
type: Number,
default: 1
},
isForwarded: {
type: Number,
default: 0
},
originalPostId: {
type: ObjectId,
default: null
},
title: {
type: String
},
description: {
type: String
},
status: {
type: Number,
default: 1
}
}, {
timestamps: true,
collection: 'fan_posts'
})
PostsSchema.virtual('isMyPost', {
ref: 'User',
localField: 'userId',
foreignField: '_id',
count: true
})
PostsSchema.virtual('comments', {
ref: 'PostComment',
localField: '_id',
foreignField: 'postId'
})
PostsSchema.virtual('commentCount', {
ref: 'PostComment',
localField: '_id',
foreignField: 'postId',
count: true
})
PostsSchema.set('toObject', { virtuals: true })
PostsSchema.set('toJSON', { virtuals: true })
const Posts = mongoose.model('Posts', PostsSchema)
Posts.consts = {
STATUS_INACTIVE: 0,
STATUS_ACTIVE: 1,
STATUS_DELETED: 2,
IS_FORWARDED: 1,
TYPE_TEXT: 1,
TYPE_IMAGE: 2,
TYPE_VIDEO: 3,
TYPE_ASK_TEXT: 4,
TYPE_ASK_IMAGE: 5,
TYPE_RATING: 6
}
module.exports = Posts
comments.model.js
const mongoose = require('mongoose')
var ObjectId = mongoose.Schema.Types.ObjectId
let PostCommentSchema = new mongoose.Schema({
userId: {
type: ObjectId,
ref: 'User'
},
postId: {
type: ObjectId,
ref: 'FanPosts'
},
comment: {
type: String
},
isReply: {
type: Number,
default: 0
},
parentCommentId: {
type: ObjectId,
ref: 'PostComment'
}
}, {
timestamps: true,
collection: 'post_comment'
})
PostCommentSchema.set('toObject', { virtuals: true })
PostCommentSchema.set('toJSON', { virtuals: true })
PostCommentSchema.virtual('replies', {
ref: 'PostComment',
localField: '_id',
foreignField: 'parentCommentId'
})
PostCommentSchema.virtual('repliesCount', {
ref: 'PostComment',
localField: '_id',
foreignField: 'parentCommentId',
count: true,
justOne: true
})
const PostComment = mongoose.model('PostComment', PostCommentSchema)
PostComment.consts = {
TYPE_NOT_REPLY: 0,
TYPE_REPLY: 1
}
module.exports = PostComment
Query:
Posts.find({celebrityId: celebrityId, status: Posts.consts.STATUS_ACTIVE})
.populate({ path: 'userId', select: 'fmId fullName' })
.populate({ path: 'isMyPost', match:{_id: userId} })
.populate({ path: 'comments', match: {isReply: PostComment.consts['TYPE_NOT_REPLY']}, populate: {path: 'userId', select: 'fmId fullName'}, populate: {path: 'replies', match: {isReply: PostComment.consts['TYPE_REPLY']}, populate: {path: 'userId', select: 'fmId fullName'} } })
.populate({ path: 'commentCount'})
.exec(function(err, posts){
if (err) return res.send({status: status.codes.http['serverError'], message: err})
return res.send({status: status.codes.http['success'], posts: posts})
})
Result:
{
"status": 200,
"posts": [
{
"type": 1,
"isForwarded": 0,
"originalPostId": null,
"status": 1,
"_id": "5d2b16519788076fafe7700c",
"celebrityId": "5d167ca099a55c2d2494dcf8",
"post": "hi how are you",
"userId": {
"_id": "5d167a397213b127aafb48f3",
"fmId": "FM499KNWDL",
"fullName": "Mohideen Abubucker"
},
"createdAt": "2019-07-14T11:47:29.863Z",
"updatedAt": "2019-07-14T11:47:29.863Z",
"__v": 0,
"isMyPost": 1,
"comments": [
{
"isReply": 0,
"_id": "5d33721a12aba934e6520f2d",
"userId": "5d167a397213b127aafb48f3",
"postId": "5d2b16519788076fafe7700c",
"comment": "comment 1",
"createdAt": "2019-07-20T19:57:14.747Z",
"updatedAt": "2019-07-20T19:57:14.747Z",
"__v": 0,
"replies": [
{
"isReply": 1,
"_id": "5d33724e12aba934e6520f2e",
"userId": {
"_id": "5d167a397213b127aafb48f3",
"fmId": "FM499KNWDL",
"fullName": "Mohideen Abubucker"
},
"postId": "5d2b16519788076fafe7700c",
"comment": "comment 1",
"parentCommentId": "5d33721a12aba934e6520f2d",
"createdAt": "2019-07-20T19:58:06.286Z",
"updatedAt": "2019-07-20T19:58:06.286Z",
"__v": 0,
"id": "5d33724e12aba934e6520f2e"
}
],
"id": "5d33721a12aba934e6520f2d"
}
],
"commentCount": 2,
"id": "5d2b16519788076fafe7700c"
},
]
}
I need to populate userId inside comments object and add repliesCount to the replies object.
The issue is, I can populate only one column. In the query, if you see I would have given populate for both userId and replies. Since replies being the last I am getting the replies data.
I don't know how to populate both replies and userId
Expected:
{
"status": 200,
"posts": [
{
"type": 1,
"isForwarded": 0,
"originalPostId": null,
"status": 1,
"_id": "5d2b16519788076fafe7700c",
"celebrityId": "5d167ca099a55c2d2494dcf8",
"post": "hi how are you",
"userId": {
"_id": "5d167a397213b127aafb48f3",
"fmId": "FM499KNWDL",
"fullName": "Mohideen Abubucker"
},
"createdAt": "2019-07-14T11:47:29.863Z",
"updatedAt": "2019-07-14T11:47:29.863Z",
"__v": 0,
"isMyPost": 1,
"comments": [
{
"isReply": 0,
"_id": "5d33721a12aba934e6520f2d",
"userId": {
"_id": "5d167a397213b127aafb48f3",
"fmId": "FM499KNWDL",
"fullName": "Mohideen Abubucker"
},
"postId": "5d2b16519788076fafe7700c",
"comment": "comment 1",
"createdAt": "2019-07-20T19:57:14.747Z",
"updatedAt": "2019-07-20T19:57:14.747Z",
"__v": 0,
"replies": [
{
"isReply": 1,
"_id": "5d33724e12aba934e6520f2e",
"userId": {
"_id": "5d167a397213b127aafb48f3",
"fmId": "FM499KNWDL",
"fullName": "Mohideen Abubucker"
},
"postId": "5d2b16519788076fafe7700c",
"comment": "comment 1",
"parentCommentId": "5d33721a12aba934e6520f2d",
"createdAt": "2019-07-20T19:58:06.286Z",
"updatedAt": "2019-07-20T19:58:06.286Z",
"__v": 0,
"id": "5d33724e12aba934e6520f2e",
"repliesCount": 1
}
],
"id": "5d33721a12aba934e6520f2d"
}
],
"commentCount": 2,
"id": "5d2b16519788076fafe7700c"
},
]
}
The problem here:
.populate({ path: 'comments', match: {isReply: PostComment.consts['TYPE_NOT_REPLY']}, populate: {path: 'userId', select: 'fmId fullName'}, populate: {path: 'replies', match: {isReply: PostComment.consts['TYPE_REPLY']}, populate: {path: 'userId', select: 'fmId fullName'} } })
Your populate parameters:
{
path: 'comments',
match: {
isReply: PostComment.consts['TYPE_NOT_REPLY']
},
populate: {
path: 'userId',
select: 'fmId fullName'
},
populate: {
path: 'replies',
match: {
isReply: PostComment.consts['TYPE_REPLY']
},
populate: {
path: 'userId',
select: 'fmId fullName'
}
}
}
Object literals are a flavor of key value map thus you cannot have multiple keys of the same value within a single level of the object.
You have 2 keys with the value "populate" at a single level of the object and it looks like only the last one is persisted in the object.
You can see that here: https://jsfiddle.net/32oe6w8y/1/
Check the mongoose documentation, I'm sure they have a mechanism to deal with this (populate may take an array.)
EDT:
Based on this you can pass an array into populate:
https://stackoverflow.com/a/21100156/2004999
That may be the solution to your problem.
Fixed problematic populate parameters:
{
path: 'comments',
match: {
isReply: PostComment.consts['TYPE_NOT_REPLY']
},
populate: [{
path: 'userId',
select: 'fmId fullName'
},{
path: 'replies',
match: {
isReply: PostComment.consts['TYPE_REPLY']
},
populate: {
path: 'userId',
select: 'fmId fullName'
}
}]
}
I am writing a request for statistics from the db. Here's the code:
Stats.aggregate([
{
$group: {
_id: {
'advertiser': '$advertiser',
'offer': '$offer'
},
stats: {
$push: {
'date': '$date',
'spent': '$spent',
'revenue': '$revenue'
}
}
}
},
{
$group: {
_id: '$_id.advertiser',
offers: {
$push: {
_id: '$_id.offer',
stats: '$stats'
}
}
}
}], callback);
I want that the advertiser had all his offers, and inside the offers there was statistics on the days along with spent and revenue. The problem is that statistics by day can be more than one and I get this answer:
stats […]
0 {…}
date 2018-01-30T22:00:00.000Z
spent 100
revenue 200
1 {…}
date 2018-01-30T22:00:00.000Z
spent 20
revenue 20
But, I need to have the same days folded into one and spent and revenue added
Also, I want to add offer info from offers collection into result offers, like I do with advertisers. Maybe someone know how to do this. Thank you
Stats in db:
{
"_id": {
"$oid": "5a4f873d381727000404d171"
},
"advertiser": {
"$oid": "5a4f74619f0251000438fe4a"
},
"offer": {
"$oid": "5a4f748c9f0251000438fe4b"
},
"spent": 415.19,
"revenue": 780.92,
"date": "2018-01-02T22:00:00.000Z",
"user": {
"$oid": "5a4f74259f0251000438fe40"
},
"__v": 0
}
Stats Schema
const StatsSchema = mongoose.Schema({
advertiser: {
type: ObjectId,
ref: 'Advertiser',
required: true
},
offer: {
type: ObjectId,
ref: 'Offer',
required: true
},
spent: {
type: Number,
required: true
},
revenue: {
type: Number,
required: true
},
date: {
type: String,
required: true
},
user: {
type: ObjectId,
ref: 'User',
required: true
}});
You can first project the day, month and year then group accordingly as below. This is a relatively long query you could add in some optimisations based on your workflow.
db.createCollection('statistics');
db.statistics.insertMany([
{advertiser: "1", offer: "1", date: "2018-01-30T22:00:00.000Z", spent: 10, revenue: 20},
{advertiser: "1", offer: "1", date: "2018-01-30T21:00:00.000Z", spent: 20, revenue: 20},
{advertiser: "2", offer: "2", date: "2018-01-30T22:00:00.000Z", spent: 10, revenue: 20},
{advertiser: "2", offer: "2", date: "2018-01-30T21:00:00.000Z", spent: 1000, revenue: 2000},
{advertiser: "2", offer: "2", date: "2018-01-31T22:00:00.000Z", spent: 25, revenue: 50}
])
transform_date = {
$project: {
advertiser: 1,
offer: 1,
date: { $dateFromString: { dateString: { $arrayElemAt: [ {$split: ["$date", "Z"]}, 0 ] }, timezone: 'UTC' } },
spent: 1,
revenue: 1
}
}
project_year_month_day = {
$project: {
advertiser: 1,
offer: 1,
date: 1,
spent: 1,
revenue: 1,
year: { $year: "$date" },
month: { $month: "$date" },
day: { $dayOfMonth: "$date" }
}
}
group_by_date_advertiser_offer_and_sum = {
$group: {
_id: {
advertiser: "$advertiser",
offer: "$offer",
day: "$day",
month: "$month",
year: "$year"
},
spent: { $sum: "$spent" },
revenue: { $sum: "$revenue" },
dates: { $push: "$date" }
}
}
group_advertiser_offer_and_push = {
$group: {
_id: {
advertiser: "$_id.advertiser",
offer: "$_id.offer"
},
stats: {
$push: {
dates: "$dates",
spent: "$spent",
revenue: "$revenue"
}
}
}
}
group_advertiser_and_push = {
$group: {
_id: "$_id.advertiser",
offers: {
$push: {
_id: "$_id.offer",
stats: "$stats"
}
}
}
}
db.statistics.aggregate([
transform_date,
project_year_month_day,
group_by_date_advertiser_offer_and_sum,
group_advertiser_offer_and_push,
group_advertiser_and_push
])
OUTPUT
[{
"_id": "2",
"offers": [{
"_id": "2",
"stats": [{
"dates": [ISODate("2018-01-31T22:00:00Z")],
"spent": 25,
"revenue": 50
}, {
"dates": [ISODate("2018-01-30T22:00:00Z"), ISODate("2018-01-30T21:00:00Z")],
"spent": 1010,
"revenue": 2020
}]
}]
} {
"_id": "1",
"offers": [{
"_id": "1",
"stats": [{
"dates": [ISODate("2018-01-30T22:00:00Z"), ISODate("2018-01-30T21:00:00Z")],
"spent": 30,
"revenue": 40
}]
}]
}]
I am trying to do groupBy with a population operation in MongoDB with Mongoose and Node.js.
I have one separate collection which is holding client_id and project_id. One client can have many projects, that's why I have created another collection for better understanding:
var CustomerHasProjectSchema = new mongoose.Schema({
client_id: {type: mongoose.Schema.Types.ObjectId, ref: 'Customer'},
project_id: {type: mongoose.Schema.Types.ObjectId, ref: 'Project'},
txn_date: {
type: Date,
default: Date.now
},
status: {
type: Number,
default : 1
}
});
I am trying to do something like
groupby(client_id)
and populate(user_id)
The exact query I have tried is
return customerHasProject.aggregate(
[
{
$match: {
$and: [
{ client_id: id }
]
}
},
{
$group : {
_id : "$client_id",
count: {
$sum: 1
}
}
},
{
$lookup: {
from: "project",
localField: "project_id",
foreignField: "_id",
as: "projects"
}
},
{
$unwind : {
"path" : "$project_id"
}
}
]
I tried this with help from another StackOverflow post.
I have also tried:
return customer2.default.find({'_id' : req.params.id}).exec().then(response=>{
project2.default.find({'client_id' : response[0]._id}).populate('client_id').then(customerWithProject=>{
res.json(customerWithProject)
})
})
But this is returning client detail in every project with a result like:
[
{
_id: "5883b189872c7d00042e1630",
projectTitle: "Marketing Analytics",
client_id: {
_id: "58833d9e1079ba000445a3a1",
clientName: "Anil",
companyName: "fgjhtrjhed",
website: "jhtryjh",
clientEmail: "frhyrt#tyhtr.dfkgjoiu",
mobileNumber: 549821654,
companyLocation: "New Delhi, Delhi, India",
clientCategory: "Freelancer",
__v: 0,
status: 1,
txn_date: "2017-01-21T10:53:18.360Z"
},
start_date: "2017-01-21T18:30:00.000Z",
end_date: "2017-01-30T18:30:00.000Z",
domain: "Back to the Future",
proposed_amount: 138000,
expectedFinalAmount: 200000,
__v: 0,
status: 1,
txn_date: "2017-01-21T19:07:53.214Z",
keywords: [
"JavaScript",
"AngularJs",
"C"
]
},
{
_id: "5883b213872c7d00042e1631",
projectTitle: "Indus Milk",
client_id: {
_id: "58833d9e1079ba000445a3a1",
clientName: "Anil",
companyName: "fgjhtrjhed",
website: "jhtryjh",
clientEmail: "frhyrt#tyhtr.dfkgjoiu",
mobileNumber: 549821654,
companyLocation: "New Delhi, Delhi, India",
clientCategory: "Freelancer",
__v: 0,
status: 1,
txn_date: "2017-01-21T10:53:18.360Z"
},
domain: "Back to the Future",
start_date: "2017-01-01T18:30:00.000Z",
end_date: "2017-01-06T18:30:00.000Z",
proposed_amount: 9200,
expectedFinalAmount: 15000,
__v: 0,
status: 1,
txn_date: "2017-01-21T19:10:11.403Z",
keywords: [
"Java",
"JavaScript"
]
},
{
_id: "5883b817872c7d00042e1632",
proposed_amount: 120000,
start_date: "2017-01-09T18:30:00.000Z",
end_date: "2017-02-03T18:30:00.000Z",
domain: "Back to the Future",
client_id: {
_id: "58833d9e1079ba000445a3a1",
clientName: "Anil",
companyName: "fgjhtrjhed",
website: "jhtryjh",
clientEmail: "frhyrt#tyhtr.dfkgjoiu",
mobileNumber: 549821654,
companyLocation: "New Delhi, Delhi, India",
clientCategory: "Freelancer",
__v: 0,
status: 1,
txn_date: "2017-01-21T10:53:18.360Z"
},
projectTitle: "Something",
__v: 0,
status: 1,
txn_date: "2017-01-21T19:35:51.835Z",
keywords: [
"C++",
"C"
]
}
]
Expecting something like
[
{
client_id: {
_id: "58833d9e1079ba000445a3a1",
clientName: "Anil",
companyName: "fgjhtrjhed",
website: "jhtryjh",
clientEmail: "frhyrt#tyhtr.dfkgjoiu",
mobileNumber: 549821654,
companyLocation: "New Delhi, Delhi, India",
clientCategory: "Freelancer",
__v: 0,
status: 1,
txn_date: "2017-01-21T10:53:18.360Z"
},
project_id : [
{
_id: "5883b213872c7d00042e1631",
projectTitle: "Indus Milk",
domain: "Back to the Future",
start_date: "2017-01-01T18:30:00.000Z",
end_date: "2017-01-06T18:30:00.000Z",
proposed_amount: 9200,
expectedFinalAmount: 15000,
},{
_id: "5883b817872c7d00042e1632",
proposed_amount: 120000,
start_date: "2017-01-09T18:30:00.000Z",
end_date: "2017-02-03T18:30:00.000Z",
domain: "Back to the Future",
projectTitle: "Something",
expectedFinalAmount: 15000,
}
]
}
]