Get value of a specific field using aggregation in MongoDB - mongodb

My document:
[
{
"_id": "5f969419d40c1580f2d4aa36",
"users": {
"foo#bar.com": "baz",
"foo2#bar.com": "baz2"
}
},
{
"_id": "5f9694d4d40c1580f2d4aa38",
"users": {
"baz#test.com": "foo"
}
}
]
If i use this aggregate, i get two users. Ok. But how can i get only the value of "foo#bar.com"?
Test in https://mongoplayground.net/p/3kW2Rw6fSjh
db.collection.aggregate([
{
"$project": {
"users": {
"$objectToArray": "$users"
}
}
},
{
"$match": {
"users.k": "foo#bar.com"
}
},
{
"$project": {
"users": {
"$arrayToObject": "$users"
}
}
}
])

You can add a $filter stage after the $match stage:
{
$set: {
users: {
$filter: {
input: "$users",
cond: {
$eq: [
"$$this.k",
"foo2#bar.com"
]
}
}
}
}
},
See how it works on the playground example

Related

Extract value from an array of objects in mongodb aggregate

I am new to mongodb,
I'm trying to extract expectedDeliveryTime from the following data:
{
"from": "Giza",
"delivery_rule": [
{
"to": "Giza",
"expectedDeliveryTime": 3
},
{
"to": "Riyadh",
"expectedDeliveryTime": 2
}
]
}
I am trying to fetch expectedDeliveryTime WHERE from='Giza' and to='Riyadh'
MySQL equivalent would be SELECT expectedDeliveryTime FROM delivery_rules AS d WHERE d.from='Giza' AND d.to='Giza'
Below is part of my code
{
$lookup: {
from: 'Setting',
pipeline: [
{
$match: {
$expr: { $eq: ['$name', 'delivery_rules'] },
},
}
],
as: 'delivery_rules',
}
},
{
$addFields: {
delivery_rules: "$delivery_rules.value"
}
},
{ $unwind: '$delivery_rules' },
{
$addFields: {
delivery_rules: {
$filter: {
input: "$delivery_rules",
as: "rule",
cond: {
$eq: [
"$$rule.from",
"Giza"
]
},
}
}
}
},
{
$group: {
expectedDeliveryTime: { $first: '$delivery_rules' },
}
},
{
$project: {
_id: 0,
expectedDeliveryTime: 1,
}
}
You can solve this using simply $match and $unwind
db.collection.aggregate([
{
"$match": {
"from": "Giza"
}
},
{
"$unwind": "$delivery_rule"
},
{
"$match": {
"delivery_rule.to": "Riyadh"
}
},
{
$project: {
_id: 0,
expectedDeliveryTime: "$delivery_rule.expectedDeliveryTime"
}
}
])
Here is the Mongo playground for your reference.

Project one field from specific object element in an array mongodb

I have a collection:
{
"_id": 1,
"_deleted": false,
"customFields": [{
"fieldName": "sapID",
"value": ""
}, {
"fieldName": "salesTerritory",
"value": ""
}, {
"fieldName": "clientType",
"value": "Corporate"
}],
}
How can I project(aggregate) only the value field of the element with fieldName = "clientType":
db.collection.aggregate(
{
$project:{value:<code>}
}
)
I tried $filter but it does not work
db.collection.aggregate([
{
$project: {
"customFields.value": 1
}
}
])
mongoplayground
db.collection.aggregate([
{
$set: {
customFields: {
$map: {
input: "$customFields",
as: "c",
in: {
$cond: {
if: { "$eq": [ "$$c.fieldName", "clientType" ] },
then: { value: "$$c.value" },
else: "$$c"
}
}
}
}
}
}
])
mongoplayground
What about this?
db.collection.aggregate([
{
$project: {
customFields: {
$filter: {
input: "$customFields",
$cond: { $eq: ["$$this.fieldName", "clientType"] }
}
}
}
}
])
Mongo Playground

find available rooms querying to bookings with aggregation

I have two collections, and i want to find available rooms between two dates 2021-10-01T00:00:00.000Z and 2021-10-31T23:59:59.999Z
bookings
{from:Date, to:Date, room:ObjectId, status:Boolean}
rooms
{_id:ObjectId, code:String, status:Boolean}
Any idea?
aggregate
db.bookings.aggregate([
{
"$match": {
"$and": [
{
"from": {
"$lt": "2021-10-16T23:59:59.999Z"
}
},
{
"to": {
"$gt": "2021-10-13T23:59:59.999Z"
}
}
],
"status": true
}
},
{
"$group": {
"_id": "1",
"notAvailableRooms": {
$addToSet: "$room"
}
}
},
{
"$lookup": {
from: "rooms",
let: {
ids: "$notAvailableRooms"
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$not: {
$in: [
"$_id",
"$$ids"
]
}
},
{
$eq: [
"$status",
true
]
}
]
}
}
}
],
as: "availableRooms"
}
},
{
"$project": {
"availableRooms": 1
}
}
])
mongoplayground

Mongodb aggregate use field reference

Data:
{
"_id": "test1",
"orderStatus": "shipped",
"history": {
"pending": {startAt: '2021/03/16'},
"shipped": {startAt: '2021/03/18'},
}
}
Is it possible to access sub document by another field?
I'd like to get current order status startAt in an aggregation pipeline, for example:
db.aggregate([{$addFields: { currentStartAt: "history.$orderStatus" }}])
but it doesn't work.
Try this one:
db.collection.aggregate([
{ $set: { history: { $objectToArray: "$history" } } },
{ $set: { history: { $filter: { input: "$history", cond: { $eq: ["$orderStatus", "$$this.k"] } } } } },
{ $project: { currentStartAt: { $first: "$history.v.startAt" } } }
])
Within a mongo shell you can also do this one:
var field = db.collection.findOne({}, { orderStatus: 1 }).orderStatus;
var field = "$history." + field + ".startAt";
db.collection.aggregate([
{ $project: { currentStartAt: field } }
])
This also works but I have no idea about performance, let me know how it performs.
db.collection.aggregate([
{
"$addFields": {
"currentStartAt": {
"$arrayElemAt": [
{
"$map": {
"input": {
"$filter": {
"input": {
"$objectToArray": "$history"
},
"as": "el",
"cond": {
"$eq": [
"$orderStatus",
"$$el.k"
]
}
}
},
"in": "$$this.v.startAt"
}
},
0
]
}
}
},
{
"$project": {
"currentStartAt": 1
}
}
])
Another query doing same thing
db.collection.aggregate([
{
"$addFields": {
"currentStartAt": {
"$filter": {
"input": {
"$objectToArray": "$history"
},
"cond": {
"$eq": [
"$orderStatus",
"$$this.k"
]
}
}
}
}
},
{
"$project": {
"currentStartAt": {
"$first": "$currentStartAt.v.startAt"
}
}
}
])

Wildcard for key in mongodb query

I have a collection equivalent to:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"sides": {
"0": {
"dist": 100
},
"1": {
"dist": 10
}
}
},
{
"_id": ObjectId("5a934e000102030405000001"),
"sides": {
"0": {
"dist": 100
}
}
}
]
I would like to perform a query that return any documents that has for any key nested in sides has the key dist with a specific value. Something like:
db.collection.find({"sides.*.dist": 10})
Here * acts as a wildcard, any key would be valid in its place.
That would retrieve:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"sides": {
"0": {
"dist": 100
},
"1": {
"dist": 10
}
}
}
]
On the other hand
db.collection.find({"sides.*.dist": 100})
Would retrive both documents.
the following song and dance won't be neccessary if sides field was an array...
db.collection.find(
{
$expr: {
$gt: [{
$size: {
$filter: {
input: { $objectToArray: "$sides" },
as: "x",
cond: { $eq: ["$$x.v.dist", 10] }
}
}
}, 0]
}
})
You could get the matching elements using this
db.collection.aggregate([
{
"$project": {
"sides_array": {//Reshape the sides
"$objectToArray": "$sides"
}
}
},
{//Denormalize to get more than one matches
"$unwind": "$sides_array"
},
{
"$match": {//Condition goes here
"sides_array.v.dist": 10
}
},
{
"$group": {//Group the data back, after unwinding
"_id": "$_id",
"sides": {
"$push": "$sides_array"
}
}
},
{
"$project": {//Reshape the data
"_id": 1,
"sides": {
"$arrayToObject": "$sides"
}
}
}
])