Map Data in MongoDB Aggregation pipeline - node.js

This is the data returned from my aggregation pipeline:
/* 1 */
{
"data" : {
"_id" : ObjectId("5c8266b8d8c9cd7a89babac6"),
"updatedBy" : ObjectId("5c2076d62781881e8764a47a"),
"updatedAt" : ISODate("2019-03-08T13:33:21.659Z"),
"createdBy" : ObjectId("5c2076d62781881e8764a47a"),
"createdAt" : ISODate("2019-03-08T12:57:28.683Z"),
"productName" : "Yellow powder",
"sku" : "563453534",
"upc" : "903453453245",
"__v" : 0,
"brand" : "",
"category" : "yellow category",
"group" : "",
"handle" : "yellow-grey",
"imageUrl" : "",
"inciRawText" : "",
"specialtyText" : ""
}
}
/* 2 */
{
"data" : {
"_id" : ObjectId("5c7692433dcd874313b9fddb"),
"sku" : "TESTSKU9",
"__v" : 0,
"brand" : "test brand",
"category" : "test category",
"createdAt" : ISODate("2019-02-27T13:36:03.027Z"),
"group" : "testgroup",
"handle" : "mewo9",
"imageUrl" : "",
"inciRawText" : "Inci Raw Text. Long memo field",
"productName" : "TEST9PROD",
"publishStatus" : "Unpublished",
"specialtyText" : "* denotes organic",
"upc" : "TESTUPC9",
"updatedAt" : ISODate("2019-02-27T13:36:03.027Z")
}
}
The aggregation pipeline is too big that's why I didn't wanted to add it here. I hope it's ok to omit that part.
Now I want to project this data to something like this using MongoDB Aggregation
{
"_id" : ObjectId("5c8266b8d8c9cd7a89babac6"),
"updatedBy" : ObjectId("5c2076d62781881e8764a47a"),
"updatedAt" : ISODate("2019-03-08T13:33:21.659Z"),
"createdBy" : ObjectId("5c2076d62781881e8764a47a"),
"createdAt" : ISODate("2019-03-08T12:57:28.683Z"),
"productName" : "Yellow powder",
"sku" : "563453534",
"upc" : "903453453245",
"__v" : 0,
"brand" : "",
"category" : "yellow category",
"group" : "",
"handle" : "yellow-grey",
"imageUrl" : "",
"inciRawText" : "",
"specialtyText" : ""
}, {
"_id" : ObjectId("5c7692433dcd874313b9fddb"),
"sku" : "TESTSKU9",
"__v" : 0,
"brand" : "test brand",
"category" : "test category",
"createdAt" : ISODate("2019-02-27T13:36:03.027Z"),
"group" : "testgroup",
"handle" : "mewo9",
"imageUrl" : "",
"inciRawText" : "Inci Raw Text. Long memo field",
"productName" : "TEST9PROD",
"publishStatus" : "Unpublished",
"specialtyText" : "* denotes organic",
"upc" : "TESTUPC9",
"updatedAt" : ISODate("2019-02-27T13:36:03.027Z")
}
I'm sure there must be a method to do it, I'm trying to do it using $map operator, but still unable to get it done.
Can anyone please suggest me some solutions?

You can achieve that using $replaceRoot aggregation operator
db.produce.aggregate( [
{
$replaceRoot: { newRoot: "$data" }
}
] )

Related

How to find triple conditions in MongoDB

I have database like below.
{
"_id" : ObjectId("628bed8298b81711c01b6ab1"),
"status" : true,
"city" : "Kefar Sava",
"request" : [
{
"deadline" : {
"from" : ISODate("2022-05-03T00:00:00.000+0000"),
"to" : ISODate("2022-05-31T00:00:00.000+0000")
},
"ownerId" : "628c424431884d1c140f56bc",
"type" : "Developer",
"name" : "Apollo",
"status" : "posted",
"_id" : ObjectId("628d496d38360a30a4c39ebd"),
},
{
"deadline" : {
"from" : ISODate("2022-05-03T00:00:00.000+0000"),
"to" : ISODate("2022-06-02T00:00:00.000+0000")
},
"ownerId" : "628c424431884d1c140f56bb",
"type" : "Developer",
"name" : "Apollo",
"status" : "posted",
"_id" : ObjectId("628d4a00a4ec7e12a02e74a3"),
},
],
"date" : ISODate("2022-05-23T20:24:34.052+0000"),
"__v" : NumberInt(0),
}
I want to find by ownerId, type, city and set status to canceled.
I use
const owner = await User.findOneAndUpdate(
{$and: [{city: city, "request.ownerId": ownerId, "request.type": type }]},
{$set: {"request.$.status": "canceled"}},
{new: true})
But I can't find by type. When I change order of that(ownerId and type), then I can't find by ownerId.
Please tell me what's wrong with my code.

How Do I group in Mongoose for categories

My Data :
I have category , Sub category 1 and sub category 2. I am storing each category by using parent_id. And the following is my data
{
"_id" : ObjectId("000000005e712c388852b550"),
"__v" : 0,
"category" : "ewb",
"created_date" : ISODate("2019-01-09T06:55:57.376Z"),
"parent_id" : null,
"status" : "Y",
"type" : "Column"
}
{
"_id" : ObjectId("000000005e712c388852b551"),
"__v" : 0,
"category" : "wealth",
"created_date" : ISODate("2019-01-09T06:55:57.378Z"),
"parent_id" : ObjectId("000000005e712c388852b550"),
"status" : "Y"
}
{
"_id" : ObjectId("000000005e712c388852b552"),
"__v" : 0,
"category" : "hh economic well-being",
"created_date" : ISODate("2019-01-09T06:55:57.379Z"),
"parent_id" : ObjectId("000000005e712c388852b551"),
"status" : "Y"
}
Now I have to print as
Category : [{
category : 'ewb',
"_id" : ObjectId("000000005e712c388852b550"),
sub_cat1 : [{
category : 'wealth',
"_id" : ObjectId("000000005e712c388852b551"),
sub_cat2 : [{
category : 'hh economic well-being',
"_id" : ObjectId("000000005e712c388852b552"),
}]
}]
}]
Without changing the schema How do I achieve this. I refered SO

How to define a unique index for an element of an arrary in Mongo

I'd like to define a unique index for the last element of an array? is that even possible in mongo?
only status[this.status.length-1].state and user_id for specific status.state (e.g active) has to be unique.
note: the last element of status array define the user's current status.
e.g: 2 user_id with status[this.status.length-1].state: **revoked** state can be exist. but 2 user_id with status[this.status.length-1].state: **active** cannot be exist.
{
"_id" : ObjectId("59a609778c6929e7122322cf"),
"user_id": '9e2cZ'
"status" : [
{
"state" : "active",
"updatedAt" : ISODate("2017-08-30T21:23:21.158Z"),
"createdAt" : ISODate("2017-08-30T21:23:21.158Z")
},
{
"updatedAt" : ISODate("2017-08-31T22:24:02.613Z"),
"createdAt" : ISODate("2017-08-31T22:24:02.613Z"),
"state" : "revoked"
},
{
"updatedAt" : ISODate("2017-08-31T22:25:02.888Z"),
"createdAt" : ISODate("2017-08-31T22:25:02.888Z"),
"state" : "active"
}
],
}
Index an array element
The only way that I know to perform it is to reverse your problem.
The latest status should be in first position of your array.
You can insert it with unshift function. And create your index this way :
db.coll.createIndex({user_id:1, 'status.0.state': 1})
This will index the first element of your array.
Here is the explain that show the use of the index :
db.coll.find({user_id: 1, 'status.0.state': "active"}).explain().queryPlanner.winningPlan
{
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"user_id" : 1,
"status.0.state" : 1
},
"indexName" : "user_id_1_status.0.state_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"user_id" : [ ],
"status.0.state" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"user_id" : [
"[1.0, 1.0]"
],
"status.0.state" : [
"[\"active\", \"active\"]"
]
}
}
}
To insert your status in node you should turn
user.status.push(newStatus);
into
user.status.unshift(newStatus);
Unique index with partial indexes
To have a uniqueness on the active status, you can combine the previous answer with a partial index.
The partial index will index only the active users.
Here is the index creation command :
db.users.createIndex(
{user_id:1, 'status.0.state': 1},
{unique: true, partialFilterExpression: {"status.0.state": {$eq: 'active'}}}
)
Then, I insert 2 users, one active and one inactive
> db.users.find().pretty()
{
"_id" : ObjectId("59a609778c6929e7122322cd"),
"user_id" : "9e2cZ",
"status" : [
{
"state" : "inactive",
"updatedAt" : ISODate("2017-08-30T21:23:21.158Z"),
"createdAt" : ISODate("2017-08-30T21:23:21.158Z")
},
{
"updatedAt" : ISODate("2017-08-31T22:24:02.613Z"),
"createdAt" : ISODate("2017-08-31T22:24:02.613Z"),
"state" : "revoked"
},
{
"updatedAt" : ISODate("2017-08-31T22:25:02.888Z"),
"createdAt" : ISODate("2017-08-31T22:25:02.888Z"),
"state" : "active"
}
]
}
{
"_id" : ObjectId("59a609778c6929e7122322cf"),
"user_id" : "9e2cZ",
"status" : [
{
"state" : "active",
"updatedAt" : ISODate("2017-08-30T21:23:21.158Z"),
"createdAt" : ISODate("2017-08-30T21:23:21.158Z")
},
{
"updatedAt" : ISODate("2017-08-31T22:24:02.613Z"),
"createdAt" : ISODate("2017-08-31T22:24:02.613Z"),
"state" : "revoked"
},
{
"updatedAt" : ISODate("2017-08-31T22:25:02.888Z"),
"createdAt" : ISODate("2017-08-31T22:25:02.888Z"),
"state" : "active"
}
]
}
It works, I have 2 users with the same user_id but different statuses.
If I try to insert another active user with the same user_id, it fails :
> db.users.insert({user_id: '9e2cZ', status: [{state: 'active'}]})
WriteResult({
"nInserted" : 0,
"writeError" : {
"code" : 11000,
"errmsg" : "E11000 duplicate key error collection: test.users index: user_id_1_status.0.state_1 dup key: { : \"9e2cZ\", : \"active\" }"
}
})
This way, you have an unique index based on the first element of an array.

Using mongoose full text search on sub doc and return the match sub only

Hello i have proble searching sub document by text, i have been doing it right with query $text: {$search: "string"}, but it returns the whole document include the parent
this is my collection looks like
{
"_id" : ObjectId("56b06b7fe61af1081ceea185"),
"accountName" : "Agnes",
"address" : {
"street" : "jl. A.Yani no 23",
"city" : "Jakarta",
"province" : "DKI Jakarta",
"country" : "indonesia",
"postalCode" : "61382"
},
"__v" : NumberInt(1),
"products" : [
{
"title" : "Ayam goreng kremes",
"ingredients" : "Daging ayam, tepung terigu, bawang merah, sasa",
"description" : "",
"price" : "15000",
"_id" : ObjectId("56c191bd3da50ccc149717a5"),
"updatedAt" : ISODate("2016-02-15T08:52:13.465+0000"),
"createdAt" : ISODate("2016-02-15T08:52:13.465+0000"),
"showCounter" : NumberInt(0),
"featured" : false
},
{
"title" : "Sate Maranggi",
"ingredients" : "Daging sapi, kecap, cabai, bawang merah, bawang putih",
"description" : "Sate maranggi enak dan lezat",
"price" : "15000",
"_id" : ObjectId("56b06eae492acbbc1fd070e9"),
"pickOfTheDay" : false,
"sold" : NumberInt(0),
"featured" : true
}
]
}
{
"_id" : ObjectId("56c190203da50ccc149717a3"),
"accountName" : "acha",
"address" : {
"street" : "jl. Sudirman no 23",
"city" : "Bogor",
"province" : "Jawa Barat",
"country" : "indonesia",
"postalCode" : "61382"
},
"__v" : NumberInt(1)
"products" : [
{
"title" : "Pecel lele",
"ingredients" : "Lele, bawang merah, bawang putih, sasa, ketumbar, kemiri",
"description" : "",
"price" : "15000",
"_id" : ObjectId("56c192aa3da50ccc149717a9"),
"updatedAt" : ISODate("2016-02-15T08:56:10.716+0000"),
"createdAt" : ISODate("2016-02-15T08:56:10.716+0000"),
"showCounter" : NumberInt(0),
"featured" : false
}
]
}
and this is my search query
model.find({ $text: { $search: "Sate" } }).exec(function(err, docs){
if (err){
res.send(err);
}else{
res.send(docs);
}
and what i need is the sub document that matches the string "Sate" and the parent id if its possible.
maybe it looks like this
{
"parentID": ObjectId("56b06b7fe61af1081ceea185"),
"title" : "Sate Maranggi",
"ingredients" : "Daging sapi, kecap, cabai, bawang merah, bawang putih",
"description" : "Sate maranggi enak dan lezat",
"price" : "15000",
"_id" : ObjectId("56b06eae492acbbc1fd070e9"),
"pickOfTheDay" : false,
"sold" : NumberInt(0),
"featured" : true
}

how to delete object in array based on object property in mongodb?

This is my mongodb document.
"_id" : ObjectId("5123731f2763c9682a000001"),
"created_on" : ISODate("2013-02-19T12:42:07.835Z"),
"currentLogin" : ISODate("2013-02-22T06:03:25.603Z"),
"email" : "xxx#test.com",
"extraPhotos" : 0,
"reqmail" : [ ],
"isActivated" : true,
"lastLogin" : ISODate("2013-02-20T05:42:15.359Z"),
"linkedAccounts" : [
{
"birthday" : "11/01/1984",
"email" : "testingxxx234#gmail.com",
"expiresIn" : "5184000",
"expiryMailNotifFlag" : -1,
"extraPhotos" : 0,
"fetchFromDate" : null,
"fetchOrder" : "oldest",
"fetchUntilDate" : "04/11/2014",
"locale" : "en_US",
"optIn" : true,
"timezone" : "5.5",
"userID" : "100000207309657"
}
],
"maxLinkedAccounts" : 4,
"name" : "venu wale",
"notifications" : [
{
"_id" : ObjectId("51275fb7389b85f222000001"),
"type" : "limitUsed",
"message" : "Account limit is used",
"accID" : "100000207309657",
"date" : ISODate("2013-02-22T12:08:23.419Z")
},
{
"_id" : ObjectId("51275fe8389b85f222000002"),
"type" : "limitUsed",
"message" : "Account limit is used",
"accID" : "100000207309657",
"date" : ISODate("2013-02-22T12:09:12.385Z")
},
{
"_id" : ObjectId("51276020389b85f222000003"),
"type" : "limitUsed",
"message" : "Account limit is used",
"accID" : "100000207309657",
"date" : ISODate("2013-02-22T12:10:08.275Z")
},
{
"_id" : ObjectId("5127605d389b85f222000004"),
"type" : "limitUsed",
"message" : "Account limit is used",
"accID" : "100000207309657",
"date" : ISODate("2013-02-22T12:11:09.946Z")
},
{
"_id" : ObjectId("51276097389b85f222000005"),
"type" : "limitUsed",
"message" : "Account limit is used",
"accID" : "100000207309657",
"date" : ISODate("2013-02-22T12:12:07.384Z")
},
{
"_id" : ObjectId("512760d2389b85f222000006"),
"type" : "limitUsed",
"message" : "Account limit is used",
"accID" : "100000207309657",
"date" : ISODate("2013-02-22T12:13:06.933Z")
}
],
"password" : "638cfc167e8431c01227e4f113ec9427821b12493ed6fd3",
"phone" : "5555555555",
"photo_updated_time" : 1361341819,
"photosProcessed" : 15,
"photosToProcess" : 400,
"plan" : {
"type" : "photoOnly"
},
"salt" : "af8ab2f5866642f03d31aa3f4a24e25e287bedc7",
"updated_on" : ISODate("2013-02-19T12:42:07.835Z")
I Want to delete any one object from notifications array. I am using this query.
db.users.update({phone:'5555555555'},{$unset:{notifications:{$elemMatch:{_id:ObjectId('51276020389b85f222000003')}}}})
But its not working.
You only need to pull this element from the notifications array:
db.collection.update({phone:"5555555555"},{$pull:{notifications: {_id:ObjectId("51276020389b85f222000003")}}})
Using pull, you can remove all elements that match your inner query. The next $pull
{$pull:{ notifications: {type: "limitUsed"}}}
will remove all notifications of type "limitUsed".

Resources