MongoDb: Retrieve all documents higher than the average value of a column - json

I have created a collection using this piece of code.
db.createCollection("item", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["_id", "item_name", "unit_price"],
properties: {
_id: {
bsonType: "string",
description: "must be a string and is required",
minLength: 3,
maxLength: 5,
pattern: "I[0-9]*$"
},
item_name: {
bsonType: "string",
description: "must be a string and is required"
},
unit_price: {
bsonType: "double"
}
}
}
},
validationLevel: "moderate"
})
and I have inserted records in the Item collection. Now I wish to list the items whose "unit_price" is less than the average price of all items.
What I have tried is
db.item.aggregate([{
$group: {
_id: null,
averageUnitPrice: {
$avg: "$unit_price"
}
}
}])
I have tried to use the above piece of code but I am not able to figure out how to take this average and use it to retrieve the documents higher than averageUnitPrice. Any help is highly appreciated!! Thanks a tonn!!!.

So I finally figured it out. This query will give me the average and the list of items which are less than the average value.
db.item.aggregate([{
$group: {
_id: null,
avg_price: {
$avg: "$unit_price"
},
unit_price: {
"$addToSet": "$unit_price"
}
},
},{
$project: {
avg_price: "$avg_price",
unit_price: {
$filter: {
input: "$unit_price",
as: "unit_prices",
cond: {
$lt: ["$$unit_prices", "$avg_price"]
}
}
}
}
}
])

Related

How to output object of an array in the root of the document in MongoDB using aggregation?

I have this document :
{"_id":"1", "elem":"ok",
"arrayOfObjects":[
{"type":"k","fieldx":"lol"},
{"type":"SndObject","fieldy":"foo"},
{"type":"Object1","fieldx":"bob"}
]
}
what is the aggregation to have this output:
{"_id":"1", "elem":"ok",
"Object1":[
{"type":"Object1","fieldx":"lol"},
{"type":"Object1","fieldx":"bob"}
],
"SndObject":[{"type":"SndObject","fieldy":"foo"}]
}
I found a way out, but it need me to know all the type i have:
{
"$addFields" : {
"Object1" : {
"$filter": {
"input": "$arrayOfObjects",
"as": "types",
"cond": {
"$and": [{ "$eq": [ "$$types.type", "Object1" ] }]
}
}
}
}
}
It would be best if i can loop over my arrayOfObjects and get the same result without pre knowledge of the type.
Might be there would be more easy option than this,
$unwind deconstruct arrayOfObjects array
$group by _id, type and elem, construct array of arrayOfObjects
$arrayToObject convert k and v from array to object
$group by _id and merge objects in root
db.collection.aggregate([
{ $unwind: "$arrayOfObjects" },
{
$group: {
_id: {
type: "$arrayOfObjects.type",
_id: "$_id"
},
elem: { $first: "$elem" },
arrayOfObjects: { $push: "$arrayOfObjects" }
}
},
{
$group: {
_id: "$_id._id",
elem: { $first: "$elem" },
arrayOfObjects: {
$mergeObjects: {
$arrayToObject: [[
{
k: "$_id.type",
v: "$arrayOfObjects"
}
]]
}
}
}
}
])
Playground

mongodb $lookup has_many association from embedded document

I have a boards collection, a lists collection, and a cards collection. An array of lists is embedded in a board document. I am trying to get an output that looks like this:
{
_id: 1,
title: "a board",
lists: [
{
_id: 1,
title: "a list",
cards: [ { _id: 1, title: "a card", list_id: 1 }, { _id: 2, title: "another card", list_id: 1 } ]
},
...
]
}
I want to nest the cards in the list it belongs to. The card document has a list_id field. I tried this:
db.boards.aggregate([
{ '$match' => { _id: 1 } },
{ '$lookup' => {
from: "cards",
localField: "lists._id",
foreignField: "list_id",
as: "cards"
} },
])
But that resulted in:
{
_id: 1,
title: "a board",
lists: [ { _id: 1, title: "a list" } ],
cards: [ { _id: 1, title: "a card", list_id: 1 }, { _id: 2, title: "another card", list_id: 1 } ]
}
I know that I have to use $unwind to get the result I want, but I can't get it to work
You need one additional aggregation pipeline step to "merge" these two lists and you can achieve it by running $map with embedded $filter. It simply works as a "join" operation on two arrays:
{
$project: {
_id: 1,
title: 1,
lists: {
$map: {
input: "$lists",
as: "list",
in: {
$mergeObjects: [
"$$list",
{
cards: {
$filter: {
input: "$cards",
cond: { $eq: [ "$$this.list_id", "$$list._id" ] }
}
}
}
]
}
}
}
}
}
Mongo Playground

Map mongo aggregation result to array

I have a mongo query that looks like this:
db.myCollection.aggregate([ {
$group: {
_id: null,
price: {
$sum: "$price"
},
inventory: {
$sum: "$inventory"
}
}
}])
Which returns
{
"_id" : null,
"price" : 26,
"inventory" : 5,
}
I would like a query which would rather return something like this:
[
{
name: "price",
amount: 26
},
{
name: "inventory",
amount: 5
}
]
EDIT:
How would I write this in Java with Spring Data? I can group and sum but don't know how to project it?
Aggregation aggregation = newAggregation(
group("$id")
.sum("price").as("price")
.sum("inventory").as("inventory")
);
You'll need to use $project. It allows us to define which fields will be returned, as well as their format.
db.myCollection.aggregate([{
$group: {
_id: null,
price: {
$sum: "$price"
},
inventory: {
$sum: "$inventory"
}
}
},
{
$project: {
_id: 0 //removes _id from result
something: [
{name: "price", amount: "$price"},
{name: "inventory", amount: "$inventory"}
]
}
}])
That will give you:
{
"something" : [
{
"name" : "price",
"amount" : 26
},
{
"name" : "inventory",
"amount" : 5
}
]
}
You can use below aggregation
db.collection.aggregate([
{ "$project": {
"data": {
"$map": {
"input": { "$objectToArray": "$$ROOT" },
"in": { "name": "$$this.k", "amount": "$$this.v" }
}
}
}},
{ "$unwind": "$data" },
{ "$replaceRoot": { "newRoot": "$data" }},
{ "$match": { "amount": { "$ne": null }}
}
])

Stuck to figure out how to access a certain field to update

I have this JSON FILE
{
"_id": "GgCRguT8Ky8e4zxqF",
"services": {
"emails": [
{
"address": "Abunae#naa.com",
"verified": false,
"verifiedMail": "Toto#hotmail.com"
}
],
"profile": {
"name": "Janis"
},
"pushIds": []
}
I want to update my verifiedMail field but couldn't figure out how to do it in Meteor, it's always returning me an error
let VerifiedEmail = "Exemple1"
await Meteor.users.update({ _id: user._id }, { $set: { 'emails.verifiedEmail': emailRefactor} }, { upsert: true })
Couldn't figure out how to access the emails.verifiedEmail field
Tried this exemlpe worked like a charm
let VerifiedEmail = "Exemple1"
await Meteor.users.update({ _id: user._id }, { $set: { 'profile.name': emailRefactor} }, { upsert: true })
but couldn't figure out how to access emails.verifiedEmail .
Could you please help me ?
Emails is an array, while profile is an object. You have to access the first object of the email array instead
This updates the exact email address from emails
Meteor.users.update({
"emails.address": emailRefactor
}, {
$set: {
"emails.$.verified": true
}
});
Or update the first element
Meteor.users.update({
_id: user._id,
"emails.address": emailRefactor
}, {
$set: {
"emails.0.verified": true
}
});
You're trying to set verifiedEmail while the actual field is verifiedMail.

Add some data in aggregation with $match in MongoDb

I am using mongoDb, restheart and also new in these technologies. I want to add one more field in aggregation. So my exist aggregation working fine. I am sharing aggregation
Aggregation:
aggrs: [{
type: "pipeline",
uri: "by_time",
stages: [{
_$match: {
bus::destination: {
_$in: {
_$var: "stands"
}
},
bus::eta: {
_$gte: {
_$var: "dateFrom"
},
_$lte: {
_$var: "dateTo"
}
}
}
},
]
}]
Json Data:
{
_id: "1938378_32323",
bus: {
valid: true,
eta: {
$date: 1500661200000
},
busDate: {
$date: 1500595200000
},
etd: {
$date: 1500661200000
},
origin: "kalu",
destination: "cell",
},
apose: false
}
Above aggregation working fine, But I want to add "apose" in aggregation. So I have implemented this code for aggregation.
stages: [{
_$match: {
bus::destination: {
_$in: {
_$var: "stands"
}
},
bus::eta: {
_$gte: {
_$var: "dateFrom"
},
_$lte: {
_$var: "dateTo"
}
},
"apose": {
"_$eq": {
"_$var": "opposit"
}
}
}
},
]
But When I hit this aggregation, I got only
{"_embedded":[],"_size":0,"_total_pages":0,"_returned":0}
My hitting url is
http:...abc.../_aggrs/by_time?avars={"stands":["KALU","MAL","SALU"],"dateFrom":{"$date":"2007-12-21T01:30:00Z"},"dateTo":{"$date":"2010-02-15T09:30:00Z"},"opposit":"false"}
your answer is valuable for me. Thanks