Mongoose find by array of ids, but with repetitive results - mongodb

Consider we have this array of IDs, which contains repetitive values.
[
'A',
'B',
'C',
'C',
'C',
'D',
.
.
.
]
How can I select the match for each ID from the DB? I don't need unique rows. I mean the result array must contain three same rows for ID C. One to one pointing. thanks.

It's better to do it on the client-side since you may just create lots of duplicated data on server and return to client.
db.collection.aggregate([
{
"$match": {
_id: {
"$in": [ "A", "B", "C", "C", "C", "D" ]
}
}
},
{
"$group": {
"_id": null,
"fields": {
"$push": "$$ROOT"
}
}
},
{
"$set": {
"fields": {
"$map": {
"input": [ "A", "B", "C", "C", "C", "D" ],
"as": "id",
"in": {
$let: {
vars: {
"findOne": {
"$first": {
"$filter": {
"input": "$fields",
"as": "item",
"cond": {
"$eq": [ "$$item._id", "$$id" ]
}
}
}
}
},
in: {
"$cond": {
"if": "$$findOne",
"then": "$$findOne",
"else": { _id: "$$id" }
}
}
}
}
}
}
}
},
{
"$unwind": "$fields"
},
{
"$replaceRoot": { "newRoot": "$fields" }
}
])
mongoplayground

Related

Remove multiple inconsistent objects from double nested array 5

here is another challenge:
I need to clean my data from incorrect objects , objects under the array "t" that contain did , dst and den fields are considered correct , #nimrok serok / #rickhg12hs helped with a working solution , but still there is some edge cases where none of objects are valid and stay empty array after the update , so I am wondering if those can be cleared in same update query?
example document:
{
"_id": ObjectId("5c05984246a0201286d4b57a"),
f: "x",
"_a": [
{
"_onlineStore": {}
},
{
"_p": {
"pid": 1,
"s": {
"a": {
"t": [
{
id: 1,
"dateP": "20200-09-20",
did: "x",
dst: "y",
den: "z"
},
{
id: 2,
"dateP": "20200-09-20"
}
]
},
"c": {
"t": [
{
id: 3,
"dateP": "20300-09-22"
},
{
id: 4,
"dateP": "20300-09-23",
}
]
}
},
h: "This must stay"
}
},
{
"_p": {
"pid": 2,
"s": {
"a": {
"t": [
{
id: 1,
"dateP": "20200-09-20",
}
]
},
"c": {
"t": [
{
id: 3,
"dateP": "20300-09-22"
},
{
id: 4,
"dateP": "20300-09-23",
}
]
}
},
h: "This must stay"
}
},
{
x: "This must stay"
}
]
}
Expected output:
{
"_a": [
{
"_onlineStore": {}
},
{
"_p": {
"h": "This must stay",
"pid": 1,
"s": {
"a": {
"t": [
{
"dateP": "20200-09-20",
"den": "z",
"did": "x",
"dst": "y",
"id": 1
}
]
}
}
}
},
{
"_p": {
"h": "This must stay",
"pid": 2,
}
},
{
"x": "This must stay"
}
],
"_id": ObjectId("5c05984246a0201286d4b57a"),
"f": "x"
}
Playground
(As you can see in the playground example , job is almost done , just for cases where all array elements are wrong the array stay empty , so it need to be removed as well ...)
mongodb version 4.4
It touk me some time , but here is the solution for those who face similar problem:
db.collection.update({},
[
{
"$set": {
_a2: {
$filter: {
input: "$_a",
as: "elem",
cond: {
"$eq": [
{
"$type": "$$elem._p.s"
},
"missing"
]
}
}
},
_a: {
$filter: {
input: "$_a",
as: "elem",
cond: {
"$ne": [
{
"$type": "$$elem._p.s"
},
"missing"
]
}
}
}
}
},
{
"$set": {
"_a": {
"$map": {
"input": "$_a",
"as": "elem",
"in": {
"$mergeObjects": [
"$$elem",
{
"_p": {
"$mergeObjects": [
"$$elem._p",
{
s: {
"$arrayToObject": {
"$map": {
"input": {
"$objectToArray": "$$elem._p.s"
},
"as": "anyKey",
"in": {
"k": "$$anyKey.k",
"v": {
"t": {
"$filter": {
"input": "$$anyKey.v.t",
"as": "t",
"cond": {
"$setIsSubset": [
[
"did",
"dst",
"den"
],
{
"$map": {
"input": {
"$objectToArray": "$$t"
},
"in": "$$this.k"
}
}
]
}
}
}
}
}
}
}
}
}
]
}
}
]
}
}
}
}
},
{
"$set": {
"_a": {
"$map": {
"input": "$_a",
"as": "elem",
"in": {
"$mergeObjects": [
"$$elem",
{
"_p": {
"$mergeObjects": [
"$$elem._p",
{
s: {
"$arrayToObject": {
"$filter": {
"input": {
"$objectToArray": "$$elem._p.s"
},
"as": "anyKey",
"cond": {
$ne: [
"$$anyKey.v.t",
[]
]
}
}
}
}
}
]
}
}
]
}
}
}
}
},
{
"$set": {
"_a": {
"$map": {
"input": "$_a",
"as": "elem",
"in": {
"$mergeObjects": [
"$$elem",
{
"_p": {
"$arrayToObject": {
"$filter": {
"input": {
"$objectToArray": "$$elem._p"
},
"as": "anyKey",
cond: {
$not: {
$in: [
"$$anyKey.v",
[
{}
]
]
}
}
}
}
}
}
]
}
}
}
}
},
{
$set: {
_a: {
"$concatArrays": [
"$_a2",
"$_a"
]
}
}
},
{
$unset: "_a2"
}
])
Explained:
Split the array in two arays via $set/$filter , _a2 (contain elements that will not be changed ) and _a ( contain the affected inconsistent )
$map/$mergeObjects/$mergeObjects/$map/$arrayToObject to remove the inconsistent objects inside _a[]._p.s.k.t[]
$map/$mergeObjects/$mergeObjects/$map/$arrayToObject/$filter to remove the empty _a[]._p.s.k.t[] arrays t with theyr keys k.
$map/$mergeObjects/$mergeObjects/$map/$arrayToObject/$filter to remove the empty _a[]._p.s:{} elements.
$concat on _a and _a2 to concatenete the fixed _a[] array elements with the ones that are correct and preserved in _a2[].
$unset the temporary array _a2[] since it has been already concatenated with _a[] in previous stage.
Special thanks to #nimrod serok & #rickhg12hs for the initial ideas!
Playground

Find subdocument nested inside a document by id in mondogb

I have a mongodb document like this
{
"_id": {
"$oid": "6241dd90891458501c17d627"
},
"A": [
{
"_id": {
"$oid": "6241ddb1891458501c17d63e"
},
"B": [
{
"_id": {
"$oid": "6241ddc4891458501c17d674"
}
},
{
"_id": {
"$oid": "6241ddda891458501c17d675"
}
}
]
},
{
"_id": {
"$oid": "6241ddbe891458501c17d63f"
},
"B": [
{
"_id": {
"$oid": "6241ddda891458501c17d678"
}
},
{
"_id": {
"$oid": "6241ddda891458501c17d679"
}
}
]
}
]
}
This document has 2 nested arrays: an array of "A" elements, inside each element of "A" there's an array of "B" elements. I need to search by an _id of a "B" element, let's say 6241ddda891458501c17d679. I need a way to obtain this structure in mongodb
{
"_id": {
"$oid": "6241dd90891458501c17d627"
},
"A": [
{
"_id": {
"$oid": "6241ddbe891458501c17d63f"
},
"B": [
{
"_id": {
"$oid": "6241ddda891458501c17d679"
}
}
]
}
]
}
How can I achieve this? Thanks very much
Maybe something like this:
Option 1, Find:
db.collection.find({
"A.B._id": {
"$oid": "6241ddda891458501c17d679"
}
},
{
"A": {
"$filter": {
"input": {
"$map": {
"input": "$A",
"as": "a",
"in": {
"_id": "$$a._id",
"B": {
"$filter": {
"input": "$$a.B",
"as": "b",
"cond": {
"$eq": [
{
"$oid": "6241ddda891458501c17d679"
},
"$$b._id"
]
}
}
}
}
}
},
"as": "an",
"cond": {
"$ne": [
"$$an.B",
[]
]
}
}
}
})
Explained:
Use find() with match query on "A.B._id" ( good to have index on this filed for best performance)
In the filter part add $filter/map/filter combination to filter only the matching _id for array B elements and preserve the array A _id , also in the initial filter condition use only non-empty arrays [] to avoid having elements from empty arrays in the final result.
playground1
Option 2 , aggregation:
db.collection.aggregate([
{
$match: {
"A.B._id": {
"$oid": "6241ddda891458501c17d679"
}
}
},
{
"$addFields": {
"A": {
"$filter": {
"input": {
"$map": {
"input": "$A",
"as": "a",
"in": {
"_id": "$$a._id",
"B": {
"$filter": {
"input": "$$a.B",
"as": "b",
"cond": {
"$eq": [
{
"$oid": "6241ddda891458501c17d679"
},
"$$b._id"
]
}
}
}
}
}
},
"as": "an",
"cond": {
"$ne": [
"$$an.B",
[]
]
}
}
}
}
}
])
playground2

MongoDB how to filter in nested array

I have below data. I want to find value=v2 (remove others value which not equals to v2) in the inner array which belongs to name=name2. How to write aggregation for this? The hard part for me is filtering the nestedArray which only belongs to name=name2.
{
"_id": 1,
"array": [
{
"name": "name1",
"nestedArray": [
{
"value": "v1"
},
{
"value": "v2"
}
]
},
{
"name": "name2",
"nestedArray": [
{
"value": "v1"
},
{
"value": "v2"
}
]
}
]
}
And the desired output is below. Please note the value=v1 remains under name=name1 while value=v1 under name=name2 is removed.
{
"_id": 1,
"array": [
{
"name": "name1",
"nestedArray": [
{
"value": "v1"
},
{
"value": "v2"
}
]
},
{
"name": "name2",
"nestedArray": [
{
"value": "v2"
}
]
}
]
}
You can try,
$set to update array field, $map to iterate loop of array field, check condition if name is name2 then $filter to get matching value v2 documents from nestedArray field and $mergeObject merge objects with available objects
let name = "name2", value = "v2";
db.collection.aggregate([
{
$set: {
array: {
$map: {
input: "$array",
in: {
$mergeObjects: [
"$$this",
{
$cond: [
{ $eq: ["$$this.name", name] }, //name add here
{
nestedArray: {
$filter: {
input: "$$this.nestedArray",
cond: { $eq: ["$$this.value", value] } //value add here
}
}
},
{}
]
}
]
}
}
}
}
}
])
Playground
You can use the following aggregation query:
db.collection.aggregate([
{
$project: {
"array": {
"$concatArrays": [
{
"$filter": {
"input": "$array",
"as": "array",
"cond": {
"$ne": [
"$$array.name",
"name2"
]
}
}
},
{
"$filter": {
"input": {
"$map": {
"input": "$array",
"as": "array",
"in": {
"name": "$$array.name",
"nestedArray": {
"$filter": {
"input": "$$array.nestedArray",
"as": "nestedArray",
"cond": {
"$eq": [
"$$nestedArray.value",
"v2"
]
}
}
}
}
}
},
"as": "array",
"cond": {
"$eq": [
"$$array.name",
"name2"
]
}
}
}
]
}
}
}
])
MongoDB Playground

How to combine mongodb original output of query with some new fields?

Collection:
[
{
"name": "device1",
"type": "a",
"para": {
"number": 3
}
},
{
"name": "device2",
"type": "b",
"additional": "c",
"para": {
"number": 1
}
}
]
My query:
db.collection.aggregate([
{
"$addFields": {
"arrayofkeyvalue": {
"$objectToArray": "$$ROOT"
}
}
},
{
"$unwind": "$arrayofkeyvalue"
},
{
"$group": {
"_id": null,
"allkeys": {
"$addToSet": "$arrayofkeyvalue.k"
}
}
}
])
The output currently:
[
{
"_id": null,
"allkeys": [
"additional",
"_id",
"para",
"type",
"name"
]
}
]
Detail see Playground
What I want to do is add a new column which includes all of top key of the mongodb query output, exclude "para". And then combine it with the old collection to form a new json.
Is it possible?
The expected result:
{
"column": [{"prop": "name"}, {"prop": "type"}, {"prop": "additional"}],
"columnData": [
{
"name": "device1",
"type": "a",
"para": {
"number": 3
}
},
{
"name": "device2",
"type": "b",
"additional": "c",
"para": {
"number": 1
}
}
]
}
You have the right general idea in mind, here's how I would do it by utilizing operators like $filter, $map and $reduce to manipulate the objects structure.
I separated the aggregation into 3 parts for readability but you can just merge stage 2 and 3 if you wish.
db.collection.aggregate([
{
"$group": {
"_id": null,
columnData: {
$push: "$$ROOT"
},
"keys": {
"$push": {
$map: {
input: {
"$objectToArray": "$$ROOT"
},
as: "field",
in: "$$field.k"
}
}
}
}
},
{
"$addFields": {
unionedKeys: {
$filter: {
input: {
$reduce: {
input: "$keys",
initialValue: [],
in: {
"$setUnion": [
"$$this",
"$$value"
]
}
}
},
as: "item",
cond: {
$not: {
"$setIsSubset": [
[
"$$item"
],
[
"_id",
"para"
]
]
}
}
}
}
}
},
{
$project: {
_id: 0,
columnData: 1,
column: {
$map: {
input: "$unionedKeys",
as: "key",
in: {
prop: "$$key"
}
}
}
}
}
])
Mongo Playground

How we can use $toUpper with array fields?

How we can use toUpper with array field, I have the following query which compare array field 'locations' with an array of camel case items, now my problem is how we can convert locations field values to upper case and then compare with array.
var array = ["KABUL","KAPISA","WARDAK","LOGAR","PARWAN","BAGHLAN","NANGARHAR","LAGHMAN",
"BAMYAN","PANJSHER","KHOST","GHAZNI","KUNARHA","PAKTYA","PAKTIKA","KUNDUZ",
"NOORISTAN","SAMANGAN","TAKHAR","DAYKUNDI","BADAKHSHAN","BALKH","GHOR",
"UROZGAN","FARYAB","ZABUL","SAR-E-PUL","NIMROZ","JAWZJAN","HELMAND","BADGHIS",
"KANDAHAR","FARAH","HERAT"];
db.getCollection('test').aggregate([
{ "$project": {
"locations": {
"$map": {
"input": {
"$setIntersection": ["$locations", array ]
},
"in": { "k": "$$this", "v": 1 }
}
}
}},
{ "$unwind": "$locations" },
{ "$group": {
"_id": "$locations.k",
"v": { "$sum": "$locations.v" }
}},
{ "$sort": { "_id": 1 } },
{ "$group": {
"_id": null,
"obj": { "$push": { "k": "$_id", "v": "$v" } }
}},
{ "$replaceRoot": {
"newRoot": { "$arrayToObject": "$obj" }
}}
])
locations field is like :
"locations" : [
"Afghanistan",
"Kabul",
.....
],
Using $map to transform "each" element of course:
{ "$project": {
"locations": {
"$map": {
"input": {
"$setIntersection": [
{ "$map": { "input": "$locations", "in": { "$toUpper": "$$this" } } },
array
]
},
"in": { "k": "$$this", "v": 1 }
}
}
}},