MongoDB: append as field the matched element from array - javascript

I have the following code:
const array = ['a', 'b', 'c']
await collection.aggregate([
{
$match: {
codes: { $in: array }
}
},
{
$addFields: { matchedField: "codes.$" } // <--- does not work
},
]).toArray();
I need to append the matched element from within array to the returned results. Is it possible to do that?
My collection contains documents with the following scheme:
{
"color": "red",
"codes": [ "b" ]
}
I need the aggregation directive to return this:
{
"color": "red",
"codes": [ "b" ],
"matchedField": "b",
}

$filter
db.collection.aggregate([
{
"$match": {
"codes": {
"$in": [
"a",
"b",
"c"
]
}
}
},
{
"$set": {
codes: {
$filter: {
input: "$codes",
as: "c",
cond: {
$in: [
"$$c",
[
"a",
"b",
"c"
]
]
}
}
}
}
}
])
mongoplayground
$setIntersection
db.collection.aggregate([
{
"$match": {
"codes": {
"$in": [
"a",
"b",
"c"
]
}
}
},
{
"$set": {
codes: {
$setIntersection: [
"$codes",
[
"a",
"b",
"c"
]
]
}
}
}
])
mongoplayground

Related

Mongo Project by referencing external Dictionary

I have a collection in MongoDB:
[
{
"uid": "a1"
},
{
"uid": "a2"
}
]
and a dictionary in my JS code
let dict = { "a1": "ref1", "a1": "ref2" };
I want to do an aggregate that will somehow join the two.
let k = ;
this.model.aggregate([
{
$match: {
uid: { $in: Object.keys(dict) }
}
},
{
$project: {
ref: // here is where I want to add the equivalent reference
}
}
])
The expected output would be something like this:
[{uid: "a1", ref: "ref1"}, {uid: "a2", ref: "ref2}]
Is there a way to get the reference from the dict into the $project?
I don't know if there is an easier way to do it, but here's how I achieved it:
db.collection.aggregate([
{
$match: {
uid: {
$in: [
"a1",
"a2"
]
}
}
},
{
"$project": {
"array": {
"$objectToArray": {
"a1": "ref1",
"a2": "ref2"
}
},
"uid": 1,
}
},
{
"$project": {
"ref": {
"$filter": {
"input": "$array",
"as": "elem",
"cond": {
"$eq": [
"$$elem.k",
"$uid"
]
}
}
},
uid: 1,
},
},
{
"$project": {
ref: {
"$arrayElemAt": [
"$ref.v",
0
]
},
uid: 1,
}
}
])
See it working here. I hope you get the idea and convert it into the nodejs equivalent version.

Having trouble in nested array of abjects

I've used Mongodb aggregation, $facet as I wanted to count every value of "reli" and "prov" from the collection.
This is my code to get results from db.
const keyy = await db.aggregate([
$facet: { "reli": [
{ $group: { _id: '$reli', count: { $sum: 1 } } } ],
"prov": [
{ $group: { _id: '$prov', count: { $sum: 1 } } }
],
])
Output Looks like this:
[
{
"reli": [
{
"_id": "abcdef",
"count": 6
},
{
"_id": "ghij",
"count": 1
},
],
"prov": [
{
"_id": "hello",
"count": 63
},
{
"_id": "hey",
"count": 9
},
]
}
]
But I want That my Expected output is :
[
{
"reli":[
{abcdef: 6},
{ghij: 1}
],
"prov":[
{"hello": 63},
{"hey": 9}
]
}
]
You can iterate using $map and generate the new array with the values you want using $arrayToObject like this:
This query simply overwrite values reli and prov with the result of the map. That result is an array compound by objects where the key k is the _id value and the value v is the count value.
db.collection.aggregate([
{
"$project": {
"reli": {
"$map": {
"input": "$reli",
"in": {
"$arrayToObject": [
[
{
k: "$$this._id",
v: "$$this.count"
}
]
]
}
}
},
"prov": {
"$map": {
"input": "$prov",
"in": {
"$arrayToObject": [
[
{
k: "$$this._id",
v: "$$this.count"
}
]
]
}
}
}
}
}
])
Example here

How to change field from subdocument a parent field in Mongoose

I am trying to export Mongo data to XLSX which requires all the data to be in the parent map but currently I have data in this format:
[
{
"_id": "eatete",
"competition": {
"_id": "eatete"
"name": "Some competition name"
},
"members": [
{
"_id": "eatete",
"name": "Saad"
},
{
"_id": "eatete",
"name": "Saad2"
}
],
"leader": {
"name": "Saad",
"institute": {
"_id": "eatete",
"name": "Some institute name"
}
},
}
]
Ideally, the data should be:
[
{
"_id": "eatete",
"competition": "Some competition name"
"member0name": "Saad",
"member1name": "Saad2",
"leadername": "Saad",
"institute": "Some institute name"
}
]
So basically what I want is to refer the data of fields of subdocuments as if those were part of parent document, like competitions = competitions.name.
Can you please help me how can I do so using Mongoose.
Thanks
With some more aggregation trick
db.collection.aggregate([
{ "$unwind": { "path": "$members", "includeArrayIndex": "i" }},
{ "$group": {
"_id": "$_id",
"competition": { "$first": "$competition.name" },
"leadername": { "$first": "$leader.name" },
"institute": { "$first": "$leader.institute.name" },
"data": {
"$push": {
"k": { "$concat": ["members", { "$toLower": "$i" }, "name"] },
"v": "$members.name"
}
}
}},
{ "$replaceRoot": {
"newRoot": {
"$mergeObjects": ["$$ROOT", { "$arrayToObject": "$data" }]
}
}},
{ "$project": { "data": 0 }}
])
You can try below aggregation on your Model:
let resultt = await Model.aggregate([
{
$project: {
_id: 1,
competition: "$competition.name",
leadername: "$leader.name",
institute: "$leader.institute.name",
members: {
$map: {
input: { $range: [ 0, { $size: "$members" } ] },
in: {
k: { $concat: [ "member", { $toString: "$$this" }, "name" ] },
v: {
$let: {
vars: { current: { $arrayElemAt: [ "$members", "$$this" ] } },
in: "$$current.name"
}
}
}
}
}
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [ "$$ROOT", { $arrayToObject: "$members" } ]
}
}
},
{
$project: {
members: 0
}
}
])
Since you need to dynamically evaluate your keys based on indexes you can use $map with $range to map a list of indexes into keys of a new object. Then you can use $arrayToObject to get an object from those keys and $mergeObjects with $replaceRoot to flatten this object structure.

Why the following query does not return any data?

I have a collection in mongodb and indexed on the field name , i do a free search using the following query to get matched results and limit to 5,
db.getCollection('_event').aggregate([
{
"$match": {
"$and": [
{
"$text": {
"$search": "liver"
}
},
{},
{}
]
}
},
{
"$group": {
"_id": null,
"count": {
"$sum": 1
},
"results": {
"$push": "$$ROOT"
}
}
},
{
"$project": {
"count": 1,
"results": {
"$slice": [
"$results",
5
]
}
}
}
])
but there is a data with liverpool . when i do replace it with "$search": "liverpool" it returns data.
what is the issue here?

Trouble creating query in MongoDB with subquery

I have a dataset that looks something like this:
{
"id": "02741544",
"items": [{
"item": "A"
}]
}, {
"id": "02472691",
"items": [{
"item": "A"
}, {
"item": "B"
}, {
"item": "C"
}]
}, {
"id": "01316523",
"items": [{
"item": "A"
}, {
"item": "B"
}]
}, {
"id": "01316526",
"items": [{
"item": "A"
}, {
"item": "B"
}]
}, {
"id": "01316529",
"items": [{
"item": "A"
}, {
"item": "D"
}]
},
I'm trying to craft a query that will give me an output that looks like this:
{
"item": "A",
"ids": [{
"id": "02741544"
}, {
"id": "02472691"
}, {
"id": "01316523"
}, {
"id": "01316526"
}, {
"id": "01316529"
}]
}, {
"item": "B",
"ids": [{
"id": "02472691"
}, {
"id": "01316523"
}, {
"id": "01316526"
}]
}, {
"item": "C",
"ids": [{
"id": "02472691"
}]
}, {
"item": "D",
"ids": [{
"id": "02472691"
}]
},
Basically, I'm trying to get the distinct items from the item array in the object, and then returning an array of ids for each obj that has that item in it's item array.
Better use the aggregation framework in which you need to run an operation that consists of the following pipeline steps (in the given order):
$unwind - This initial step will flatten the items array i.e. it produces a copy of each document per array entry. This is necessary for processing the documents further down the pipeline as "denormalised" documents which you can aggregate as groups.
$group - This will group the flattened documents by the item subdocument key and create the ids list by using the $push accumulator operator.
-- UPDATE --
As #AminJ pointed out in the comments, if items can have duplicate item values and you don't want duplicate ids in the result you can use $addToSet instead of $push
The following example demonstrates this:
db.collection.aggregate([
{ "$unwind": "$items" },
{
"$group": {
"_id": "$items.item",
"ids": {
"$push": { "id": "$id" } /* or use
"$addToSet": { "id": "$id" } if you don't want duplicate ids */
}
}
}
])
Sample Output
{
"_id" : "A",
"ids" : [
{ "id" : "02741544" },
{ "id" : "02472691" },
{ "id" : "01316523" },
{ "id" : "01316526" },
{ "id" : "01316529" }
]
}
/* 2 */
{
"_id" : "B",
"ids" : [
{ "id" : "02472691" },
{ "id" : "01316523" },
{ "id" : "01316526" }
]
}
/* 3 */
{
"_id" : "C",
"ids" : [
{ "id" : "02472691" }
]
}
/* 4 */
{
"_id" : "D",
"ids" : [
{ "id" : "01316529" }
]
}
The result from an aggregate() function is a cursor to the documents produced by the final stage of the aggregation pipeline operation. So if you want the results in an array you can use the cursor's toArray() method which returns an array that contains all the documents from it.
For example:
var pipeline = [
{ "$unwind": "$items" },
{
"$group": {
"_id": "$items.item",
"ids": {
"$push": { "id": "$id" } /* or use
"$addToSet": { "id": "$id" } if you don't want duplicate ids */
}
}
}
],
results = db.collection.aggregate(pipeline).toArray();
printjson(results);
Here's a solution using an aggregation pipeline:
db.col.aggregate([
{
$unwind: "$items"
},
{
$project: {
id: 1,
item: "$items.item"
}
},
{
$group: {
_id: "$item",
ids: {
$push: "$id"
}
}
}
])

Categories

Resources