I want to $match in a MongoDB, the number of documents in thousands, so looking for dynamic sol:
$doc.k equal to $info.data.k after k, $match then we have a document that contains only information that $matchs, see the expected output document. If you have any questions let me know.
[
{
"doc": {
"k": "ABC",
"v": {
"sec": 0
}
},
"info": [
{
"data": [
{
"k": "XYZ",
"v": {
"know": "alpha"
}
},
{
"k": "ABC",
"v": {
"know": "alpha"
}
}
]
}
]
}
]
The expected output document will look like this
[
{
"doc": {
"k": "ABC",
"v": {
"sec": 0
}
},
"info": [
{
"data": [
{
"k": "ABC",
"v": {
"know": "alpha"
}
}
]
}
]
}
]
$set - Update info field.
1.1. $map - As info is an array, need to iterate each document in the info array and return a new array.
1.1.1. $filter - Filter the document by doc.k and current iterate k value.
db.collection.aggregate([
{
$set: {
info: {
$map: {
input: "$info",
in: {
data: {
$filter: {
input: "$$this.data",
cond: {
$eq: [
"$doc.k",
"$$this.k"
]
}
}
}
}
}
}
}
}
])
Sample Mongo Playground
Related
I'm working in MongoDB and getting stuck at one aggregation case. Let me show you my collection.
First collection (data):
[
{
"_id": "8e7b3fa0-4230-448c-8f70-1d7300632834",
"data": [
{
"animal" : "7d44251a-b308-4deb-875a-33ef0a69fe2b",
"place": "Chennai"
},
{
"animal" : "fcfdd527-5885-48b0-a91f-03f72f78528f",
"place": "Kolkata"
}
]
}
]
Second collection (Animal):
[
{
"_id": "7d44251a-b308-4deb-875a-33ef0a69fe2b",
"name": "Dog"
},
{
"_id": "7d44251a-b308-4deb-875a-33ef0a69fe2b",
"name": "Cat"
}
]
I'm using this query:
db.data.aggregate([
{
"$lookup": {
"from": "animal",
"localField": "data.animal",
"foreignField": "_id",
"as": "doc"
}
},
{
"$unwind": "$doc"
},
{
"$project": {
"_id": 1,
"data.animal": "$doc.name",
"data.place": 1
}
}
])
and it result me this
[
{
"_id": "8e7b3fa0-4230-448c-8f70-1d7300632834",
"data": [
{
"animal": "Dog",
"place": "Chennai"
},
{
"animal": "Dog",
"place": "Kolkata"
}
]
},
{
"_id": "8e7b3fa0-4230-448c-8f70-1d7300632834",
"data": [
{
"animal": "Cat",
"place": "Chennai"
},
{
"animal": "Cat",
"place": "Kolkata"
}
]
}
]
Where I'm expecting like this
[
{
"_id": "8e7b3fa0-4230-448c-8f70-1d7300632834",
"data": [
{
"animal": "Dog",
"place": "Chennai"
},
{
"animal": "Cat",
"place": "Kolkata"
}
]
}
]
Mongo Playground
Also sharing this question in Mongo playgroud. Thanks in advance!!
Solution 1
$unset - Deconstruct the data array into multiple documents.
$lookup - Perform join with animal collection.
$project - Decorate the output document. For data.animal field, get the first value via $first.
$group - Group by _id. Push the data document into the data array.
db.data.aggregate([
{
"$unwind": "$data"
},
{
"$lookup": {
"from": "animal",
"localField": "data.animal",
"foreignField": "_id",
"as": "doc"
}
},
{
"$project": {
"_id": 1,
"data.animal": {
$first: "$doc.name"
},
"data.place": 1
}
},
{
$group: {
_id: "$_id",
data: {
$push: "$data"
}
}
}
])
Demo Solution 1 # Mongo Playground
Solution 2
$lookup
$set - Set data field.
2.1. $map - Iterate the data array and returns a new array.
2.1.1. $mergeObjects - Merge current iterated document with place field and the document from 2.1.1.1.
2.1.1.1. $first - Get the first document from the filtered doc arrays by matching the ids via $filter.
$unset - Remove _id and animals._id fields.
db.data.aggregate([
{
"$lookup": {
"from": "animal",
"localField": "data.animal",
"foreignField": "_id",
"as": "doc"
}
},
{
$set: {
data: {
$map: {
input: "$data",
as: "data",
in: {
$mergeObjects: [
{
place: "$$data.place"
},
{
$first: {
$filter: {
input: "$doc",
cond: {
$eq: [
"$$this._id",
"$$data.animal"
]
}
}
}
}
]
}
}
}
}
},
{
$unset: [
"doc",
"data._id"
]
}
])
Demo Solution 2 # Mongo Playground
I have a doc like this
{
"_id" : "oidfi",
"users": [
{
"_id": "q",
"tags": ["a", "b", "c"],
"age": 20
},
{
"_id": "q",
"tags": ["x", "y", "z"],
"age": 30
}
],
"type": "repo"
}
I want to filter the users array with several fields ( I'm able to do that with the below query )
{
"$match": {
"$and": [
{
"_id": "a506f8af6f510f616bea14715276d474d2b363f0aef10443cc0f11559b139147"
}
]
}
},
{
"$project": {
"users": {
"$filter": {
"input": "$users",
"as": "users",
"cond": {
"$and": [
{
"$gte": [
"$$user.age",
15
]
}
]
}
}
}
}
}
]
is there a way for me to add condition where I give a list of tags items and get the users who are matching at least one item from their tags array
$$user.tags.containsAny(["a", "x"])
Thanks
You can do the followings in an aggregation pipeline:
$setIntersection to find out the intersection of the users.tags array and your input array i.e. ["a", "x"] in your example
$size to find the size of array in step 1
$match by the size of step 2 larger than 0
"anyTagsMatched": {
$gt: [
{
$size: {
"$setIntersection": [
"$users.tags",
[
"a",
"x"
]
]
}
},
0
]
}
Here is the Mongo playground for your reference.
You may have like following
db.collection.aggregate([
{
"$project": {
"users": {
"$filter": {
"input": "$users",
"cond": {
"$and": [
{ "$gte": [ "$$this.age", 15 ] },
{
$gt: [
{
$size: {
"$setIntersection": [
"$$this.tags",
[ "a", "x" ]
]
}
},
0
]
}
]
}
}
}
}
}
])
Working Mongo playground
Document 1: Need to covert object to Arrays. I have to iterate on each element of the key and value, how I can convert from an object to Array.
{
"Data":{
"A" :{
"name" : alpha,
"Score" : 199
},
"B" :{
"name" : Beta,
"Score" : 122
}
}
}
Expected Output :
{
"name" :[
alpha,
Beta
],
"score": [
199,
122
]
}
You can achieve this using mongo aggregation framework.
[
{
"$addFields": {
"Data": {
"$objectToArray": "$Data"
}
}
},
{
$unwind: "$Data"
},
{
"$group": {
"_id": "_id",
"name": {
$push: "$Data.v.name"
},
"score": {
$push: "$Data.v.Score"
},
}
}
]
Mongo Playground: https://mongoplayground.net/p/JVTW5rEoJoa
I have a document like this:
I need to return the documents and filter the nested array (lessons) where any item of subLessons into input array
[{
"_id": {
"$oid": "6081fedbee5d133dbffb42eb"
},
"name": "my quiz",
"lessons": [
{
"_id": "460c42e1-b0b7-437e-ab63-c59cce8ced0d",
"name": "section",
"subLesson": [
{
"$oid": "6081fed9ee5d133dbffb3cba"
},
{
"$oid": "6081fed9ee5d133dbffb3cc0"
}
]
},
{
"_id": "f7b5c95f-1a68-42ca-880c-22ef3831ff03",
"name": "ffff",
"subLesson": [
{
"$oid": "6081fed9ee5d133dbffb3cbb"
}
]
}
]
}
}]
I wrote the following query but it does not work. I do not know how to use $elemMatch in $filter
db.collection.aggregate([
{
"$project": {
_id: 1,
lessons: {
$filter: {
"input": "$lessons",
"as": "lesson",
"cond": {
"$$lesson.subLesson": {
"$elemMatch": {
"$in": [
ObjectId("6081fed9ee5d133dbffb3cba")
]
}
}
}
}
}
}
}
])
I am trying to find the record such that the result looks like the following.
[{
"_id": {
"$oid": "6081fedbee5d133dbffb42eb"
},
"lessons": [
{
"_id": "460c42e1-b0b7-437e-ab63-c59cce8ced0d",
"name": "zzzz",
"subLesson": [
{
"$oid": "6081fed9ee5d133dbffb3cba"
},
{
"$oid": "6081fed9ee5d133dbffb3cc0"
}
]
}
]
},
}]
Can anyone please help out to understand how can I make this work
thanks
You can use $in directly
db.collection.aggregate([
{
$project: {
lessons: {
$filter: {
input: "$lessons",
cond: {
$in: [ ObjectId("6081fed9ee5d133dbffb3cba"), "$$this.subLesson" ]
}
}
}
}
}
])
Working Mongo playground
Update 1
db.collection.aggregate([
{ "$unwind": "$lessons" },
{
"$match": {
"lessons.subLesson": {
$in: [ ObjectId("6081fed9ee5d133dbffb3cba"), ObjectId("6081fed9ee5d133dbffb3cbb") ]
}
}
},
{
$group: {
_id: "$_id",
name: { $first: "$name" },
lessons: { $push: "$lessons" }
}
}
])
Mongo Playground
Assume I have the following document:
[
{
"callId": "17dac51e-125e-499e-9064-f20bd3b1a9d8",
"caller": {
"firstName": "Test",
"lastName": "Testing",
"phoneNumber": "1231231234"
},
"routeHistory": [
{
"assignedUserId": "cfa0ffe9-c77d-4eec-87d7-4430f7772e81",
"routeDate": "2020-01-01T06:00:00.000Z",
"status": "routed"
},
{
"assignedUserId": "cfa0ffe9-c77d-4eec-87d7-4430f7772e81",
"routeDate": "2020-01-03T06:00:00.000Z",
"status": "ended"
}
]
}
]
I want to get results where routeHistory.routeDate is equal to the $max routeDate value in routeHistory. I would expect my results to look like the following:
[
{
"callId": "17dac51e-125e-499e-9064-f20bd3b1a9d8",
"caller": {
"firstName": "Test",
"lastName": "Testing",
"phoneNumber": "1231231234"
},
"routeHistory": [
{
"assignedUserId": "cfa0ffe9-c77d-4eec-87d7-4430f7772e81",
"routeDate": "2020-01-03T06:00:00.000Z",
"status": "ended"
}
]
}
]
Is there a clean way to do this in a single aggregate, so that additional $match criteria can be applied?
You can use $let to define temporary variable being $max date and the use $filter along with $arrayElemAt to get first matching element:
db.collection.aggregate([
{
$addFields: {
routeHistory: {
$let: {
vars: {
maxDate: { $max: "$routeHistory.routeDate" }
},
in: {
$arrayElemAt: [
{ $filter: { input: "$routeHistory", cond: { $eq: [ "$$maxDate", "$$this.routeDate" ] } } },
0
]
}
}
}
}
}
])
Mongo Playground
EDIT:
version without $let:
db.collection.aggregate([
{
$addFields: {
maxDate: {
$max: "$routeHistory.routeDate"
}
}
},
{
$addFields: {
routeHistory: {
$arrayElemAt: [
{
$filter: { input: "$routeHistory", cond: { $eq: [ "$$maxDate", "$$this.routeDate" ] } }
},
0
]
}
}
}
}
])