MongoDB Aggregate with sum of array object values - node.js

I have a collection with the following data:
{ "id": 1,
"name": "abc",
"age" : "12"
"quizzes": [
{
"id": "1",
"time": "10"
},
{
"id": "2",
"time": "20"
}
]
},
{ "id": 2,
"name": "efg",
"age" : "20"
"quizzes": [
{
"id": "3",
"time": "11"
},
{
"id": "4",
"time": "25"
}
]
},
...
I would like to perform the MongoDB Aggregation for a sum of quizzes for each document.and set it to totalTimes field
And this is the result that I would like to get after the querying:
{ "id": 1,
"name": "abc",
"age" : "12",
"totalTimes": "30"
"quizzes": [
{
"id": "1",
"time": "10"
},
{
"id": "2",
"time": "20"
}
]
},
{ "id": 2,
"name": "efg",
"age" : "20",
"totalTimes": "36"
"quizzes": [
{
"id": "3",
"time": "11"
},
{
"id": "4",
"time": "25"
}
]
},
...
How can I query to get the sum of quizzes time?

Quite simple using $reduce
db.collection.aggregate([
{
$addFields: {
totalTimes: {
$reduce: {
input: "$quizzes",
initialValue: 0,
in: {
$sum: [
{
$toInt: "$$this.time"
},
"$$value"
]
}
}
}
}
}
])
Mongo Playground

Related

How to filter the response from mongoDB, so nested arrays will include only items that meet a condition?

My documents look like this
{
"_id": {
"$oid": "62825f71005ce00c5f0235c1"
},
"user": "jon",
"roles": {
"User": 2001,
},
"STOCK ": [
{
"sku": "BLACK-M",
"productname": "BLACK",
"sendout": 0,
"recived": 1,
"totalinstock": 40,
"location": "B32",
"_id": {
"$oid": "62826016005ce00c5f0235c8"
}
},
{
"sku": "CJNS",
"productname": "89796363",
"sendout": 0,
"recived": 45,
"totalinstock": 0,
"location": "B232",
"_id": {
"$oid": "62836f2d56b4f1ac79c99b8d"
}
}
],
"ORDERS": [
{
"date": {
"$date": "2022-06-02T15:23:58Z"
},
"OrderNumber": "745607",
"City": "xxxxx",
"Address": "yyyyyy",
"Phone": "8678678678",
"Name": "Holly ",
"Trackingnumber": 40,
"ZipCode": 00000,
"Province": "New ",
"Quantity": [
1
],
"Product_Name": [
" pants pants"
],
"SKU": [
"CJNS"
],
"_id": {
"$oid": "6298d61ba6eeec72b78332a7"
}
},
{
"date": {
"$date": "2022-06-02T15:23:58Z"
},
"OrderNumber": "748840",
"City": "xxxxx",
"Address": "yyyyyy",
"Phone": "354353454",
"Name": "Michael",
"Trackingnumber": 0,
"ZipCode": 00000,
"Province": "North",
"Quantity": [
1
],
"Product_Name": [
" pants pants"
],
"SKU": [
"CJNS"
],
"_id": {
"$oid": "6298d61ba6eeec72b78332a9"
}
}
]
}
I successful to return all the objects in STOCK or all the objects in ORDERS
Through this query
const foundUser= await User.find({"user":req.body.user},("Orders") ).exec()
Now I want to filter the response to include only items where "Trackingnumber" is different from 0
For the sample data I expect to receive only
{
"date": {
"$date": "2022-06-02T15:23:58Z"
},
"OrderNumber": "748840",
"City": "xxxxx",
"Address": "yyyyyy",
"Phone": "354353454",
"Name": "Michael",
"Trackingnumber": 0,
"ZipCode": 00000,
"Province": "North",
"Quantity": [
1
],
"Product_Name": [
" pants pants"
],
"SKU": [
"CJNS"
],
"_id": {
"$oid": "6298d61ba6eeec72b78332a9"
}
}
You can use an aggregation pipeline with a $filter for this:
db.collection.aggregate([
{
$match: {
"user": "jon"
}
},
{
$project: {
ORDERS: {
$filter: {
input: "$ORDERS",
as: "item",
cond: {$ne: ["$$item.Trackingnumber", 0]}
}
}
}
}
])
Playground example
User.find({"Orders" : {"Trackingnumber": 0} })

mongodb aggregation: get all values for the particular field in list

I have collections like:
[
{
"_id": "1",
"religion": "south",
"tested": true,
"fruit": "orange",
"created_at": 2211123333
},
{
"_id": "2",
"religion": "north",
"tested": false,
"fruit": "apple",
"created_at": 223444433
},
{
"_id": "3",
"religion": "north",
"tested": true,
"fruit": "orange",
"created_at": 234567876
}
]
if religion is south and tested is true then get all the values of fruits in list.
tried:
pipeline = [{"$match": {"$and": [{"religion": "south"}, {"tested": true}]}}, {}{"$project": {"fruit": 1, "_id": 0}}]
db.collection.aggregate(pipeline).to_list(length=None)
getting result as : [{"fruit": "orange"}, {"fruit": "apple"}]
but result should be like: {"fruit" : ["orange", "apple"]}
use $group, $addToSet and using$cond you don't need the $match stage
test it at mongoPlayground
[
{
"$group": {
"_id": null,
"fruit": {
"$addToSet": {
"$cond": [
{
"$and": [
{
"$eq": [
"$religion",
"south"
]
},
{
"$eq": [
"$tested",
true
]
}
]
},
"$fruit",
"$$REMOVE"
]
}
}
}
},
{
"$project": {
fruit: 1,
_id: 0
}
}
]

Mongoose Sort by elements in array

i have my docs like this :
[
{
"_id": "1",
"name": "Valerio",
"gender": "M",
"favorites": []
},
{
"_id": "2",
"name": "Samad",
"gender": "M",
"favorites": ["1", "3", "4"]
},
{
"_id": "3",
"name": "Ayub",
"gender": "F",
"favorites": ["1", "4"]
},
{
"_id": "4",
"name": "Apour",
"gender": "M",
"favorites": ["2"]
}
]
and I want to sort them based on _id : "1" exists in the favorites array like this :
[
{
"_id": "2",
"name": "Samad",
"gender": "M",
"favorites": ["1", "3", "4"]
},
{
"_id": "3",
"name": "Ayub",
"gender": "F",
"favorites": ["1", "4"]
},
{
"_id": "4",
"name": "Apour",
"gender": "M",
"favorites": ["2"]
},
{
"_id": "1",
"name": "Valerio",
"gender": "M",
"favorites": []
}
]
here is what I'm using but this is not sorting it's just filtering :
User.find({
$and: [
{ favorites: { $all: [mongoose.Types.ObjectId("1")] } },
{ gender: { $in:['M'] } },
],
})
i would really appreciate any help, thank you in advance.
You can add a temporary key that holds 1 if the key exists
and 0 if the key doesn't exist, sort in descending order based on this temporary key.
db.collection.aggregate([
{
"$addFields": {
"searchElemExists": {
"$cond": {
"if": {
"$in": [
"1", // <-- Value you want to be sorted based on existance
"$favorites"
]
},
"then": 1,
"else": 0
}
}
},
},
{
"$sort": {
"searchElemExists": -1
}
},
{
"$project": {
"searchElemExists": 0,
},
},
])
Playground Execution of Sample Data

How to filter results from collection the $lookup in mongoose

i want to filter the result as the following in mongodb. I use $lookup to populate the result from another collection. Please check my following code
This code below is what i get
{
"_id": "5f3d563122de0730d0f6a754",
"barcode": "1234",
"productname": "Lays Packet",
"brandName": "Lays",
"productSize": "12",
"price": "12",
"quant": "12",
"imageurl": "http://localhost:3000/images/imageurl-1597855281050.jpg",
"remaining": "12",
"creator": "3d943b957fb5db510d824c5cbd6e8f7d",
"__v": 0,
"source": [
{
"_id": "5f3a9bbc325a074240a1a815",
"firstname": "test",
"lastname": "test",
"storename": "test",
"gst": "test",
"phoneNumber": 1,
"email": "1#demo.com",
"address1": "test",
"address2": "test",
"city": "test",
"state": "test",
"country": "test",
"zip": "1",
"password": "1",
"usertype": 3,
"unique_SHOP": "3d943b957fb5db510d824c5cbd6e8f7d",
"__v": 0
}
]
},
How to retrieve only unique_SHOP and zip from source listing.I want result like the one below with one or more fields
{
"_id": "5f3d563122de0730d0f6a754",
"barcode": "1234",
"productname": "Lays Packet",
"brandName": "Lays",
"productSize": "12",
"price": "12",
"quant": "12",
"imageurl": "http://localhost:3000/images/imageurl-1597855281050.jpg",
"remaining": "12",
"creator": "3d943b957fb5db510d824c5cbd6e8f7d",
"__v": 0,
"source": [
{
"zip": "1",
"unique_SHOP": "3d943b957fb5db510d824c5cbd6e8f7d",
}
]
},
The query i use
List.aggregate([
{$match:
{ productname: { $regex: req.params.query,$options: "i" }}
},
{ $lookup:{
from: "suppliers",
localField: "creator",
foreignField: "unique_SHOP",
as: "source"
}
},
])
You can try $lookup with pipeline,
$match condition of creator id
$project to display required fields
{
$lookup: {
from: "suppliers",
as: "source",
let: { creator: "$creator" },
pipeline: [
{
$match: {
$expr: { $eq: ["$$creator", "$_id"] }
}
},
{
$project: {
_id: 0,
zip: 1,
unique_SHOP: 1
}
}
]
}
}
Playground

Synonyms, storing weights in document for relevance scoring in Elastic Search

The story: Given the example documents below and by extending them, is it possible to get the following ranking:
A search on "Cereals" results in the following ranking
Cornflakes
Rice Krispies
A search on "Rice" results in the following ranking
Basmati
Rice Krispies
The documents against the search is performed:
[{
name: "Cornflakes"
},
{
name: "Basmati"
},
{
name: "Rice Krispies"
}]
Of course, some of them does not even held the search term, so an option is to add an array of synonyms with a text value and weight with would help in computing the ranking:
[{
name: "Cornflakes",
synonyms: [
{t: 'Cereals', weight: 100},
{t: 'Sugar', weight: 100}]
},
{
name: "Basmati",
synonyms: [
{t: 'Cereals', weight: 1},
{t: 'Rice', weight: 1000}]
},
{
name: "Rice Krispies",
synonyms: [
{t: 'Cereals', weight: 10},
{t: 'Rice', weight: 1}]
}]
Is it the right approach?
What is the Elastic Search query for taking into account weighted synonyms?
I think "tags" would be a more appropriate name for the field than "synonyms".
You could use a nested type to store tags and use function score to combine the value of the tags.weight field (of the best matching tag if any) with the match score on the name field.
One such implementation could look as follows:
put test
put test/tag_doc/_mapping
{
"properties" : {
"tags" : {
"type" : "nested" ,
"properties": {
"t" : {"type" : "string"},
"weight" : {"type" : "double"}
}
}
}
}
put test/tag_doc/_bulk
{ "index" : { "_index" : "test", "_type" : "tag_doc", "_id":1} }
{"name": "Cornflakes","tags": [{"t": "Cereals", "weight":100},{"t": "Sugar", "weight": 100}]}
{ "index" : { "_index" : "test", "_type" : "tag_doc","_id":2} }
{ "name": "Basmati","tags": [{"t": "Cereals", "weight": 1},{"t": "Rice", "weight": 1000}]}
{ "index" : { "_index" : "test", "_type" : "tag_doc","_id":3} }
{ "name": "Rice Krispies", "tags": [{"t": "Cereals", "weight": 10},{"t": "Rice", "weight": 1}]}
post test/_search
{
"query": {
"dis_max": {
"queries": [
{
"match": {
"name": {
"query": "cereals",
"boost": 100
}
}
},
{
"nested": {
"path": "tags",
"query": {
"function_score": {
"functions": [
{
"field_value_factor": {
"field": "tags.weight"
}
}
],
"query": {
"match": {
"tags.t": "cereals"
}
},
"boost_mode": "replace",
"score_mode": "max"
}
},
"score_mode": "max"
}
}
]
}
}
}
Result :
"hits": {
"total": 3,
"max_score": 100,
"hits": [
{
"_index": "test",
"_type": "tag_doc",
"_id": "1",
"_score": 100,
"_source": {
"name": "Cornflakes",
"tags": [
{
"t": "Cereals",
"weight": 100
},
{
"t": "Sugar",
"weight": 100
}
]
}
},
{
"_index": "test",
"_type": "tag_doc",
"_id": "3",
"_score": 10,
"_source": {
"name": "Rice Krispies",
"tags": [
{
"t": "Cereals",
"weight": 10
},
{
"t": "Rice",
"weight": 1
}
]
}
},
{
"_index": "test",
"_type": "tag_doc",
"_id": "2",
"_score": 1,
"_source": {
"name": "Basmati",
"tags": [
{
"t": "Cereals",
"weight": 1
},
{
"t": "Rice",
"weight": 1000
}
]
}
}
]
}

Resources