sum key and group by key with Mongodb and Laravel - mongodb

Having this collection -
{
"_id": "5b508587de796c0006207fa7",
"id": "1",
"status": "pending",
"updated_at": "2018-07-19 13:02:40",
"created_at": "2018-07-19 12:35:19"
},
{
"_id": "5b508587de796c0006207fa5",
"id": "2",
"status": "completed",
"updated_at": "2018-07-19 13:02:40",
"created_at": "2018-07-19 12:35:19"
},
I want to have a query that will sum the status key by the id key.
For example -
{
"id":"1",
"pending":"1"
}
I am using Laravel 5.5 with MongoDB

Here is a working MongoPlayground. Check out Mongo's reference for Aggregations, as well as the $group operator.
db.collection.aggregate([
{
$group: {
_id: "$status",
sumOfStatus: {
$sum: 1
}
}
}
])
EDIT: After proof-reading, I'm not really sure that was what you were looking for. This example will return your a list of statuses for each id such as:
[
{
"_id": "5",
"completed": 3,
"pending": 3
}
]
To do so, I'm leveraging theĀ $cond operator in order to conditionally $sum documents depending on their status value. One drawback is that you have to repeat this for each value. Not sure of a way around that.
Regarding the Laravel implementation, I'm definitely not a Laravel expert, but check out this answer which shows an example on how to access the aggregate() method.

Related

Positional operator targets wrong element of array field in `findOneAndUpdate` update query

I have a document
{
"_id": "62ac8190ddb08e6ee5f2c7dd",
"status": "NEW",
"vendor": "62ac8171ddb08e6ee5f2c7ca",
"productsInOrder": [
{
"product": "62ac8176ddb08e6ee5f2c7cd",
"amount": 1
},
{
"product": "62ac8181ddb08e6ee5f2c7d0",
"amount": 1
}
],
"createdAt": "2022-06-17T13:28:48.815Z",
"updatedAt": "2022-06-17T13:39:44.073Z",
"orderNumber": 82,
"__v": 2
}
And a query to update second element of productsInOrder array in the document
db.collection.findOneAndUpdate({
status: "NEW",
vendor: "62ac8171ddb08e6ee5f2c7ca",
"productsInOrder.product": "62ac8181ddb08e6ee5f2c7d0",
"productsInOrder.amount": {
$lte: 3
},
},
{
$inc: {
"productsInOrder.$.amount": 1
}
})
After executing the update query I expect product with id "62ac8181ddb08e6ee5f2c7d0" to be updated, i.e it's amount increased by 1
{
"_id": "62ac8190ddb08e6ee5f2c7dd",
"status": "NEW",
"vendor": "62ac8171ddb08e6ee5f2c7ca",
"productsInOrder": [
{
"product": "62ac8176ddb08e6ee5f2c7cd",
"amount": 1
},
{
"product": "62ac8181ddb08e6ee5f2c7d0",
"amount": 2 // increased by 1
}
],
"createdAt": "2022-06-17T13:28:48.815Z",
"updatedAt": "2022-06-17T13:39:44.073Z",
"orderNumber": 82,
"__v": 2
}
but instead product with id "62ac8176ddb08e6ee5f2c7cd" gets updated as seen here https://mongoplayground.net/p/UEU5LCAVjD0
I don't understand why positional operator selects the first element of the array instead of the element specified in find query
How can I update array element of the same id that is specified in find query in `"productsInOrder.product"?
Refer to the docs:
The positional $ update operator behaves ambiguously when filtering on multiple array fields.
When the server executes an update method, it first runs a query to
determine which documents you want to update. If the update filters
documents on multiple array fields, the subsequent call to the
positional $ update operator doesn't always update the required
position in the array.
Since your query specify productsInOrder.product and productsInOrder.amount, it's considered to be filtering on multiple array fields. In your case, you should use $elemMatch instead:
db.collection.update({
status: "NEW",
vendor: "62ac8171ddb08e6ee5f2c7ca",
productsInOrder: {
$elemMatch: {
product: "62ac8181ddb08e6ee5f2c7d0",
amount: {
$lte: 3
}
}
}
},
{
$inc: {
"productsInOrder.$.amount": 1
}
})
MongoPlayground

how to sort mongodb document to appear document with certain key value to appear first,

// i want to appear document with isFeatured:"true" at first
{ "_id": "625c13ea5c5d3f49f152783b",
"name": "helmet 1",
"description": "gold",
"category": [
"helmet"
],
"price": "25000",
"stock": 25,
"user": "",
"isFeatured": true // with this property to come first and "isFeatured:false" later
}
You can invoke sort on the cursor to sort the documents in myCollection on the isFeatured field in descending order.
db.myCollection.find().sort({ isFeatured: -1 })
Or on aggregation pipeline query
db.myCollection.aggregate([{ $sort: { isFeatured: -1 } }])

MongoDB get all documents with highest value in collection

Context:
I have a MongoDB full of Documents like this:
[
{
"_id": "615dc97907f597330c510279",
"code": "SDFSDFSDF",
"location": "ABC1",
"week_number": 39,
"year": 2020,
"region": "NA"
},
....
{
"_id": "615dc97907f597330c51027a",
"code": "SDFSGSGR",
"location": "ABC1",
"week_number": 42,
"year": 2021,
"region": "EU"
},
....
{
"_id": "615dc97607f597330c50ff50",
"code": "GGSFHSFS",
"location": "DEF2",
"week_number": 42,
"year": 2021,
"region": "EU",
"audit_result": {
"issues_found": true,
"comment": "comment."
}
}
]
Problem
I am trying to write an aggregation which should return all object with the highest "week_number" and highest "year". So with the example above, I want to return the full documents of _id "615dc97907f597330c51027a" and "615dc97607f597330c50ff50".
I tried multiple approaches. like first sorting and then grouping but with no success.
currently I have something like this, which seemed logical, but it returns all documents not only the one with the highest week and year
[
{
'$match': {
'$expr': {
'$max': '$week_number',
'$max': '$year'
}
}
}
]
You can do the followings in an aggregation pipeline:
$group by year and week_number; push the _id into an array for future lookup
$sort by year: -1 and week_number: -1
$limit to get the first grouping, which is the one with max year and week_number
$lookup the original documents using the previously stored array of _id in step 1
$replaceRoot to get back the documents
Here is the Mongo playground for your reference.

Mongodb aggregate to find if a user is in any other user's follower list

I collected followers list and friends list for n number of users from twitter and stored them in mongodb.
Here is a sample document:
{
"_id": ObjectId("561d6f8986a0ea57e51ec95c"),
"status": "True",
"UserId": "1489245878",
"followers": [
"1566382441",
"1155774331"
],
"followersCount": 2,
"friendsCount": 5,
"friends": [
"1135511478",
"998082481",
"565321118",
"848123988",
"343334562"
]
}
I wanted to know within my collection, are there any userids that are also in the followers list of some other documents. Lets say we have user "a", now i would like to know if user "a" is in the followers list of any other document within the same collection. I'm not sure how to do this. In case if we have, i would like to project the userid and the _id of the document that has the userid within the followers list.
I guess you can use aggregate function like below to get this result.
db.getCollection('your_collection").aggregate([
{
"$match": {
"followers": "1566382441"
}
},
{
"$project": {
"followers": 1
}
},
{
"$unwind": "$followers"
},
{
"$match": {
"followers": "1566382441"
}
},
{
"$group": {
"_id": "$followers",
"ids": {
"$addToSet": "$_id"
}
}
},
{
"$project": {
"userId": "$_id",
"ids": 1,
"_id": 0
}
}
])
I am using only a sample of your data. You can add your list of users for whom you are trying to filter in both stages of "$match". Just see if this helps.
P.S: I know its been a long time since you asked this question! But you know, its never late!

Sorting before doing GROUP BY in MongoDB

I have a mongo database for bug tracking. It contains 2 collections:
Project
{
"_id": 1,
"name": "My Project"
}
Bug
{
"_id": 1,
"project": 1,
"title": "we have a bug",
"timestamp": 1400215183000
}
On the dashboard, I want to display the latest bug of each project - up to total of 10.
So basically, when doing GROUP BY "project" field, I need to make sure it will always select the LATEST bug (by doing a pre-sort by "timestamp").
I'm not sure how to combine sorting and grouping together, thanks.
In order to get the "lastest" bug per project while limiting to 10 results:
db.collection.aggregate({
{ "$sort": { "timestamp": -1, "project": 1 } },
{ "$group": {
"_id": "$project",
"bug": {
"$first": {
"_id": "$_id",
"title": "$title",
"timestamp": "$timestamp"
}
}
}},
{ "$limit": 10 }
})
So the sorting is done by timestamp and project (as an optimization) then you do the $group and the $limit. The grouping here is just looking on the "boundaries" using $first, and just returning all of the rest of the document, which you may or may not need.
Try to actually restrict your "timestamp" range using $match first in order to optimize this.