Mongodb find comparing diffrent elements from the same array - arrays

I have an array with say two elements, want to compare element[0]and element[1], return true if element[0].code== element[1].code
"SourceArray" : [
{
"source" : "Acetone",
"code" : "90915"
},
{
"source" : "Ketone",
"code" : "90915"
}

Use $expr with aggregation expressions, like:
db.collection.find({
$expr: {
$eq: [
"$SourceArray.0.code",
"$SourceArray.1.code"
]
}
})

Related

Update nested array in a mongo document

I have below collection
{
"_id" : ObjectId("5d7e6c54c23c210001108556"),
“parentId" : "5d0162ba69cf130001a16115",
"tasks" : [
{
"_id" : "ae60a8f1",
"taskMetaDataIds” : [
ObjectId("5d55a648e2f7320001e578ac")
],
"name" : “meta 3"
},
{
"_id" : "07544d96",
"taskMetaDataIds" : [
ObjectId("5d55a648e2f732676676676”),
ObjectId("5d55a648e2612333556888”)
],
"name" : “meta 2"
},
],
"name" : “New Topic",
"createdBy" : "01526151-8303-450f-b08b-b36a1760b774",
"createdDate" : ISODate("2019-09-15T16:52:36.150+0000"),
"updatedDate" : ISODate("2019-09-15T16:52:36.150+0000")
}
I am looking for below output . Is there an operator which can directly convert the array of objects into array of strings as shown below. I can do it with a script by looping over the taskMetaDataIds array but I am looking to use a direct mongo operator which suits my purpose.
{
"_id" : ObjectId("5d7e6c54c23c210001108556"),
“parentId" : "5d0162ba69cf130001a16115",
"tasks" : [
{
"_id" : "ae60a8f1",
"taskMetaDataIds” : [
"5d55a648e2f7320001e578ac"
],
"name" : “meta 3"
},
{
"_id" : "07544d96",
"taskMetaDataIds" : [
"5d55a648e2f732676676676”,
“5d55a648e2612333556888”
],
"name" : “meta 2"
},
],
"name" : “New Topic",
"createdBy" : "01526151-8303-450f-b08b-b36a1760b774",
"createdDate" : ISODate("2019-09-15T16:52:36.150+0000"),
"updatedDate" : ISODate("2019-09-15T16:52:36.150+0000")
}
I tried below but it does not seem to be working-
db.getCollection("data").updateOne({"_id":ObjectId("5d7e6c54c23c210001108556")}, [{ $set: { "tasks.taskMetaDataIds": "$tasks.taskMetaDataIds.str" } }])
$set - Set tasks array field.
1.1. $map - Iterate each element in the tasks array and return a new array.
1.1.1. $mergeObjects - Merge the current iterated task object and taskMetaDataIds array (from the result 1.1.1.1).
1.1.1.1. $map - Iterate the current iterated task object's taskMetaDataIds array and returns a new array.
1.1.1.1.1. $toString - Convert each iterate id from ObjectId type to string type.
db.getCollection("data").updateOne({
"_id": ObjectId("5d7e6c54c23c210001108556")
},
[
{
$set: {
"tasks": {
$map: {
input: "$tasks",
as: "task",
in: {
$mergeObjects: [
"$$task",
{
"taskMetaDataIds": {
$map: {
input: "$$task.taskMetaDataIds",
in: {
$toString: "$$this"
}
}
}
}
]
}
}
}
}
}
])
Demo # Mongo Playground

Compare array within objects in an array of the same Mongo document

I have a Mongo database with multiple documents and they all contain 2 Items for in store and online locations. The difference will be that attributes within the items object may differ. I am trying to capture all documents that have differing attributes between the object items with the array.
I've tried using $expr and comparing the position of the elements such as "Items.0.Attributes" == "Items.1.Attributes" but had no luck.
{
"ID" : "123456789",
"Items" : [
{
"ItemDept" : "softLines",
"ProductId" : {
"Name" : "shirts",
"_id" : "12345Shirts"
},
"Attributes" : [ "blue","small","mens"],
"Season" : "Summer"
"Location":"online"
}
,
{
"ItemDept" : "softlines",
"ProductId" : {
"Name" : "shirts",
"_id" : "12345Shirts")
},
"Attributes" : [ "blue","small","women"],
"Season" : "Summer"
"Location":"stores"
}
]
}
If I understand what you want to find/output, here's one way you could do it.
db.collection.find({
"$expr": {
"$not": {
"$setEquals": [
{"$first": "$Items.Attributes"},
{"$last": "$Items.Attributes"}
]
}
}
})
Try it on mongoplayground.net.

$lte not working as expected even after number String converted to Int in MongoDB

Since $lte and $gt does a lexicographical comparison if the value is in string format, I have tried to convert the values to int or double. But I am not getting the expected results. For this case i want the query to execute to 2 as only 2 documents are present with the "userDL" field value less than or equal to 5. Please note: I cannot use aggregate and I have to use db.collection.find(query).count(). Please help me here with the query.
Collection content:
{ "_id" : "14_0", "data" : [ { "dn" : "subdata=data_a", "userUL" : "0", "objectClass" : "NEWDATA", "userDL" : "5" } ] }
{ "_id" : "15_0", "data" : [ { "dn" : "subdata=data_b", "userUL" : "0", "objectClass" : "NEWDATA", "userDL" : "3" } ] }
{ "_id" : "16_0", "data" : [ { "dn" : "subdata=data_c", "userUL" : "0", "objectClass" : "NEWDATA", "userDL" : "9" } ] }
1st, I tried normal querying which gave me unsupported conversion error:
db.testcol.find({ $expr: { $lte: [ { $toDouble: "$data.userDL" }, 5 ] } }).count()
2020-09-18T06:26:37.010+0530 E QUERY [js] uncaught exception: Error: count failed: {
"ok" : 0,
"errmsg" : "Unsupported conversion from array to double in $convert with no onError value",
"code" : 241,
"codeName" : "ConversionFailure"
} :
_getErrorWithCode#src/mongo/shell/utils.js:25:13
DBQuery.prototype.count#src/mongo/shell/query.js:376:11
#(shell):1:1
The above error i fixed in the below query but i am using aggregate. This works fine in terms of comparison and outputs true/false.
db.testcol.aggregate([{ $project:{ adjustedGrades:{$map:{input: "$data.userDL",as: "grade",in: {$lte : [{ $toInt: "$$grade" },5] } }}}}])
{ "_id" : "14_0", "adjustedGrades" : [ true ] }
{ "_id" : "15_0", "adjustedGrades" : [ true ] }
{ "_id" : "16_0", "adjustedGrades" : [ false ] }
I tried using the above aggregate inside find query but either i am not getting any output or the wrong output(i am getting 3 instead to 2)
db.testcol.find({ $expr: { $lte: [ {$map:{input: "$data.userDL",as: "grade",in: { $toInt: "$$grade" } }} , 5 ] } })
no output here
db.testcol.find({$expr: {$map: {input: "$data.userDL",as: "grade",in: {$lte : [{ $toInt: "$$grade" },5] } }}}).count()
3
[UPDATE]
> db.testcol.aggregate([{ $project:{ adjustedGrades:{$map:{input: "$data.userDL",as: "grade",in: {$lte : [{ $toInt: "$$grade" },5] } }}}}, {$match: {adjustedGrades: {$eq: true}}}])
{ "_id" : "14_0", "adjustedGrades" : [ true ] }
{ "_id" : "15_0", "adjustedGrades" : [ true ] }
I want to able able to do this using db.collection.find(). Can someone help here? I don't actually need the adjustedGrades field. I want these 2 documents to be returned when I do db.testcol.find(aggregatequery). Finally I want to be able to do db.testcol.find(aggregatequery).count().
PS: I have to use find() and count() only.
An example of using $reduce to iterate the array and test each element:
db.collection.find({
$expr: {
$reduce: {
input: "$data",
initialValue: false,
in: {
$or: [
"$$value",
{$lte: [{$toDouble: "$$this.userDL"}, 5]}
]
}
}
}
})
Playground
Note that using $expr in this way will not be able to use an index.
If it is permissible to modify the documents, updating so that each userDL is numeric will permit both querying directly and indexing that field.
MongoDB Enterprise mongos> db.foo.insert({a:'20',b:[{c:'20'}]})
WriteResult({ "nInserted" : 1 })
MongoDB Enterprise mongos> db.foo.find({$expr:{$gte:[{$convert:{input:'$a',to:'int'}},30]}})
MongoDB Enterprise mongos> db.foo.find({$expr:{$gte:[{$convert:{input:'$a',to:'int'}},3]}})
{ "_id" : ObjectId("5f64632b24059b0e3ee998b8"), "a" : "20", "b" : [ { "c" : "20" } ] }
This should work:
db.getCollection("myCollection").find({ "data.0.userDL": { $lte: "5" } }, {}).count()

Query collection containing BsonArray

Sorry, first time trying mongo.
Given the following data ...
db.masterList.findOne()
{
"_id" : ObjectId("59d128805b19310ac8ab3fc2"),
"MasterDefinition" : {
"Location" : [
"Whole House",
"Master Bedroom",
"Hallway 2"
],
"DeviceType" : [
"Receptacle",
"GFI",
"LED dimmer"
],
"Style" : [
"Decora",
"Standard"
],
"Color" : [
"White",
"Light Almond"
]
}
}
How do I retrieve the contents of the Color array? I expect something like
["White","Light Almond"]
How do I list the 4 arrays directly subordinate to MasterDefintion? I expect to see
["Location","DeviceType","Style","Color"]
Thanks
For the first part, you can simply do a
collection.aggregate({
$project: {
"_id": 0, // exclude the "_id" field from the result
"result": "$MasterDefinition.Color"
}
})
The second part requires a little magic (documentation can be found here: aggregation framework, $project, $objectToArray):
collection.aggregate({
$project: {
"temp": {
$objectToArray: "$MasterDefinition" // transform the "MasterDefinition" subdocument into an array
}
}
}, {
$project:{
"_id": 0, // do not include the "_id" field in the result - this is an optional step
"result": "$temp.k" // only get the keys (as in "k" fiels) from the array
}
})
How do I retrieve the contents of the Color array? I expect something like ["White","Light Almond"]
// the first argument here is a filter, the second argument is a projection
// since you specified no filter I have only included a projection
// this projection tells MongoDB to return the Color subdocument
// from within the MasterDefinition sub document
db.getCollection('masterList').find({}, {'MasterDefinition.Color': 1})
The above command will return:
{
"_id" : ObjectId("59d128805b19310ac8ab3fc2"),
"MasterDefinition" : {
"Color" : [
"White",
"Light Almond"
]
}
}
How do I list the 4 arrays directly subordinate to MasterDefintion? I expect to see ["Location","DeviceType","Style","Color"]
This is a bit trickier because "Location","DeviceType","Style","Color" are not elements in an array instead they are the names of attributes in the MasterDefinition subdocument. You can use the $objectToArray aggregation operator to turn these attribute names into an array but the resulting document doesn't looks exactly like what you hoped for. Here's an example ...
db.getCollection('masterList').aggregate([
// creates an array named "categories" from the attributes of the MasterDefinition sub document
{ $project: { categories: { $objectToArray: "$MasterDefinition" } } },
// projects on the keys of the "categories" array
{$project: {'categories.k': 1}}
])
... which produces this output:
{
"_id" : ObjectId("59d128805b19310ac8ab3fc2"),
"categories" : [
{
"k" : "Location"
},
{
"k" : "DeviceType"
},
{
"k" : "Style"
},
{
"k" : "Color"
}
]
}

MongoDB: Find all matched array element from single document

I have a mongodb document like this,
{
"_id" : ObjectId("4e8ae86d08101908e1000001"),
"eId": 101,
"about": "test",
"tags" : [
{"name": "jana"},
{"name":"bala"},
{"name":"jk"},
{"name":"charles"}
]
}
I need to find all matched array elements, where the name matched with given array.
db.coll.find({"tags": {"$elemMatch": {"name": {"$in": [/^jana/i, /^charles/i] }}}})
for this query i got the following result
{
"_id" : ObjectId("4e8ae86d08101908e1000001"),
"tags" : [
{
"name" : "jana"
}
]
}
$elemMatch query only return the first matched element, but i want all the matched array element like this,
{
"_id" : ObjectId("4e8ae86d08101908e1000001"),
"tags" : [
{
"name" : "jana"
},
{
"name" : "charles"
}
]
}
Is it possible to get the result like this?
note: i don't want any others fields, I want only the matched array elements along with _id
You can use MongoDB Aggregation Pipeline:
db.coll.aggregate([
{'$unwind': '$tags'},
{'$match':
{"tags.name":
{"$in": [/^jana/, /^charles/i] }
}
},
{'$group':
{
'_id': '$_id',
'tags':
{'$push': '$tags'}
}
}
])
Result : -
{
"result" : [
{
"_id" : ObjectId("5538b214706a90c718f75a41"),
"tags" : [
{
"name" : "jana"
},
{
"name" : "charles"
}
]
}
],
"ok" : 1
}

Resources