Why I need to specify $switch default statement while it is optional? - mongodb

I want to update the documents with a condition. I tried $cond, but it needs an expression for false case. And I don't want to update anything if it is false. Below is the sample of the document:
{
"_id" : ObjectId("5bc29e0d0fc2c40a9d628afe"),
"BasicInfo" : {
"RepNo" : "AE179",
"CompanyName" : "First Bancshares Inc",
"IRSNo" : "640862173",
"CIKNo" : "0000947559",
"Name" : "Ordinary Shares",
"Ticker" : "FBMS",
"CUSIP" : "318916103",
"ISIN" : "US3189161033",
"RIC" : "FBMS.O",
"SEDOL" : "2184300",
"DisplayRIC" : "FBMS.OQ",
"InstrumentPI" : "10552665",
"QuotePI" : "26300255",
"Exchange" : "NASDAQ"
},
"Annual" : {
"Date" : ISODate("2017-12-31T00:00:00.000Z"),
"INC" : {
"SIIB" : {
"Description" : "Interest Income, Bank",
"Value" : 66.06941
},
"STIE" : {
"Description" : "Total Interest Expense",
"Value" : 6.90925
},
"ENII" : {
"Description" : "Net Interest Income",
"Value" : 59.16016
Then, I tried to use $switch since the documentation said default statement is optional.
And I have written the following code:
db.getCollection('FinancialStatement').aggregate([
{"$unwind":"$Annual"},
{"$addFields":{"Annual.Price":
{"$switch":{
branches:[
{
case: {
"$and":[
{"$eq":["$_id", ObjectId("5bc29e0d0fc2c40a9d628afe")]},
{"$eq":["$Annual.Date", ISODate("2017-12-31 00:00:00.000Z")]}
]
},
then: 1000}
],
default: -2000
}
}
}
}
]
)
It basically add a new field called annual.price if objectID and date requirements are met. However, if I omitt the default statement, the program returns an error saying :
Assert: command failed: {
"ok" : 0,
"errmsg" : "$switch could not find a matching branch for an input, and no default was specified.",
"code" : 40066,
"codeName" : "Location40066"
}

From docs on usage of default
Optional. The path to take if no branch case expression evaluates to
true.
Although optional, if default is unspecified and no branch case
evaluates to true, $switch returns an error.
Use $$REMOVE in 3.6.
Something like
Using $cond
{"$addFields":{
"Annual.Price":{
"$cond":[
{
"$and":[
{"$eq":["$_id",ObjectId("5bc29e0d0fc2c40a9d628afe")]},
{"$eq":["$Annual.Date",ISODate("2017-12-31 00:00:00.000Z")]}
]
},
1000,
"$$REMOVE"
]
}
}}
Using $switch
{"$addFields":{
"Annual.Price":{
"$switch":{
"branches":[
{
"case":{
"$and":[
{"$eq":["$_id",ObjectId("5bc29e0d0fc2c40a9d628afe")]},
{"$eq":["$Annual.Date",ISODate("2017-12-31 00:00:00.000Z")]}
]
},
"then":1000
}
],
"default":"$$REMOVE"
}
}
}}

Related

Mongo aggregation pipeline : How can I access one group stage variable in subsequent stage?

I have two collections here :
> db.Unit.find({}).limit(1).pretty()
{
"_id" : ObjectId("58b6878de1648d05903e1beb"),
"number" : "07in15in4",
"owner" : {
"name" : "vacant"
},
"floor" : 15,
"tower" : 4,
"VisitorLogs" : [
ObjectId("58b6878ee1648d05903e1d22"),
ObjectId("58b6878ee1648d05903e236e"),
ObjectId("58b6878ee1648d05903e23c9"),
ObjectId("58b6878ee1648d05903e2454"),
ObjectId("58b6878ee1648d05903e2915"),
ObjectId("58b6878ee1648d05903e2aae"),
ObjectId("58b6878ee1648d05903e2b93")
]
}
and
> db.VisitorLog.find({}).limit(1).pretty()
{ "_id" : ObjectId("58b6878fe1648d05903e2efa"), "purpose" : "WorkHere" }
VisitorLogs in the Unit collection refers to the second collection, i.e. VisitorLog.
I need to find the the units which have the maximum number of visits for each purpose.
I tried this in mongo shell:
units=db.Unit
units.aggregate([
{$match:{'VisitorLogs':{$gt:[]}}},
{$unwind:'$VisitorLogs'},
{$lookup:{
from:"VisitorLog",
localField:"VisitorLogs",
foreignField:"_id",
as:"log"}
},
{$group:{_id:{number:"$number", purpose:"$log.purpose"}, count:{$sum:1}}},
{$sort:{count:-1}},
{$group:{_id:"$_id.purpose", unit:{$first:"$_id.number"}}},
{$limit:10}
])
I get the following result :
{ "_id" : [ "Business" ], "unit" : "05in12in2" }
{ "_id" : [ "WorkHere" ], "unit" : "09in04in2" }
{ "_id" : [ "Casual" ], "unit" : "10in05in2" }
{ "_id" : [ "JobInterview" ], "unit" : "05in14in2" }
This means that, for example, unit number 05in12in2 had the maximum visits which had the purpose "Business"
I now want to get the number of "business" visits that 05in12in2 had.
I think its in "count" variable of the first group stage.
How do I access that ? I tried {$group:{_id:"$_id.purpose", unit:{$first:"$_id.number"}, visits:"$count"}}, in the second last stage, i.e just before the limit stage but I get error :
> units.aggregate([
... {$match:{'VisitorLogs':{$gt:[]}}},
... {$unwind:'$VisitorLogs'},
... {$lookup:{from:"VisitorLog", localField:"VisitorLogs", foreignField:"_id", as:"log"}},
... {$group:{_id:{number:"$number", purpose:"$log.purpose"}, count:{$sum:1}}},
... {$sort:{count:-1}},
... {$group:{_id:"$_id.purpose", unit:{$first:"$_id.number"}, visits:"$count"}},
... {$limit:10}
... ])
assert: command failed: {
"ok" : 0,
"errmsg" : "the group aggregate field 'visits' must be defined as an expression inside an object",
"code" : 15951
} : aggregate failed
_getErrorWithCode#src/mongo/shell/utils.js:25:13
doassert#src/mongo/shell/assert.js:13:14
assert.commandWorked#src/mongo/shell/assert.js:287:5
DBCollection.prototype.aggregate#src/mongo/shell/collection.js:1312:5
#(shell):1:1
Can someone please help me ?
You have this information, but you are cutting it off by the $group stage. Just change the stage to the following (notice the last argument):
{ $group : {
_id : "$_id.purpose",
unit : { $first : "$_id.number" },
visits : { $first : "$count" }
}}
The following outputs what I needed :
units.aggregate([
{$match:{'VisitorLogs':{$gt:[]}}},
{$unwind:'$VisitorLogs'},
{$lookup:{from:"VisitorLog", localField:"VisitorLogs", foreignField:"_id", as:"log"}},
{$group:{_id:{number:"$number", purpose:"$log.purpose"}, count:{$sum:1}}},
{$sort:{count:-1}},
{$group:{_id:"$_id.purpose", number:{$first:"$_id.number"}, visits:{$first:"$count"}}},
{$limit:10}
])

Positional operator and field limitation

In a find query projection, fields I specify after the positional operator are ignored and the whole document is always returned.
'myArray.$.myField' : 1 behave exactly like 'myArray.$' : 1
the positional operator selects the right document. But this document is quite big. I would like to project only 1 field from it.
Exemple:
db.getCollection('match').find({"participantsData.id" : 0001}, { 'participantsData.$.id': 1, })
here the response I have
{
"_id" : "myid",
"matchCreation" : 1463916465614,
"participantsData" : [
{
"id" : 0001,
"plenty" : "of",
"other" : "fields",
"and" : "subdocuments..."
}
]
}
This is what I want
{
"_id" : "myid",
"matchCreation" : 1463916465614,
"participantsData" : [
{
"id" : 0001
}
]
}
Is it possible with mongo?
Yes it can be done in mongo
Please try the below query
db.getCollection('match').find(
{"participantsData.id" : 0001},
{"participantsData.id": 1, "matchCreation": 1 })
This will give you the below result
{
"_id" : "myid",
"matchCreation" : 1463916465614,
"participantsData" : [
{
"id" : 1
}
]
}

how to update property in nested mongo document

I want to update a particular property in a nested mongo document
{
"_id" : ObjectId("55af76e60b0e4b318ba822ec"),
"make" : "MERCEDES-BENZ",
"model" : "E-CLASS",
"variant" : "E 250 CDI CLASSIC",
"fuel" : "Diesel",
"cc" : 2143,
"seatingCapacity" : 5,
"variant_+_fuel" : "E 250 CDI CLASSIC (Diesel)",
"make_+_model_+_variant_+_fuel" : "MERCEDES-BENZ E-CLASS E 250 CDI CLASSIC (Diesel)",
"dropdown_display" : "E-CLASS E 250 CDI CLASSIC (Diesel)",
"vehicleSegment" : "HIGH END CARS",
"abc" : {
"variantId" : 1000815,
"makeId" : 1000016,
"modelId" : 1000556,
"fuelId" : 2,
"segmentId" : 1000002,
"price" : 4020000
},
"def" : {
"bodyType" : 1,
"makeId" : 87,
"modelId" : 21584,
"fuel" : "DIESEL",
"vehicleSegmentType" : "E2"
},
"isActive" : false
}
This is my document. If I want to add or update a value for key "nonPreferred" inside "abc", how do I go about it?
I tried it with this query:
db.FourWheelerMaster.update(
{ "abc.modelId": 1000556 },
{
$Set: {
"abc": {
"nonPreferred": ["Mumbai", "Pune"]
}
}
},
{multi:true}
)
but it updates the whole "abc" structure, removed all key:values inside it and kept only newly inserted key values like below
"abc" : {
"nonPreferred" : [
"Mumbai",
"Pune"
]
},
Can anyone tell me how to update only particular property inside it and not all the complete key?
Instead of using the $set operator, you need to push that array using the $push operator together with the $each modifier to append each element of the value separately as follows:
db.FourWheelerMaster.update(
{ "abc.modelId": 1000556 },
{
"$push": {
"abc.nonPreferred": {
"$each": ["Mumbai", "Pune"]
}
}
},
{ "multi": true }
)

$add operation returns null value

I have collection with document structure :
{
'year' : 2014,
'month' : 1
}
I am executing the following operation :
db.collname.aggregate(
[
{
$project : {
'year100' : {$multiply : ["$year" , 100]},
'result' : { '$add' : ['$year100', '$month'] }
}
}
]
);
I get the following result :
{
"result" : [
{
"_id" : ObjectId("5563596c515a88832210f0e4"),
"year100" : 201400.0000000000000000,
"result" : null
},
}
Why is add operation returuning null value as against to actual value ? Please help.
MongoDb not allow to used same fields in project to arithmetic operation instead of one $project used two different projects like this :
db.collname.aggregate({ $project : { 'year100' : {$multiply : ["$year" , 100]} ,"month":"$month"} },{"$project":{"year100":1,"result":{"$add":["$year100","$month"]}}})

get a mongo document based in two different values

I have the following document structure
{
"_id" : "aaa0001",
"path" : "/some/path",
"information" : {
"name" : "info"
},
"colors" : {
"colors" : [
{
"key" : "AAAA001",
"name" : "White"
},
{
"key" : "BBBB002",
"name" : "Black"
}
]
}
}
the idea is that I have to return the document by the color key. I have two parameters the "path" and the "color", so, I was trying to make something like this
db.components.find(
{$and:[
{"path" : "/some/path"},
{"colors":{"colors" : {$elemMatch: { "key" : "AAAA001" } } } }
]})
I'm getting the following message "Script is executed successfully, but there is no results to show".
Can anyone give me some directions regarding this?
thanks
Use the following query:
db.components.find({
"path": "/some/path",
"colors.colors.key" : "AAAA001"
})
MongoDB expects query document to contain field-value pairs { <field>: <value> }. So, in your example you're querying for a document with colors field equal to:
{"colors" : {$elemMatch: { "key" : "AAAA001" } } }
As for $and and $elemMatch operators, you don't need them in such a simple query.
For more information read Query Documents.
Update
You can also select only matching subdocument from colors array using Positional Operator $:
db.components.find({
"path": "/some/path",
"colors.colors.key" : "AAAA001"
}, {
_id: 0,
"colors.colors.$": 1
})
Though, you won't be able to change your documents structure, thus getting
{ "colors" : { "colors" : [ { "key" : "AAAA001", "name" : "White" } ] } }