How to update array of objects to LowerCase in mongodb? - mongodb

I need to update the role in team array to lowercase.
db.users.find().pretty().limit(1)
{
"_id" : ObjectId("5d9fd81d3d598088d2ea5dc9"),
"employed" : "USA-Atlanta",
"firstName" : "Rory",
"siteRole" : "super admin",
"status" : "active",
"team" : [
{
"name" : "SALES AND MARKETING",
"displayName" : "S&M",
"role" : "Manager"
}
]
}
Tried this code.I m getting it with normal fields.
db.users.find( {}, { 'role': 1 } ).forEach(function(doc) {
db.users.update(
{ _id: doc._id},
{ $set : { 'role' : doc.role.toLowerCase() } },
{ multi: true }
)
});
sample output
"team" : [
{
"name" : "SALES AND MARKETING",
"displayName" : "S&M",
"role" : "manager"
}
]

I think the below Aggregation query is what you are looking for
var count = 0;
db.users.aggregate([
{
"$match": {
"team.role": {$exists: true}
}
},
{
"$project": {
"_id": 1,
// "team": 1,
"teamModified": {
"$map": {
"input": "$team",
"as": "arrayElems",
"in": {
"$mergeObjects": [
"$$arrayElems",
{"role": {"$toLower": "$$arrayElems.role"}}
]
}
}
}
}
},
]).forEach(function(it) {
db.users.updateOne({
"_id": it["_id"]
}, {
"$set": {
"team": it["teamModified"]
}
})
printjson(++count);
})
printjson("DONE!!!")
Note: I haven't tested the script properly in my local, so do let me know if it didn't help you out

Related

formatted response for find query mongo

I have record:
{
"name" : "user",
"number":"09xxxxxxx21",
"pc" : [{
"pcId" : "1",
"pcName" : "Lenovo",
"pcOwner" : "user1",
"using" : true
}, {
"pcId" : "2",
"pcName" : "Lenovo",
"pcOwner" : "user1",
"using": false
}, {
"pcId" : "3",
"pcName" : "Dell",
"pcOwner" : "user1",
"using": true
}, {
"pcId" : "4",
"pcName" : "Dell",
"pcOwner" : "user1",
"using": true
}
]}
}
using query .find({'pcID':'4','pc.pcName':'Dell'}) I'm getting complete record but I want record where I have pcName:'Dell' only.
Something Like:
{
"name" : "user",
"number":"09xxxxxxx21",
"pc" : [
{
"pcId" : "3",
"pcName" : "Dell",
"pcOwner" : "user1",
"using": true
},
{
"pcId" : "4",
"pcName" : "Dell",
"pcOwner" : "user1",
"using": true
}
]}
}
or those 2 object only.
$filter is what you are looking for.
You can use it in an [aggregate][1]
with $match you select the documents you want in the collection
Add then with $filter you filter the list
db.collection.aggregate([
{
"$match": {
"pcID": "4"
}
},
{
$project: {
pc: {
$filter: {
input: "$pc",
as: "pc",
cond: {
$eq: [
"$$pc.pcName",
"Dell"
]
}
}
}
}
}
])
Try it here
You can use the Aggregate
db.collection.aggregate([
{
"$match": {
"name": "user",
"pc.pcName": "Dell"
}
},
{
"$project": {
"name": 1,
"number": 1,
"pc": {
"$filter": {
"input": "$pc",
"as": "pc",
"cond": {
"$eq": [
"$$pc.pcName",
"Dell"
]
}
}
}
}
}
])

Project boolean value if any of the nested array item satisfies a condition in mongoDB

An example structure of my collection is below
{
"_id" : ObjectId("5f631d6f3792ae9ce5e35ddd"),
"from" : "kathy",
"content" : "hello",
"to" : [
{
"name" : "david",
"isfavorite" : true
},
{
"name" : "james",
"isfavorite" : false
},
{
"name" : "steve",
"isfavorite" : true
}
]
}
{
"_id" : ObjectId("5f631d6f3792ae9ce5e35dde"),
"from" : "kathy",
"content" : "hey",
"to" : [
{
"name" : "david",
"isfavorite" : false
},
{
"name" : "john",
"isfavorite" : false
},
{
"name" : "roy",
"isfavorite" : true
}
]
}
I am trying to get messages received by a particular person. In this case if we show steve's messages I need to know if it is set as favorite by him. What I need is to project the favorite flag on the outer document itself if it matches some condition
I tried this but not working
db.getCollection('Messages_Collection').aggregate([
{
$match:
{
"to":
{
$elemMatch:
{
"name":"steve"
}
}
}
},
{
$project:
{
"_id":1,
"from":1,
"to":1,
"isfavorite":
{
$cond:
{
if:
{
"$to":
{
$elemMatch:
{
"isfavorite":true,
"name":"david"
}
},
then:true,
else:false
}
}
}
}
}])
Mongo version :4.0.13
You can simply use $filter to filter the particular person and get.
[
{
$match: {
"to.name": "steve"
}
},
{
$addFields: {
isfavorite: {
"$arrayElemAt": [
{
$filter: {
input: "$to",
cond: {
$eq: [
"$$this.name",
"steve"
]
}
}
},
0
]
}
}
},
{
$addFields: {
isfavorite: "$isfavorite.isfavorite"
}
}
]
Working Mongo playground

Combine results based on condition during group by

Mongo query generated out of java code:
{
"pipeline": [{
"$match": {
"Id": "09cd9a5a-85c5-4948-808b-20a52d92381a"
}
},
{
"$group": {
"_id": "$result",
"id": {
"$first": "$result"
},
"labelKey": {
"$first": {
"$ifNull": ["$result",
"$result"]
}
},
"value": {
"$sum": 1
}
}
}]
}
Field 'result' can have values like Approved, Rejected, null and "" (empty string). What I am trying to achieve is combining the count of both null and empty together.
So that the empty string Id will have the count of both null and "", which is equal to 4
I'm sure theres a more "proper" way but this is what i could quickly come up with:
[
{
"$group" : {
"_id" : "$result",
"id" : {
"$first" : "$result"
},
"labelKey" : {
"$first" : {
"$ifNull" : [
"$result",
"$result"
]
}
},
"value" : {
"$sum" : 1.0
}
}
},
{
"$group" : {
"_id" : {
"$cond" : [{
$or: [
{"$eq": ["$_id", "Approved"]},
{"$eq": ["$_id", "Rejected"]},
]}},
"$_id",
""
]
},
"temp" : {
"$push" : {
"_id" : "$_id",
"labelKey" : "$labelKey"
}
},
"count" : {
"$sum" : "$value"
}
}
},
{
"$unwind" : "$temp"
},
{
"$project" : {
"_id" : "$temp._id",
"labelKey": "$temp.labelKey",
"count" : "$count"
}
}
],
);
Due to the fact the second group is only on 4 documents tops i don't feel too bad about doing this.
I have used $facet.
The MongoDB stage $facet lets you run several independent pipelines within the stage of a pipeline, all using the same data. This means that you can run several aggregations with the same preliminary stages, and successive stages.
var queries = [{
"$match": {
"Id": "09cd9a5a-85c5-4948-808b-20a52d92381a"
}
},{
$facet: {//
"empty": [
{
$match : {
result : { $in : ['',null]}
}
},{
"$group" : {
"_id" : null,
value : { $sum : 1}
}
}
],
"non_empty": [
{
$match : {
result : { $nin : ['',null]}
}
},{
"$group" : {
"_id" : '$result',
value : { $sum : 1}
}
}
]
}
},
{
$project: {
results: {
$concatArrays: [ "$empty", "$non_empty" ]
}
}
}];
Output :
{
"results": [{
"_id": null,
"value": 52 // count of both '' and null.
}, {
"_id": "Approved",
"value": 83
}, {
"_id": "Rejected",
"value": 3661
}]
}
Changing the group by like below solved the problem
{
"$group": {
"_id": {
"$ifNull": ["$result", ""]
},
"id": {
"$first": "$result"
},
"labelKey": {
"$first": {
"$ifNull": ["$result",
"$result"]
}
},
"value": {
"$sum": 1
}
}
}

Rewind data of two nested array field after $unwind and $lookup and $filter on date range in $project

{
"_id" : ObjectId("590b12b6330e1567acd29e69"),
"name": "Foo",
"sales_history" : [
{
"_id" : ObjectId("593ce8e4cfaa652df543d9e3"),
"sold_at" : ISODate("2017-06-11T06:53:24.881Z"),
"sold_to" : ObjectId("593509e938792e046ba14a02"),
"sold_products" : [
{
"product_dp" : 100,
"quantity" : 1,
"product_id" : ObjectId("591068be1f4c6c79a442a788"),
"_id" : ObjectId("593ce8e4cfaa652df543d9e5")
},
{
"product_dp" : 100,
"quantity" : 1,
"product_id" : ObjectId("593a33dccfaa652df543d924"),
"_id" : ObjectId("593ce8e4cfaa652df543d9e4")
}
]
},
{
"_id" : ObjectId("5944cb7142a04740357020b9"),
"sold_at" : ISODate("2017-06-17T06:25:53.332Z"),
"sold_to" : ObjectId("5927d4a59e58ba0c61066f3b"),
"sold_products" : [
{
"product_dp" : 500,
"quantity" : 1,
"price" : 5650,
"product_id" : ObjectId("593191ed53a2741dd9bffeb5"),
"_id" : ObjectId("5944cb7142a04740357020ba")
}
]
}
]
}
I have User schema like this. I want detail of product_id reference, with a date range search criteria on sold_at date field.
My expected data like following when I searched in sold_at at: 2017-06-11
{
"_id" : ObjectId("590b12b6330e1567acd29e69"),
"name": "Foo",
"sales_history" : [
{
"_id" : ObjectId("593ce8e4cfaa652df543d9e3"),
"sold_at" : ISODate("2017-06-11T06:53:24.881Z"),
"sold_to" : ObjectId("593509e938792e046ba14a02"),
"sold_products" : [
{
"product_dp" : 100,
"quantity" : 1,
"product_id": {
_id:ObjectId("hsfgg123412yh3gy1u2g3"),
name: "Product1",
code: "FG0154"
},
}
]
}
]
}
Product detail need to be populate in product_id, sales_history array need to be filtered in date range.
You can try below aggregation query.
$filter sales history on date range followed by $unwinding sales history & sold_products.
$lookup sold_products to get the product details.
$group back sold_products & sales history
db.collection.aggregate([
{
"$project": {
"name": 1,
"sales_history": {
"$filter": {
"input": "$sales_history",
"as": "history",
"cond": {
"$and": [
{
"$gte": [
"$$history.sold_at",
ISODate("2017-06-11T00:00:00.000Z")
]
},
{
"$lt": [
"$$history.sold_at",
ISODate("2017-06-12T00:00:00.000Z")
]
}
]
}
}
}
}
},
{
"$unwind": "$sales_history"
},
{
"$unwind": "$sales_history.sold_products"
},
{
"$lookup": {
"from": lookupcollection,
"localField": "sales_history.sold_products.product_id",
"foreignField": "_id",
"as": "sales_history.sold_products.product_id"
}
},
{
"$group": {
"_id": {
"_id": "$_id",
"sales_history_id": "$sales_history._id"
},
"name": {
"$first": "$name"
},
"sold_at": {
"$first": "$sales_history.sold_at"
},
"sold_to": {
"$first": "$sales_history.sold_to"
},
"sold_products": {
"$push": "$sales_history.sold_products"
}
}
},
{
"$group": {
"_id": "$_id._id",
"name": {
"$first": "$name"
},
"sales_history": {
"$push": {
"_id": "$_id.sales_history_id",
"sold_at": "$sold_at",
"sold_to": "$sold_to",
"sold_products": "$sold_products"
}
}
}
}
]);

Retrieve field value from array of sub document

I have some documents like this:
{
"hash": "14a076f9f6cecfc58339330eeb492e267f63062f6d5f669c7cdbfecf9eb4de32",
"started_services": [],
"deleted_files": [],
"software": {
"adobe" : {
"licenses" : [
{ "key": "2384723",
"date": "26-10-2012"
},
{ "key": "23888823",
"date": "09-11-2012"
}
]
}
}
}
How do I retrieve just the hash value and the list of "key" values?
I did the following, but, as you see, the result has the entire path which I do not want.
> db.repository.find({"$and": [{"datetime_int": {"$gte": 1451952000}},{"software.adobe.licenses.key" : { $exists : true}}]}, {hash:1, "software.adobe.licenses.key":1, _id:0}).limit(10)
{ "hash" : "a1532e0609aaf6acfa9e505e93af0bee0856a9a67398aeaa72aa6eb2fffd134e", "software" : { "adobe" : { "licenses" : [ { "key" : "2008350" }, { "key" : "2018350" }, { "key" : "2028350" }, { "key" : "2038350" }, { "key" : "2048350" }, { "key" : "2058350" }, { "key" : "2068350" }, { "key" : "2078350" }...]}}}
The result I want should look like this:
{"hash": "a1532e0609aaf6acfa9e505e93af0bee0856a9a67398aeaa72aa6eb2fffd134e",
"key": ["2008350", "2018350", "2028350", "2038350", "2048350", "2058350", "2068350", "2078350"]
}
How do I do that?
You can do this with the aggregation framework.
db.repository.aggregate([
{ "$match": {
"datetime_int": { "$gte": 1451952000 },
"software.adobe.licenses.key" : { "$exists" : true }
}},
{ "$project": {
"hash": 1,
"key": {
"$map": {
"input": "$software.adobe.licenses",
"as": "soft",
"in": "$$soft.key"
}
}
}}
])
Starting From MongoDB 3.2 you can directly project the sub-document array field.
{ "$project": { "hash": 1, "key": "$software.adobe.licenses.key"}}
db.key.aggregate((
{ "$match": {
"datetime_int": { "$gte": 1451952000 }
}},
{"$unwind":"$software.adobe.licenses"},
{"$project":{"key":"$software.adobe.licenses.key", "hash":1, "_id":0}}
))
outputs the following :
{ "hash" : "14a076f9f6cecfc58339330eeb492e267f63062f6d5f669c7cdbfecf9eb4de32", "key" : [ "2384723", "23888823" ] }