Using aggregate $lookup and $mergeObjects - javascript

I want to join collection.
before, I used only lookup, so that I could get separated field that is joined.
but I need to get result similar mysql join.
I noticed there is $lookup and $mergeObjects for this action but not working well.
user collection model.
{
"_id": ObjectId("xxxxxxx"), //this is default id from mongoDB
"name": 'admin user',
"email": 'admin#test.com',
"password": 'xxxxxxxx',
"roles": [
{
"id": 0,
"approved": true
},{
"id": 2,
"approved": true
}
]
},{
"_id": ObjectId("xxxxxxx"), //this is default id from mongoDB
"name": 'userOne',
"email": 'user#test.com',
"password": 'xxxxxxxx',
"roles": [
{
"id": 1,
"approved": true
}
]
}
roles collection model.
{
"_id": ObjectId("xxxxxxx"), //this is default id from mongoDB
"id": '0',
"name": 'administrator'
},{
"_id": ObjectId("xxxxxxx"), //this is default id from mongoDB
"id": '0',
"name": 'employeer'
},{
"_id": ObjectId("xxxxxxx"), //this is default id from mongoDB
"id": '0',
"name": 'freelancer'
}
after join, I want to get result like below.
{
"_id": ObjectId("xxxxxxx"), //this is default id from mongoDB
"name": 'admin user',
"email": 'admin#test.com',
"password": 'xxxxxxxx',
"roles": [
{
"id": 0,
"name": "administrator", //join result
"approved": true
},{
"id": 2,
"name": "freelancer", //join result
"approved": true
}
]
},{
"_id": ObjectId("xxxxxxx"), //this is default id from mongoDB
"name": 'userOne',
"email": 'user#test.com',
"password": 'xxxxxxxx',
"roles": [
{
"id": 1,
"name": "employeer", //join result
"approved": true
}
]
}

You can use below aggregation with mongodb 3.4
You need to $unwind the roles array first and then $group to rollback again
db.users.aggregate([
{ "$unwind": "$roles" },
{ "$lookup": {
"from": "roles",
"localField": "roles.id",
"foreignField": "id",
"as": "roles.role"
}},
{ "$unwind": "$roles.role" },
{ "$addFields": {
"roles": { "$mergeObjects": ["$roles.role", "$roles"] }
}},
{ "$group": {
"_id": "$_id",
"email": { "$first": "$email" },
"password": { "$first": "$password" },
"roles": { "$push": "$roles" }
}},
{ "$project": { "roles.role": 0 }}
])
Which is quite simple with the mongodb 3.6 and above
db.users.aggregate([
{ "$unwind": "$roles" },
{ "$lookup": {
"from": "roles",
"let": { "roleId": "$roles.id", "approved": "$roles.approved" },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$id", "$$roleId"] }}},
{ "$addFields": { "approved": "$$approved" }}
],
"as": "roles"
}},
{ "$unwind": "$roles" },
{ "$group": {
"_id": "$_id",
"email": { "$first": "$email" },
"password": { "$first": "$password" },
"roles": { "$push": "$roles" }
}}
])
Both will give you similar Output
[
{
"_id": ObjectId("5a934e000102030405000004"),
"email": "user#test.com",
"password": "xxxxxxxx",
"roles": [
{
"_id": ObjectId("5a934e000102030405000001"),
"approved": true,
"id": 1,
"name": "employeer"
}
]
},
{
"_id": ObjectId("5a934e000102030405000003"),
"email": "admin#test.com",
"password": "xxxxxxxx",
"roles": [
{
"_id": ObjectId("5a934e000102030405000000"),
"approved": true,
"id": 0,
"name": "administrator"
},
{
"_id": ObjectId("5a934e000102030405000002"),
"approved": true,
"id": 2,
"name": "freelancer"
}
]
}
]

Related

MongoDB match expression query not working

MongoDb query match expression not working.
I have a posts collection and want to return only those posts which match the user Id of the user who created it but my query does not seem to be working.
Sample Dataset
[
// 1
{
"_id": ObjectId("6257047cffd61ab62864c1ae"),
"type": "A",
"source": "B",
"status": "A",
"user": ObjectId("622b55ff0b0af6b049c387d3")
},
// 2
{
"_id": ObjectId("6257047cffd61ab62864c1ad"),
"type": "B",
"source": "A",
"status": "A",
"user": ObjectId("622b55ff0b0af6b049c387d3")
},
// 3
{
"_id": ObjectId("6257047cffd61ab62864c1ce"),
"type": "A",
"source": "C",
"status": "B",
"user": ObjectId("622b55ff0b0af6b049c387d3")
},
// 4
{"_id": ObjectId("6257047cffd61ab62864c1cb"),
"type": "A",
"source": "B",
"status": "C",
"user": ObjectId("622b56250b0af6b049c387d6")
}
]
MongoDb Query:-
db.collection.aggregate([
{
$addFields: {
paramType: "All",
paramSource: "All",
paramStatus: "All",
},
},
{
$match: {
$expr: {
$and: [
{
user: ObjectId("622b55ff0b0af6b049c387d3")
},
{
$or: [
{
$eq: [
"$paramType",
"All"
],
},
{
$eq: [
"$paramType",
"$type"
],
},
],
},
{
$or: [
{
$eq: [
"$paramSource",
"All"
],
},
{
$eq: [
"$paramSource",
"$source"
],
},
],
},
{
$or: [
{
$eq: [
"$paramStatus",
"All"
],
},
{
$eq: [
"$paramStatus",
"$status"
],
},
],
},
],
},
},
},
{
$setWindowFields: {
output: {
totalCount: {
$count: {}
}
}
}
},
{
$sort: {
createdAt: -1
}
},
{
$skip: 0
},
{
$limit: 6
},
{
"$project": {
"paramSource": false,
"paramStatus": false,
"paramType": false,
}
}
])
Query Output:-
[
{
"_id": ObjectId("6257047cffd61ab62864c1ae"),
"source": "B",
"status": "A",
"totalCount": 4,
"type": "A",
"user": ObjectId("622b55ff0b0af6b049c387d3")
},
{
"_id": ObjectId("6257047cffd61ab62864c1ad"),
"source": "A",
"status": "A",
"totalCount": 4,
"type": "B",
"user": ObjectId("622b55ff0b0af6b049c387d3")
},
{
"_id": ObjectId("6257047cffd61ab62864c1ce"),
"source": "C",
"status": "B",
"totalCount": 4,
"type": "A",
"user": ObjectId("622b55ff0b0af6b049c387d3")
},
{
"_id": ObjectId("6257047cffd61ab62864c1cb"),
"source": "B",
"status": "C",
"totalCount": 4,
"type": "A",
"user": ObjectId("622b56250b0af6b049c387d6")
}
]
The query does not work output contains posts created by all users it is not filtering.
The $match part should look like this:
{
$match: {
$and: [
{
user: ObjectId("622b55ff0b0af6b049c387d3")
},
{
$or: [{paramType: {$eq: "All"}},
{$expr: {$eq: ["$paramType", "$type"]}}
]
},
{
$or: [{paramSource: {$eq: "All"}},
{$expr: {$eq: ["$paramSource", "$type"]}}
]
},
{
$or: [{paramStatus: {$eq: "All"}},
{$expr: {$eq: ["$paramStatus", "$type"]}}
]
}
]
}
}
The $expr should only be assigned to cases where both values are in the document. This query returns 3 / 4 documents, the ones in which user: ObjectId("622b55ff0b0af6b049c387d3")
BTW, the last 3 conditions on this $match stage are redundant, as they will always be true, since the query sets them with the value 'All' on the former stage

How to only get the array nested subdocuments from dynamic depth level, with that document id and not having to iterate through it

[
{
"_id": "1",
"name": "sanggiyo",
"gender": "Male",
"children": [
{
"name": "tono",
"gender": "Male",
"children": [
{
"name": "anton",
"gender": "male",
"children": [],
"_id": "4"
}
],
"_id": "2"
},
{
"name": "rin",
"gender": "Female",
"children": [],
"_id": "3"
}
],
"createdAt": "2022-04-17T11:14:00.648Z",
"updatedAt": "2022-04-17T11:14:16.277Z",
"__v": 0
}
]
lets say i have the id for the second nested object, or third level nested object (_id=2 or _id=3), or four level nested object (_id=4)
how to get only the document that is related to that id and not the whole document?
so far I have tried:
db.collection.aggregate([
{
$match: {
$or: [
{
"_id": "1"
},
{
"children._id": "1"
},
{
"children.children._id": "1"
}
]
}
},
{
"$replaceRoot": {
"newRoot": {
"$first": {
"$filter": {
"input": "$children",
"as": "item",
"cond": {
$eq: [
"$$item._id",
"2"
]
}
}
}
}
}
}
])
I need every id can get their own document. so in this case i expect 4 different output from 4 different case.
case 1 with id=1, i can get all data
case 2 with id=2,
{
"_id": "2",
"children": [
{
"_id": "4",
"children": [],
"gender": "male",
"name": "anton"
}
],
"gender": "Male",
"name": "tono"
}
case 3 with id=3
{
"_id": "3",
"children": [],
"gender": "Female",
"name": "rin"
}
case 4 with id=4
{
"name": "anton",
"gender": "male",
"children": [],
"_id": "4"
}
this is mongo playground for testing purpose, this way i can only get case 2 and 3 work, it fail getting case 1 and 4
https://mongoplayground.net/p/8A7kwHtmEaX

MongoDB nested aggregation lookup

I wont to connect two collection so as to workouts._id had an assigned value exercise.name.
first collection "workouts":
{
"_id": {
"$oid": "60ffbd531b1cba41e8a7fde3"
},
"id_user": {
"$oid": "60fd50ee1e9d064a5cee0bec"
},
"date": {
"$date": "2021-05-20T22:00:00.000Z"
},
"name": "wielki bicek",
"workouts": [{
"_id": {
"$oid": "60e07e588a2f1946f895475f"
},
"parts": [{
"_id": {
"$oid": "60ffbd531b1cba41e8a7fde5"
},
"weight": "30",
"repeating": "10"
}]
}, {
"_id": {
"$oid": "60e07e588a2f1946f895475f"
},
"parts": [{
"_id": {
"$oid": "60ffbd531b1cba41e8a7fde9"
},
"weight": "30",
"repeating": "10"
}, {
"_id": {
"$oid": "60ffbd531b1cba41e8a7fdea"
},
"weight": "30",
"repeating": "10"
}]
}],
"__v": 0
}
second collection "exercises":
{
"_id": {
"$oid": "60e07e588a2f1946f895475f"
},
"name": "Wyciskanie sztangi",
"image": "uploads\\2021-07-03T15-12-24.173Z-test.gif",
"type": "force",
"groupMuscle": {
"$oid": "60daf099c1aaa20b4cfdf0fa"
},
"instruction": "Opierając tułów znazewnątrz.",
"__v": 0
}
I have one collection that I want to run nested aggregation on this, but I don't know how to run this operation.
$unwind unwind workouts
$lookup lookpup exercises
$addFileds get first item
$addFileds get only name field
$project remove useless field
$group group by original id
db.workouts.aggregate([
{
"$unwind": "$workouts"
},
{
"$lookup": {
"from": "exercises",
pipeline: [
{
"$project": {
name: 1
}
}
],
"localField": "workouts._id",
"foreignField": "_id",
"as": "names"
}
},
{
"$addFields": {
"workouts.i": {
"$first": "$names"
}
}
},
{
"$addFields": {
"workouts.name": "$workouts.i.name"
}
},
{
"$project": {
"names": 0,
"workouts.i": 0
}
},
{
"$group": {
"_id": {
"_id": "$_id",
"id_user": "$id_user",
"date": "$date",
"name": "$name"
},
"workouts": {
$push: "$workouts"
}
}
}
])
mongoplayground

$project field in MongoDB with dates conditional return empty

I need to return data with standard plains and plains with promotion if today's date is still within the period.
I have already managed to do almost everything, the only problem is that in $project, my condition is correct but returns empty.
Collection of course:
{
"_id": "58a4fcc8dddbeb0ba0a84ac8",
"updatedAt": "2017-02-16T10:26:37.841Z",
"createdAt": "2017-02-16T01:13:44.936Z",
"name": "Engenharia da computação",
"hours": 5000,
"AreaId": "58a4eec3aa912c63b241698d",
"PriceId": "58a4fbd3dddbeb0ba0a84ac0",
"ModalityId": "584e073edde7042be39b31a2",
"__v": 0,
"isActive": true
}
Collection of Price:
{
"_id": "58a4fbd3dddbeb0ba0a84ac0",
"description": "Preço de banana",
"__v": 0,
"promotions": [
{
"name": "Promoção de natal",
"rules": "Somente valido no notal",
"startsAt": "2017-01-16T17:12:26.289Z",
"expiresAt": "2017-07-16T17:12:26.289Z",
"_id": "58a4fbd3dddbeb0ba0a84ac1",
"isActive": false,
"plains": [
{
"parcels": 1,
"from": 1800,
"to": 1500,
"_id": "58a4fbd3dddbeb0ba0a84ac4"
},
{
"parcels": 2,
"from": 900,
"to": 800,
"_id": "58a4fbd3dddbeb0ba0a84ac3"
},
{
"parcels": 3,
"from": 400,
"to": 300,
"_id": "58a4fbd3dddbeb0ba0a84ac2"
}
]
}
],
"plains": [
{
"parcels": 1,
"from": 2000,
"to": 1600,
"_id": "58a4fbd3dddbeb0ba0a84ac7"
},
{
"parcels": 2,
"from": 1000,
"to": 999,
"_id": "58a4fbd3dddbeb0ba0a84ac6"
},
{
"parcels": 3,
"from": 500,
"to": 400,
"_id": "58a4fbd3dddbeb0ba0a84ac5"
}
]
}
I will receive from a form the parcels in number, example: '1 or 2 parcels'.
If this exists in the 'plains' documents in promotions, you should search, but you should check the dates if the promotion has not expired, otherwise you should look for default plains value.
This is my query:
Course
.aggregate([
{
$match: {
_id: ObjectId(req.body.CourseId)
}
},
{
$lookup: {
'from' : 'prices',
'localField' : 'PriceId',
'foreignField': '_id',
'as' : 'price'
}
},
{
$redact: {
$cond: {
if : {
$or: [
{$eq: ['$parcels', 1]},
{$or: '$price'},
{$or: '$promotions'},
{$or: '$plains'},
]
},
then: "$$DESCEND",
else: "$$PRUNE"
}
}
},
{
$project: {
promotions: {
$filter: {
input: "$price.promotions",
as : "promotion",
cond : {$and: [
{$lte: ["$$promotion.startsAt", new Date]},
{$gte: ["$$promotion.expiresAt", new Date]}
]}
}
},
plains : "$price.plains"
}
}
])
The return is this:
{
"_id": "58a4fcc8dddbeb0ba0a84ac8",
"promotions": [],
"plains": [
[
{
"parcels": 1,
"from": 2000,
"to": 1600,
"_id": "58a4fbd3dddbeb0ba0a84ac7"
}
]
]
}
Theoretically the array of promotions should not come empty, it's like this:
[
{
"name": "February Promotion",
"rules": "Valid only in February",
"startsAt": "2017-01-16T17:12:26.289Z",
"expiresAt": "2017-07-16T17:12:26.289Z",
"_id": "58a4fbd3dddbeb0ba0a84ac1",
"plains": [
{
"parcels": 1,
"from": 1800,
"to": 1500,
"_id": "58a4fbd3dddbeb0ba0a84ac4"
},
{
"parcels": 2,
"from": 900,
"to": 800,
"_id": "58a4fbd3dddbeb0ba0a84ac3"
},
{
"parcels": 3,
"from": 400,
"to": 300,
"_id": "58a4fbd3dddbeb0ba0a84ac2"
}
]
}
]
Thanks.

Logstash convert entry // JSON

when I send a http Post to logstach my entry seems sth like that:
{
"_index": "logstash-2014.11.25",
"_type": "logs",
"_id": "PyALYOSjRi-N8cfg8axyMg",
"_score": null,
"_source": {
"message": "technology=apache",
"#version": "1",
"#timestamp": "2014-11-25T11:57:56.931Z",
"host": "127.0.0.1"
},
"sort": [
1416916676931,
1416916676931
]
}
Are you have any idea to handle the message so that it seems like thsi:
{
"_index": "logstash-2014.11.25",
"_type": "logs",
"_id": "PyALYOSjRi-N8cfg8axyMg",
"_score": null,
"_source": {
"#version": "1",
"#timestamp": "2014-11-25T11:57:56.931Z",
"host": "127.0.0.1"
},
"sort": [
1416916676931,
1416916676931
]
**"technology": "apache"**
}

Categories