Modifying or Unwinding deep nested arrays with mongo query - mongodb

After having made my own aggregation pipeline, I have arrived at the following result:
[
{
"_id": "53712c7238b8d900008ef71c",
"address": [
{
"_id": "53712c7238b8d900008ef71b",
"phoneNumber": "4092348294",
...
}
],
"name": "TestDealer",
"vehicles": [
{
"_id": "53712fa138b8d900008ef720",
"createdAt": "2014-05-12T20:08:00.000Z",
"tags": [
"vehicle"
],
"opts": [
{
"_id": "53712fa138b8d900008ef71e",
"hasSunroof": false,
...
}
],
"listed": true,
"disclosures": [
{
"_id": "53712fa138b8d900008ef71f",
"waterDamage": false,
...
}
],
"details": [
{
"_id": "53712fa138b8d900008ef71d",
"year": 2007,
...
}
]
},
{
"_id": "5373c6439058859af7904a17",
"createdAt": "2014-05-14T19:17:00.000Z",
"tags": [
"vehicle"
],
"opts": [
{
"_id": "5373c6439058859af7904a15",
"hasSunroof": false,
....
"other": []
}
],
"listed": true,
"disclosures": [
{
"_id": "5373c6439058859af7904a16",
"waterDamage": false,
...
}
],
"details": [
{
"_id": "5373c6439058859af7904a14",
"year": 2013,
...
}
]
}
]
}
]
As you can see, vehicles is an array of subdocuments (vehicles) that follow a specific schema:
details: an array of details subdocs
opts: an array of opts subdocs
disclosures: an array of disclosures subdocs
For each of details, disclosures and opts I would like to return the first subdoc in the array as opposed to an array. However, I'm not sure where to start. I can't really $unwind vehicles because it is possible that it might be empty.
Here's is what my final data should look like:
[
{
"_id": "53712c7238b8d900008ef71c",
"address": [
{
"_id": "53712c7238b8d900008ef71b",
"phoneNumber": "4092348294",
...
}
],
"name": "TestDealer",
"vehicles": [
{
"_id": "53712fa138b8d900008ef720",
"createdAt": "2014-05-12T20:08:00.000Z",
"tags": [
"vehicle"
],
"opts": {
"_id": "53712fa138b8d900008ef71e",
"hasSunroof": false,
...
},
"listed": true,
"disclosures": {
"_id": "53712fa138b8d900008ef71f",
"waterDamage": false,
...
},
"details": {
"_id": "53712fa138b8d900008ef71d",
"year": 2007,
...
}
},
{
"_id": "5373c6439058859af7904a17",
"createdAt": "2014-05-14T19:17:00.000Z",
"tags": [
"vehicle"
],
"opts": {
"_id": "5373c6439058859af7904a15",
"hasSunroof": false,
....
"other": []
},
"listed": true,
"disclosures": {
"_id": "5373c6439058859af7904a16",
"waterDamage": false,
...
},
"details": {
"_id": "5373c6439058859af7904a14",
"year": 2013,
...
}
}
]
}
]
EDIT:
Here is the aggregation I do to get my data (in CoffeeScript):
{
$match:
_id: ObjectId(params.id)
}
{
$unwind: '$inventories'
}
{
$match:
'inventories._id': 'active'
}
{
$redact:
$cond:
if:
$eq:['$listed',false]
then: '$$PRUNE'
else: '$$DESCEND'
}
{
$project:
address: 1
name: '$dealerName'
email: '$_id.email'
vehicles: '$inventories.vehicles'
}
As well as the original data:
{ "_id" : ObjectId( "53712c7238b8d900008ef71c" ),
"email" : "test#test.com",
"createdAt" : Date( 1399925280000 ),
"password" : "$2a$10$3dhicJuONkXeI9mQflXF0.JftAx7mLQaY2./6a3O7xdrs5qfH2zMW",
"firstName" : "TestName",
"lastName" : "testName",
"dealerName" : "TestDealer",
"dealerType" : "Franchised Dealer",
"emailIsVerified" : false,
"lastLoggedInAt" : Date( 1400247300000 ),
"expiresAt" : Date( 4524062882000 ),
"deletedAt" : null,
"updatedAt" : Date( 1400251500000 ),
"inventories" : [
{ "title" : "activeInventory",
"_id" : "active",
"vehicles" : [
{ "_id" : ObjectId( "53712fa138b8d900008ef720" ),
"createdAt" : Date( 1399925280000 ),
"tags" : [
"vehicle" ],
"opts" : [
{ "_id" : ObjectId( "53712fa138b8d900008ef71e" ),
"hasSunroof" : false,
"hasSnowTires" : false,
"hasPowerWindows" : false,
"hasNavigationSystem" : false,
"hasManuals" : false,
"hasMags" : false,
"hasLeather" : false,
"hasKeylessEntry" : false,
"hasHeatedSeats" : false,
"hasDvdSystem" : false,
"hasAirConditioning" : false,
"has2SetsOfKeys" : false,
"other" : [] } ],
"listed" : true,
"disclosures" : [
{ "_id" : ObjectId( "53712fa138b8d900008ef71f" ),
"waterDamage" : false,
"vehicleLien" : false,
"usVehicle" : false,
"totalLossByInsurer" : false,
"theftOrRecovery" : false,
"taxiOrLimo" : false,
"structuralPartsAreDamagedOrAlteredOrRepaired" : false,
"suspensionOrSubFrameNeedsRepair" : false,
"repainted" : false,
"previousDamageExceeding3k" : false,
"powerTrainNeedsRepair" : false,
"policeCruiser" : false,
"outOfProvinceOrState" : false,
"originalOwner" : false,
"originalManufacturerVinPlate" : false,
"originalSpecificationsChanged" : false,
"odometerRolledBackOrReplaced" : false,
"manufacturerWarrantyCancelled" : false,
"manufacturerBadgesChanged" : false,
"fuelSystemNeedsRepair" : false,
"fireDamage" : false,
"engineNeedsRepair" : false,
"emergencyVehicle" : false,
"electricalSystemNeedsRepair" : false,
"dailyRental" : false,
"computerNeedsRepair" : false,
"antiLockBrakesDamagedOrInoperable" : false,
"airbagIsMissingOrInoperable" : false,
"airConditioningNeedsRepair" : false } ],
"details" : [
{ "_id" : ObjectId( "53712fa138b8d900008ef71d" ),
"year" : 2007,
"vin" : "JN8AZ08W27W649264",
"trim" : "SE",
"transmission" : "Automatic",
"price" : 132123,
"model" : "Murano 4D Utility AWD",
"mileageReading" : 125000,
"make" : "Nissan",
"interiorColor" : "Black",
"history" : "Carproof Verified ($40.00)",
"hasAccident" : false,
"fuelType" : "Biodiesel",
"exteriorColor" : "Blue",
"driveTrain" : "FWD",
"description" : "dsadas",
"cylinders" : 4,
"mileageType" : "kms" } ] },
{ "_id" : ObjectId( "53713bfc1429925f0faf79d0" ),
"createdAt" : Date( 1399929780000 ),
"tags" : [
"vehicle" ],
"opts" : [
{ "_id" : ObjectId( "53713bfc1429925f0faf79ce" ),
"hasSunroof" : false,
"hasSnowTires" : false,
"hasPowerWindows" : false,
"hasNavigationSystem" : false,
"hasManuals" : false,
"hasMags" : false,
"hasLeather" : false,
"hasKeylessEntry" : false,
"hasHeatedSeats" : false,
"hasDvdSystem" : false,
"hasAirConditioning" : false,
"has2SetsOfKeys" : false,
"other" : [] } ],
"listed" : false,
"disclosures" : [
{ "_id" : ObjectId( "53713bfc1429925f0faf79cf" ),
"waterDamage" : false,
"vehicleLien" : false,
"usVehicle" : false,
"totalLossByInsurer" : false,
"theftOrRecovery" : false,
"taxiOrLimo" : false,
"structuralPartsAreDamagedOrAlteredOrRepaired" : false,
"suspensionOrSubFrameNeedsRepair" : false,
"repainted" : false,
"previousDamageExceeding3k" : false,
"powerTrainNeedsRepair" : false,
"policeCruiser" : false,
"outOfProvinceOrState" : false,
"originalOwner" : false,
"originalManufacturerVinPlate" : false,
"originalSpecificationsChanged" : false,
"odometerRolledBackOrReplaced" : false,
"manufacturerWarrantyCancelled" : false,
"manufacturerBadgesChanged" : false,
"fuelSystemNeedsRepair" : false,
"fireDamage" : false,
"engineNeedsRepair" : false,
"emergencyVehicle" : false,
"electricalSystemNeedsRepair" : false,
"dailyRental" : false,
"computerNeedsRepair" : false,
"antiLockBrakesDamagedOrInoperable" : false,
"airbagIsMissingOrInoperable" : false,
"airConditioningNeedsRepair" : false } ],
"details" : [
{ "_id" : ObjectId( "53713bfc1429925f0faf79cd" ),
"year" : 2007,
"vin" : "JN8AZ08W27W649264",
"trim" : "SE",
"transmission" : "Manual",
"price" : 13241234,
"model" : "Murano 4D Utility AWD",
"mileageReading" : 1312312,
"make" : "Mercedes-Benz",
"interiorColor" : "Black",
"history" : "Carproof Claims ($25.00)",
"hasAccident" : true,
"fuelType" : "Diesel",
"exteriorColor" : "Black",
"driveTrain" : "RWD",
"description" : "werwe",
"cylinders" : 4,
"mileageType" : "kms" } ] },
{ "_id" : ObjectId( "5373c6439058859af7904a17" ),
"createdAt" : Date( 1400095020000 ),
"tags" : [
"vehicle" ],
"opts" : [
{ "_id" : ObjectId( "5373c6439058859af7904a15" ),
"hasSunroof" : false,
"hasSnowTires" : false,
"hasPowerWindows" : false,
"hasNavigationSystem" : false,
"hasManuals" : false,
"hasMags" : false,
"hasLeather" : false,
"hasKeylessEntry" : false,
"hasHeatedSeats" : false,
"hasDvdSystem" : false,
"hasAirConditioning" : false,
"has2SetsOfKeys" : false,
"other" : [] } ],
"listed" : true,
"disclosures" : [
{ "_id" : ObjectId( "5373c6439058859af7904a16" ),
"waterDamage" : false,
"vehicleLien" : false,
"usVehicle" : false,
"totalLossByInsurer" : false,
"theftOrRecovery" : false,
"taxiOrLimo" : false,
"structuralPartsAreDamagedOrAlteredOrRepaired" : false,
"suspensionOrSubFrameNeedsRepair" : false,
"repainted" : false,
"previousDamageExceeding3k" : false,
"powerTrainNeedsRepair" : false,
"policeCruiser" : false,
"outOfProvinceOrState" : false,
"originalOwner" : false,
"originalManufacturerVinPlate" : false,
"originalSpecificationsChanged" : false,
"odometerRolledBackOrReplaced" : false,
"manufacturerWarrantyCancelled" : false,
"manufacturerBadgesChanged" : false,
"fuelSystemNeedsRepair" : false,
"fireDamage" : false,
"engineNeedsRepair" : false,
"emergencyVehicle" : false,
"electricalSystemNeedsRepair" : false,
"dailyRental" : false,
"computerNeedsRepair" : false,
"antiLockBrakesDamagedOrInoperable" : false,
"airbagIsMissingOrInoperable" : false,
"airConditioningNeedsRepair" : false } ],
"details" : [
{ "_id" : ObjectId( "5373c6439058859af7904a14" ),
"year" : 2013,
"vin" : "12345678901234567",
"trim" : "SE",
"transmission" : "Automatic",
"price" : 13564,
"model" : "Red",
"mileageReading" : 13453,
"make" : "Daihatsu",
"interiorColor" : "Black",
"history" : "Carproof Verified ($40.00)",
"hasAccident" : true,
"fuelType" : "Diesel",
"exteriorColor" : "Black",
"driveTrain" : "FWD",
"description" : "wrw",
"cylinders" : 3,
"mileageType" : "kms" } ] } ],
"tags" : [
"inventory",
"active",
"vehicles" ] },
{ "title" : "soldInventory",
"_id" : "sold",
"vehicles" : [],
"tags" : [
"inventory",
"sold",
"vehicles" ] },
{ "title" : "deletedInventory",
"_id" : "deleted",
"vehicles" : [],
"tags" : [
"inventory",
"deleted",
"vehicles" ] } ],
"address" : [
{ "_id" : ObjectId( "53712c7238b8d900008ef71b" ),
"phoneNumber" : "4092348294",
"country" : "Canada",
"zip" : "301111",
"region" : "Quebec",
"city" : "Montreal",
"street2" : "Apt. 101",
"street1" : "1213 Street",
"additionalNumbers" : [] } ],
"__v" : 3 }

If you are using 2.6 (which you must be since you use $redact) and if the array is always guaranteed to be empty if there are no tags or opts, you can use this technique to make sure you don't lose vehicles which have empty arrays when you $unwind:
In a $project phase add the following for all arrays you want to $unwind to keep just the first element:
opts:{$cond:{if:{$eq:[{$size:"$opts"},0]}, then:{$literal:[ "none" ]}, else:"$opts"}}
For each sub-array you can now group keeping the first one, and then repeat again for the next sub-array.
You'll need to be careful about unwinding and re-grouping for each array separately to keep from ending up with any duplication, if you are not aggregating anything else, you can do it in a single group with as many $unwinds as there are sub-arrays.

Related

MongoDB Redash - FieldPath field names may not contain '.'

I am new to mongodb. I am trying to take the daily count. I want count of those element where the fields I_C or L2_C exist and for the last 3 months.
I have written this code, but it is showing error -
"FieldPath field names may not contain '.'".
What would be the correct way to go for this?
Here is the schema of the data I am trying to query on.
{
"_id" : ObjectId("5b83af839bc195a7cfbabefb"),
"firstName" : null,
"lastName" : null,
"realm" : null,
"username" : null,
"password" : "$2a$10$WVTWLevvoJXcMs/x9o/qMOEV56owh2ppjZXJt4pSR8PIDuBeRehwW",
"credentials" : null,
"challenges" : null,
"email" : "ritesh.rnagpure#gmail.com",
"emailVerified" : null,
"verificationToken" : null,
"status" : null,
"created" : null,
"lastUpdated" : ISODate("2019-08-28T12:15:07.808Z"),
"dateCreated" : ISODate("2018-08-27T08:00:02.595Z"),
"source" : "web",
"isFromAPUrl" : false,
"personal" : {
"phone" : "9975891779",
"referralcode" : "",
"iccode" : "221930",
"isiccode" : "yes",
"referrerEmail" : "zohaibrulz#gmail.com",
"referrerName" : "Momin Zohaib Hasanali",
"residency" : {
"type" : "Indian",
"status" : "Status_Resident_Individual",
"nationality" : "Nationality_Indian",
"nationalityOther" : ""
},
"isNameFromIT" : true,
"name" : "RITESH RAJENDRA NAGPURE",
"birthdate" : {
"date" : "7",
"month" : "4",
"year" : "1993"
},
"previousPan" : "AJEPN0774B",
"previousDOB" : "07/04/1993",
"address" : {
"correspondence" : {
"line1" : "Flat No-404, G Wing, Sai Satyam Residency,",
"line2" : "Near Don Bosco School, Kalyan West, Opposite Raunak city, Kalyan, Thane",
"city" : "Kalyan",
"state" : "Maharashtra",
"pincode" : "421301",
"proof" : ""
},
"permanent" : {
"line1" : "Flat No-404, G Wing, Sai Satyam Residency,",
"line2" : "Near Don Bosco School, Kalyan West, Opposite Raunak city, Kalyan, Thane",
"city" : "Kalyan",
"state" : "Maharashtra",
"pincode" : "421301",
"proof" : "",
"proofOtherText" : ""
},
"sameAsCorrespondence" : true
},
"isaddressFromQR" : false,
"isaadhar" : false,
"iskra" : false,
"fatherOrSpouseName" : "RAJENDRA",
"ipvdetails" : {
"ipvrequire" : false,
"ipvstatus" : "done"
},
"aadharNumber" : "875847348082",
"mothername" : "SAVITA",
"gender" : "male",
"polexposed" : "no",
"maritalStatus" : "single",
"incomeType" : "annual",
"income" : "below_1L",
"occupation" : "Private Sector",
"tradingExperience" : "< 1 Year",
"promocode" : "CASHBACK",
"requireddoc" : {
"ispanrequire" : true,
"isuidrequire" : true,
"ischequerequire" : true,
"isphotorequire" : true,
"iscoraddressrequire" : false,
"issignaturerequire" : true,
"isaddressrequire" : false,
"isbankstatementrequire" : true,
"isoptionaldoc" : false
},
"uploadeddoc" : {
"ispan" : true,
"isuid" : true,
"isaddress" : false,
"isphoto" : true,
"iscoraddress" : false,
"ischeque" : true,
"issignature" : true,
"isoptionaldoc" : false,
"isbankstatement" : true,
"issignaturebmp" : true
},
"docname" : {
"uid" : "5b83af839bc195a7cfbabefb_uid.pdf",
"pan" : "5b83af839bc195a7cfbabefb_pan.jpg",
"photo" : "5b83af839bc195a7cfbabefb_photo.jpg",
"address" : "",
"coraddress" : "",
"cheque" : "5b83af839bc195a7cfbabefb_cheque.pdf",
"bankstatement" : "5b83af839bc195a7cfbabefb_bankstm.pdf",
"signature" : "5b83af839bc195a7cfbabefb_sign.jpg",
"other" : "",
"optionaldoc" : ""
},
"doctype" : {
"pan" : "pan",
"uid" : "uid",
"add" : "uid",
"cheque" : "bankstatement",
"bankstm" : "bankstatement",
"photo" : "photo"
},
"isaddressmodified" : true,
"isAllEsigned" : true,
"isEsignMerged" : true,
"iskycFile" : true,
"isKycEsigned" : true,
"isApplicationCompleted" : true,
"applicationStatus" : "completed",
"isPanVerified" : true,
"panStatus" : "E",
"panName" : "RITESH RAJENDRA NAGPURE",
"rejectionReasonList" : [],
"makerAssigned" : false,
"checkerAssigned" : true,
"checkerOfficer" : "robot",
"checkerTime" : ISODate("2019-05-29T11:30:33.004Z")
},
"txn" : {
"L1_L2" : ISODate("2018-08-27T11:06:35.251Z"),
"L1_L2_officer" : "prajakta.rane",
"L2_R" : ISODate("2018-08-27T10:43:19.063Z"),
"L2_R_officer" : "avilon.pereira",
"R_L1" : ISODate("2018-08-27T10:48:51.377Z"),
"L2_I" : ISODate("2018-08-27T11:43:12.718Z"),
"L2_I_officer" : "rajkumar.kesari",
"I_C" : ISODate("2018-08-27T12:58:52.343Z"),
"I_C_officer" : "suraj.pandey"
},
"sales" : {
"openStateOfficer" : "bhaskar.geera",
"aadhaar" : false,
"assignStep" : "registration",
"assignTo" : "bhaskar.geera",
"callTime" : "2018-08-27T11:35:15.446Z",
"calledMessage" : "Ringing",
"calledStatus" : "Ringing_Not_Picked",
"iscalled" : true,
"callCount" : 5
},
"utm_params" : {},
"landingPage" : "",
"suiteID" : "b9245931-a66f-3647-11b8-5b83af881208",
"_scopeMeta" : {},
"panPageVisited" : true,
"kra" : {
"pan" : "AJEPN0774B",
"dob" : "07/04/1993",
"name" : "",
"fatherOrSpouseName" : "",
"agency" : "",
"email" : "",
"mobile" : "",
"address" : {
"correspondence" : {
"line1" : "",
"line2" : "",
"city" : "",
"state" : "",
"pincode" : "",
"proof" : ""
},
"permanent" : {
"line1" : "",
"line2" : "",
"city" : "",
"state" : "",
"pincode" : "",
"proof" : ""
}
},
"gender" : "",
"maritalStatus" : "",
"appStatus" : "005",
"appUpdateStatus" : "005",
"authMode" : "0"
},
"aadhar" : {},
"overlayOpenData" : {
"showSegmentsOverlay" : false,
"showBrokerageOverlay" : false,
"showReviewOverlay" : true,
"showVerifyOverlay" : true
},
"currentStep" : "review/verify",
"namePageVisited" : true,
"options" : {
"BSE_EQ" : true,
"NSE_EQ" : true,
"BCD_FO" : false,
"NCD_FO" : true,
"NSE_FO" : true,
"MCX" : true,
"MF" : true
},
"pricingPlan" : "BASIC-D0",
"bank" : {
"primary" : {
"ifsc" : "SBIN0017460",
"name" : "STATE BANK OF INDIA",
"address" : "MADHUBAN TIRTHDHAM COMPLEX,SHOP NO 4,ADHARWADI ROAD,KALYAN WEST,DISTT.THANE.MAHARASHTRA 421301",
"branch" : "ADHARWADI ROAD BRANCH",
"city" : "THANE",
"state" : "MAHARASHTRA",
"country" : "INDIA",
"micr" : "",
"pincode" : "421301",
"type" : "savings",
"account" : "31527410599"
}
},
"kycSplitStart" : 63,
"kycDocsLength" : 4,
"eSignProperties" : {
"signername" : "Ritesh Rajendra Nagpure"
},
"formSubmittedDate" : ISODate("2018-08-27T10:48:51.377Z"),
"ucccode" : "177762",
"crmPaymentSynced" : true,
"passwordResetCode" : "",
"lastPaymentDate" : ISODate("2018-10-26T09:55:24.149Z"),
"registrationWorkflowId" : 0,
"currentWorkflowId" : 0,
"isPaymentFromLedger" : false,
"lastLoginDate" : ISODate("2019-08-28T12:14:58.569Z"),
"viewedPreSales" : true
}
I have written this query -
"collection": "user",
"aggregate": [
{
"$match": {
"$or" : [
{
"$and" : [
{"txn.I_C" : {"$exists": true}}, {"txn.I_C" : {"$gt" : {"$humanTime" :"2019-07-01"}}}]
},
{
"$and" : [
{"txn.L2_C" : {"$exists": true}}, {"txn.L2_C" : {"$gt" : {"$humanTime" :"2019-07-01"}}}]
}
]}
},
{ "$addFields" : {"date" :{"$cond": { "if": {"txn.I_C" : {"$exists" : true}}, "then": "txn.I_C", "else": "txn.L2_C"}}}},
{
"$project" : {
"_id" : 0,
"datePartDay": {
"$concat": [
{
"$substr": [
{
"$year": "$date"
},
0,
4
]
},
"-",
{
"$substr": [
{
"$month": "$date"
},
0,
2
]
},
"-",
{
"$substr": [
{
"$dayOfMonth": "$date"
},
0,
2
]
}
]
}
}
},
{
"$group": {
"_id": "$datePartDay",
"count": {
"$sum": 1
}
}
}
]
}
Try this way, please:
"collection": "user",
"aggregate": [
{
"$match": {
"$or" : [
{
"$and" : [
{"txn.I_C" : {"$exists": true}}, {"txn.I_C" : {"$gt" : {"$humanTime" :"2019-07-01"}}}]
},
{
"$and" : [
{"txn.L2_C" : {"$exists": true}}, {"txn.L2_C" : {"$gt" : {"$humanTime" :"2019-07-01"}}}]
}
]}
},
{ "$addFields" : {"date" :{"$cond": {"if": {"$ne" : ["$txn.I_C", undefined]}, "then": "$txn.I_C", "else": "$txn.L2_C"}}}},
{
"$project" : {
"_id" : 0,
"datePartDay": {
"$concat": [
{
"$substr": [
{
"$year": "$date"
},
0,
4
]
},
"-",
{
"$substr": [
{
"$month": "$date"
},
0,
2
]
},
"-",
{
"$substr": [
{
"$dayOfMonth": "$date"
},
0,
2
]
}
]
}
}
},
{
"$group": {
"_id": "$datePartDay",
"count": {
"$sum": 1
}
}
}
]
}

mongodb update single object in array

How to update Single object in array of objects by rules query is that object must by in document that has translation_key == as input that i read and object must have same language (id) if not i must create new object . ( mongodb, mongoose)
Language are required and uniueq in translations
{
"_id" : ObjectId("5bfd5324725fb12bc4863cd8"),
"deleted" : false,
"deleted_at" : null,
"noAuth" : false,
"hidden" : false,
"translation_key" : "Standard",
"translation_type" : "text",
"translation" : [
{
"system" : {
"android" : false,
"ios" : false,
"web" : false,
"api" : false
},
"language" : ObjectId("5bf52e06edb9902e2113d8b3"),
"text":'',
"active" : false,
"prepared_string" : false,
"_id" : ObjectId("5bfd910c2998d929644abd90"),
"params" : []
},
{
"system" : {
"android" : false,
"ios" : false,
"web" : true,
"api" : false
},
"active" : false,
"prepared_string" : false,
"_id" : ObjectId("5bfdb5d22998d929644ac2af"),
"language" : ObjectId("5bf52e06edb9902e6471d8c1"),
"text" : "Standard",
"app_version" : "2.0",
"params" : []
}
],
"create_date" : ISODate("2018-11-27T14:22:28.635Z"),
"update_date" : ISODate("2018-11-27T14:22:28.635Z"),
"__v" : 3
}
Model.findOneAndUpdate(
{
_id: "document_id",
"translation.language": "languageId",
},
{
$set: {
"translation.$.text: "text",
"translation.$.active": "status",
"translation.$.system.android" : true
},
{ new: true, upsert: true, }
)
.lean();

Understanding explain() to optimize complex queries

I have a aggregate pipeline that takes over 5 seconds to return 200 rows.
I'm trying to optimize my current pipeline using explain("executionStats").
This is my pipeline:
db.getCollection("content_topics").explain("executionStats").aggregate([{
"$lookup": {
from: "users",
localField: "creator",
foreignField: "_id",
as: "user"
}
},
{
$unwind: "$user"
},
{
"$match": {
created_at: {
"$gte": 1528914600,
"$lte": 1534271400
},
dash_status: 3,
language: "hi",
parent_topic_id: {
"$eq": null
},
status: 1,
"user.device_os": {
"$ne": "BOT"
}
}
},
{
"$sort": {
created_at: -1
}
},
{
"$addFields": {
user_handle: "$user.handle",
user_phone: "$user.phone",
user_status: "$user.status"
}
},
{
"$project": {
topic_id: 1,
n_vokes: 1,
message: 1,
title: 1,
language: 1,
description: 1,
voice_desc: 1,
image: 1,
image_share: 1,
hashtag: 1,
location: 1,
default_text: 1,
creator: 1,
created_at: 1,
status: 1,
ref_id: 1,
weightage: 1,
username: 1,
slug_generated: 1,
user_handle: 1,
user_phone: 1,
user_status: 1
}
},
{
"$skip": 0
},
{
"$limit": 200
}
]);
And this is what mongo explain()ed:
{
"stages" : [
{
"$cursor" : {
"query" : {
"$and" : [
{
"created_at" : {
"$gte" : 1528914600.0
}
},
{
"created_at" : {
"$lte" : 1534271400.0
}
},
{
"dash_status" : {
"$eq" : 3.0
}
},
{
"language" : {
"$eq" : "hi"
}
},
{
"parent_topic_id" : {
"$eq" : null
}
},
{
"status" : {
"$eq" : 1.0
}
}
]
},
"fields" : {
"created_at" : 1.0,
"creator" : 1.0,
"default_text" : 1.0,
"description" : 1.0,
"hashtag" : 1.0,
"image" : 1.0,
"image_share" : 1.0,
"language" : 1.0,
"location" : 1.0,
"message" : 1.0,
"n_vokes" : 1.0,
"ref_id" : 1.0,
"slug_generated" : 1.0,
"status" : 1.0,
"title" : 1.0,
"topic_id" : 1.0,
"user.handle" : 1.0,
"user.phone" : 1.0,
"user.status" : 1.0,
"user_handle" : 1.0,
"user_phone" : 1.0,
"user_status" : 1.0,
"username" : 1.0,
"voice_desc" : 1.0,
"weightage" : 1.0,
"_id" : 1.0
},
"queryPlanner" : {
"plannerVersion" : 1.0,
"namespace" : "vokalapp.content_topics",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"dash_status" : {
"$eq" : 3.0
}
},
{
"language" : {
"$eq" : "hi"
}
},
{
"parent_topic_id" : {
"$eq" : null
}
},
{
"status" : {
"$eq" : 1.0
}
},
{
"created_at" : {
"$lte" : 1534271400.0
}
},
{
"created_at" : {
"$gte" : 1528914600.0
}
}
]
},
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"parent_topic_id" : {
"$eq" : null
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"dash_status" : 1.0,
"status" : 1.0,
"language" : 1.0,
"parent_topic_id" : 1.0,
"created_at" : 1.0
},
"indexName" : "index_for_dashboard",
"isMultiKey" : false,
"multiKeyPaths" : {
"dash_status" : [
],
"status" : [
],
"language" : [
],
"parent_topic_id" : [
],
"created_at" : [
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"dash_status" : [
"[3.0, 3.0]"
],
"status" : [
"[1.0, 1.0]"
],
"language" : [
"[\"hi\", \"hi\"]"
],
"parent_topic_id" : [
"[null, null]"
],
"created_at" : [
"[1528914600.0, 1534271400.0]"
]
}
}
},
"rejectedPlans" : [
{
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"dash_status" : {
"$eq" : 3.0
}
},
{
"language" : {
"$eq" : "hi"
}
},
{
"parent_topic_id" : {
"$eq" : null
}
},
{
"status" : {
"$eq" : 1.0
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"created_at" : 1.0
},
"indexName" : "created_at_index",
"isMultiKey" : false,
"multiKeyPaths" : {
"created_at" : [
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"created_at" : [
"[1528914600.0, 1534271400.0]"
]
}
}
},
{
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"dash_status" : {
"$eq" : 3.0
}
},
{
"parent_topic_id" : {
"$eq" : null
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"language" : 1.0,
"status" : 1.0,
"created_at" : 1.0,
"answers.type" : 1.0
},
"indexName" : "language_status_created_at_answer_type_index",
"isMultiKey" : true,
"multiKeyPaths" : {
"language" : [
],
"status" : [
],
"created_at" : [
],
"answers.type" : [
"answers"
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"language" : [
"[\"hi\", \"hi\"]"
],
"status" : [
"[1.0, 1.0]"
],
"created_at" : [
"[1528914600.0, 1534271400.0]"
],
"answers.type" : [
"[MinKey, MaxKey]"
]
}
}
},
{
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"dash_status" : {
"$eq" : 3.0
}
},
{
"parent_topic_id" : {
"$eq" : null
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"language" : -1.0,
"status" : -1.0,
"weightage" : -1.0,
"created_at" : -1.0
},
"indexName" : "language_status_weightage_created_at_index",
"isMultiKey" : false,
"multiKeyPaths" : {
"language" : [
],
"status" : [
],
"weightage" : [
],
"created_at" : [
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"language" : [
"[\"hi\", \"hi\"]"
],
"status" : [
"[1.0, 1.0]"
],
"weightage" : [
"[MaxKey, MinKey]"
],
"created_at" : [
"[1534271400.0, 1528914600.0]"
]
}
}
}
]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 7072.0,
"executionTimeMillis" : 984.0,
"totalKeysExamined" : 7072.0,
"totalDocsExamined" : 7072.0,
"executionStages" : {
"stage" : "FETCH",
"filter" : {
"parent_topic_id" : {
"$eq" : null
}
},
"nReturned" : 7072.0,
"executionTimeMillisEstimate" : 40.0,
"works" : 7073.0,
"advanced" : 7072.0,
"needTime" : 0.0,
"needYield" : 0.0,
"saveState" : 63.0,
"restoreState" : 63.0,
"isEOF" : 1.0,
"invalidates" : 0.0,
"docsExamined" : 7072.0,
"alreadyHasObj" : 0.0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 7072.0,
"executionTimeMillisEstimate" : 0.0,
"works" : 7073.0,
"advanced" : 7072.0,
"needTime" : 0.0,
"needYield" : 0.0,
"saveState" : 63.0,
"restoreState" : 63.0,
"isEOF" : 1.0,
"invalidates" : 0.0,
"keyPattern" : {
"dash_status" : 1.0,
"status" : 1.0,
"language" : 1.0,
"parent_topic_id" : 1.0,
"created_at" : 1.0
},
"indexName" : "index_for_dashboard",
"isMultiKey" : false,
"multiKeyPaths" : {
"dash_status" : [
],
"status" : [
],
"language" : [
],
"parent_topic_id" : [
],
"created_at" : [
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"dash_status" : [
"[3.0, 3.0]"
],
"status" : [
"[1.0, 1.0]"
],
"language" : [
"[\"hi\", \"hi\"]"
],
"parent_topic_id" : [
"[null, null]"
],
"created_at" : [
"[1528914600.0, 1534271400.0]"
]
},
"keysExamined" : 7072.0,
"seeks" : 1.0,
"dupsTested" : 0.0,
"dupsDropped" : 0.0,
"seenInvalidated" : 0.0
}
}
}
}
},
{
"$lookup" : {
"from" : "users",
"as" : "user",
"localField" : "creator",
"foreignField" : "_id",
"unwinding" : {
"preserveNullAndEmptyArrays" : false
},
"matching" : {
"$nor" : [
{
"device_os" : {
"$eq" : "BOT"
}
}
]
}
}
},
{
"$sort" : {
"sortKey" : {
"created_at" : -1.0
},
"limit" : NumberLong(200)
}
},
{
"$addFields" : {
"user_handle" : "$user.handle",
"user_phone" : "$user.phone",
"user_status" : "$user.status"
}
},
{
"$project" : {
"_id" : true,
"hashtag" : true,
"voice_desc" : true,
"description" : true,
"location" : true,
"language" : true,
"topic_id" : true,
"image" : true,
"slug_generated" : true,
"n_vokes" : true,
"ref_id" : true,
"image_share" : true,
"message" : true,
"created_at" : true,
"default_text" : true,
"weightage" : true,
"user_handle" : true,
"title" : true,
"status" : true,
"creator" : true,
"user_status" : true,
"username" : true,
"user_phone" : true
}
}
],
"ok" : 1.0
}
This is after I created the index "index_for_dashboard" with the keys that I use in "$match". I also created an index for "device_os" from user collection. But to no dice, no improvements in the response time.
Possible culprits:
Is $lookup expensive. If it is can I fetch only the fields I need.
Can the indexing be done better. Should I use a different set of
fields?
Is $addField expensive. If it is I can offload it to
application level.
How should I troubleshoot(and understand) with the explain result to help myself optimize the query?
I can't make an educated call here, need some directions.
First, you want to make sure that you are running at least v3.6.3 of MongoDB because in v3.6 there's a new feature that allows $lookup to specify sub-pipelines. Those pipelines can actually use indexes, however, there was a bug which only got fixed in the version mentioned above.
The following should be as fast as things can get here:
db.getCollection("content_topics").createIndex({ created_at: -1, dash_status: 1, language: 1, parent_topic_id: 1, status: 1 }); // this index will get used by the main $match and the $sort stage
db.getCollection("users").createIndex({ device_os: 1 }); // this index will get used by the sub-pipeline in $lookup
db.getCollection("content_topics").aggregate([{
"$match": { // filter at the start in order to be able to use indexes
created_at: {
"$gte": 1528914600,
"$lte": 1534271400
},
dash_status: 3,
language: "hi",
parent_topic_id: {
"$eq": null
},
status: 1
// see the below $lookup stage in case you're wondering where the user filter went
}
},
{
"$sort": {
created_at: -1 // sort straight away so the index can be used
}
},
{
"$lookup": {
from: "users",
let: { "creator": "$creator" },
pipeline: [{ // use new v3.6 pipeline syntax to be able to leverage indexes
$match: {
$expr: {
$and: [
{ $eq: [ "$_id", "$$creator" ] },
{ $ne: [ "$device_os", "BOT" ] } // here is the "device_os" filter inside the pipeline so index can be used
]
}
}
}, {
$project: {
_id: 0, // "_id" field is not needed
user_handle: 1, // only those fields are of interest
user_phone: 1,
user_status: 1
}
}],
as: "user"
}
},
{
$unwind: "$user"
},
{
"$project": {
topic_id: 1,
n_vokes: 1,
message: 1,
title: 1,
language: 1,
description: 1,
voice_desc: 1,
image: 1,
image_share: 1,
hashtag: 1,
location: 1,
default_text: 1,
creator: 1,
created_at: 1,
status: 1,
ref_id: 1,
weightage: 1,
username: 1,
slug_generated: 1,
user_handle: "$user.handle", // no extra $addFields stage needed
user_phone: "$user.phone", // same here
user_status: "$user.status" // and here
}
},
{
"$skip": 0
},
{
"$limit": 200
}
]);

Why is mongodb not using full index

I have a collection with one 4 key compound index:
> db.event.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
},
{
"v" : 2,
"key" : {
"epochWID" : 1,
"category" : 1,
"mos.types" : 1,
"mos.name" : 1
},
"name" : "epochWID_category_motype_movalue",
}
]
Query is as follows:
> db.event.explain().find({ "epochWID": 1510456188087, "category": 6, "mos.types": 9, "mos.name": "ctx_1" })
{
"queryPlanner" : {
"plannerVersion" : 1,
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"category" : {
"$eq" : 6
}
},
{
"epochWID" : {
"$eq" : 1510456188087
}
},
{
"mos.name" : {
"$eq" : "ctx_1"
}
},
{
"mos.types" : {
"$eq" : 9
}
}
]
},
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"mos.name" : {
"$eq" : "ctx_1"
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"epochWID" : 1,
"category" : 1,
"mos.types" : 1,
"mos.name" : 1
},
"indexName" : "epochWID_category_motype_movalue",
"isMultiKey" : true,
"multiKeyPaths" : {
"epochWID" : [ ],
"category" : [ ],
"mos.types" : [
"mos",
"mos.types"
],
"mos.name" : [
"mos"
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"epochWID" : [
"[1510456188087.0, 1510456188087.0]"
],
"category" : [
"[6.0, 6.0]"
],
"mos.types" : [
"[9.0, 9.0]"
],
"mos.name" : [
"[MinKey, MaxKey]"
]
}
}
},
"rejectedPlans" : [ ]
},
"serverInfo" : {
"version" : "3.4.9",
},
"ok" : 1
}
Now if you look at the plan's indexBounds: it uses the first 3 keys but not the 4th mos.name, why?
"indexBounds" : {
"epochWID" : [
"[1510456188087.0, 1510456188087.0]"
],
"category" : [
"[6.0, 6.0]"
],
"mos.types" : [
"[9.0, 9.0]"
],
"mos.name" : [
"[MinKey, MaxKey]"
]
}
Based on https://docs.mongodb.com/manual/core/index-multikey/#compound-multikey-indexes we need to use $elemMatch, so following query uses the full index
> db.event.explain().find({ "epochWID": 1510456188087, "category": 6, "mos": { $elemMatch: {"types": 9, "name": "ctx_1"} } })
{
"queryPlanner" : {
"plannerVersion" : 1,
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"mos" : {
"$elemMatch" : {
"$and" : [
{
"name" : {
"$eq" : "ctx_1"
}
},
{
"types" : {
"$eq" : 9
}
}
]
}
}
},
{
"category" : {
"$eq" : 6
}
},
{
"epochWID" : {
"$eq" : 1510456188087
}
}
]
},
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"mos" : {
"$elemMatch" : {
"$and" : [
{
"types" : {
"$eq" : 9
}
},
{
"name" : {
"$eq" : "ctx_1"
}
}
]
}
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"epochWID" : 1,
"category" : 1,
"mos.types" : 1,
"mos.name" : 1
},
"indexName" : "epochWID_category_motype_movalue",
"isMultiKey" : true,
"multiKeyPaths" : {
"epochWID" : [ ],
"category" : [ ],
"mos.types" : [
"mos",
"mos.types"
],
"mos.name" : [
"mos"
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"epochWID" : [
"[1510456188087.0, 1510456188087.0]"
],
"category" : [
"[6.0, 6.0]"
],
"mos.types" : [
"[9.0, 9.0]"
],
"mos.name" : [
"[\"ctx_1\", \"ctx_1\"]"
]
}
}
},
"rejectedPlans" : [ ]
},
"serverInfo" : {
"version" : "3.4.9",
},
"ok" : 1
}
EDIT: I contacted MongoDb support. Regarding multi key indexes and array fields - tl;dr is - An index is fine as long as only one of the indexed fields ever contains an array value (which is true in my case). Nesting level doesn't matter. The problem is indeed parallel arrays due to need of cartesian product.
A multi-key index cannot be across multiple arrays within a document. See the Limitations and reasoning in the documentation https://docs.mongodb.com/manual/core/index-multikey/#compound-multikey-indexes

Search Mongo Document based on BSON DateTime

My mongo document looks like below -
{
"_id" : ObjectId("5977dab66ea64e345288ceec"),
"transactionid" : "1",
"servicename" : "Reservation Service",
"starttime" : {
"dateTime" : true,
"symbol" : false,
"javaScriptWithScope" : false,
"string" : false,
"regularExpression" : false,
"javaScript" : false,
"double" : false,
"document" : false,
"DBPointer" : false,
"number" : false,
"boolean" : false,
"null" : false,
"bsonType" : "DATE_TIME",
"array" : false,
"int32" : false,
"int64" : false,
"binary" : false,
"value" : NumberLong(1501026996822),
"decimal128" : false,
"objectId" : false,
"timestamp" : false
},
"endtime" : {
"dateTime" : true,
"symbol" : false,
"javaScriptWithScope" : false,
"string" : false,
"regularExpression" : false,
"javaScript" : false,
"double" : false,
"document" : false,
"DBPointer" : false,
"number" : false,
"boolean" : false,
"null" : false,
"bsonType" : "DATE_TIME",
"array" : false,
"int32" : false,
"int64" : false,
"binary" : false,
"value" : NumberLong(1501026997875),
"decimal128" : false,
"objectId" : false,
"timestamp" : false
},
"status" : "Success",
"updatedby" : "muf",
"requestpayload" : "{\n \"service_header\": {\n \t\n \n \t\"service_name\": \"Reservation Service\",\n \t\"source_id\": \"Amex\",\n \t \t\"document_id\":\"1\",\n \t\"transaction_id\":\"1\"\n },\n \t\n \t\"airline\":\"united\",\n\t\"destination\":\"LAX\"\n\n\n}",
"responsepayload" : "[{\"departureDate\":\"2015/02/11\",\"airlineName\":\"United\",\"destination\":\"LAX\",\"price\":345.99,\"planeType\":\"Boeing 737\",\"code\":\"ER45if\",\"origin\":\"MUA\",\"emptySeats\":52},{\"departureDate\":\"2015/04/11\",\"airlineName\":\"United\",\"destination\":\"LAX\",\"price\":346.0,\"planeType\":\"Boeing 777\",\"code\":\"ER45jd\",\"origin\":\"MUA\",\"emptySeats\":12},{\"departureDate\":\"2015/06/11\",\"airlineName\":\"United\",\"destination\":\"LAX\",\"price\":423.0,\"planeType\":\"Boeing 707\",\"code\":\"ER0945\",\"origin\":\"MUA\",\"emptySeats\":0}]",
"exception" : "",
"requesthost" : "localhost",
"requestport" : "8086",
"requestpath" : "/flights"
}
I am trying to query below document using below query -
db.getCollection('Transaction').find({ starttime: { "$gt" : { "$date" : (new ISODate("2018-07-25T23:56:36.822Z")*1) } } })
I do get the result. However when I try to use less than and greater than together I don't get any result. I am not sure what is best way to query above document.
db.getCollection('Transaction').find({ starttime: { "$gt" : { "$date" : (new ISODate("2015-07-25T23:56:36.822Z")*1) } , "$lt" : { "$date" : (new ISODate("2018-07-25T23:56:36.822Z")*1) } } })