How to count documents in pymongo with different names - mongodb

I have a document formatted as below. I am trying to get most used command in discord and this is my mongodb collection format. I am using pymongo. Previously I used postgresql, but because of the problems in hosting, I shifted to mongodb.
Any help would be appreciated...
{
'guild': guild_id,
'channel': ctx.channel.id,
'author': ctx.author.id,
'used': message.created_at,
'prefix': ctx.prefix,
'command': command,
'failed': ctx.command_failed,
}
Now the problem is I want to fetch a query...
query = """SELECT command,
COUNT(*) as "uses"
FROM commands
WHERE guild_id=$1
GROUP BY command
ORDER BY "uses" DESC
LIMIT 5;
"""
I am searching this for hours, and still I didn't get the results..

You can use mongo aggregations
In your case it can be handled with the following mongo query:
db.getCollection('yourCollection').aggregate([
{$match:{"guild": guild_id}},
{$group:{_id: "$command", uses:{$sum: 1}}},
{$sort: {"uses": -1}},
{$limit: 5}
])
To adjust this query in pymongo you can take a look at here.

records = db.Collection.aggregate(
[{"$match": {"guild": guild_id}}, {"$unwind": "$command"},
{"$group": {"_id": "$command", "uses": {"$sum": 1}}},
{"$sort": SON([("uses", -1), ("_id", -1)])}, {"$limit": 5}])
records = await records.to_list(length=5)
Well this should be in case of python.

Related

Mongo aggregate pipeline project groups on match (equivalent to SQL select sum as group)

I am new to Mongo DB and would really appreciate some help on the following query on the database. I basically need to select all the results where the ‘Outcome’ field is in ‘first’, ‘second’ or ‘third’ and project that grouped within countries as ‘medals’ (where medals were won).
I then need to do the reverse in the pipeline to select where medals were not won. I guess the equivalent in SQL would be select the sun of the entries where the outcome is in ‘first’, ‘second’ or ‘third’ as ‘medals’.And select the sum of the entries not in ‘first’, ‘second’ or ‘third’ as ‘non_medals’. Then groups the results by country.
Below is the query I have managed to come up with thus far, but cannot seem to get it right.
pipeline_4 = [
{'$match': {'Outcome': {'$in': ['first','second', 'third'] } ,'Country': {'$exists': True}}},
{'$group': {'_id': {'outcome': '$Outcome', 'country': '$Country'},
'medals': {'$sum': 1}}},
{'$project': {
'outcome': 1, 'country', 1, 'medals': 1
}},
{'$match': {'Outcome': {'$nin': ['first','second', 'third'] } ,'Country': {'$exists': True}}},
{'$group': {'_id': {'outcome': '$Outcome', 'country': '$Country'},
'non_medals': {'$sum': 1}}},
{'$project': {
'outcome': 1, 'country', 1, 'non_medals': 1
}}
]
Could anyone please advise on this? at the moment I only get the medal groups returned and they are not grouped. If you need more info please ask, as I say I am new to Mongo and may be approaching it in too much of a standard SQL way.
Thanks,
$project step is wrong
Since you define {_id:{outcome:"$foo"}} in the $group step, you have to use outcome:"$_id.outcome" in the project step.
You have two pipelines in pipeline_4 ? you need to create two requests

Sort by embedded document field

I have the following document
Previously I didn't have the embedded document vdata and I had a field named timestamp and when I ran the query below I got the latest entry.
db.TagValues.aggregate({$match: {tagDetail: "UMIS.99TOTMW.F_CV"}}, {$sort: {timestamp: -1}}, {$limit: 1})
How do I amend the above query to show the same result based on the format of my new document. I have tried the following but I get a syntax error.
db.TagValues.aggregate({$match: {t: "UMIS.99TOTMW.F_CV"}}, {$sort: {vdata.d: -1}}, {$limit: 1})
Use double quotes for the nested fields
{ "$sort": {"vdata.d": -1}}

Mongodb aggregation sort on array primative

Given documents like
{
...
name:'whatever',
games: [122, 199, 201, 222]
}
db.col.aggregate({$match:{}},
{$sort:{'games.0': -1}})
doesn't sort ... no errors ... it just doesn't sort on the first array element of the games array.
Although a query with the same syntac .. works fine
col.find({}).sort({'games.0':-1})
if I change the collection so games is an array of objects like
[ {game1:198}, {game2:201} ...]
then the aggregation works using
{$sort:{'games.game1': -1}})
what am I missing to get this to work with an array of numbers?
Try unwinding the array first by applying the $unwind operator on the array, then use $sort on the deconstructed array and finally use $group to get the original documents structure:
db.coll.aggregate([
{"$unwind": "$games"},
{"$sort": {"games": 1}},
{
"$group": {
"_id": "$_id",
"name": {"$first": "$name"},
"games": {"$push": "$games"}
}
}
])
Try this:
db.coll.aggregate([
{"$unwind": "$games"},
{"$sort": {"games": -1}}
]}
I hope this will work for you as you expected.
In mongo 3.4 find sort i.e. db.col.find({}).sort({'games.0':-1}) works as expected whereas aggregation sort doesn't.
In mongo 3.6 both find and aggregation sort works as expected.
Jira issue for that: https://jira.mongodb.org/browse/SERVER-19402
I would recommend you to update your mongo version and your aggregation query will work fine.

MongoDB - filter in find function

I am writing a query in mongo db. I want to know that how do I use substring function in find function, like I have a date string. I want to filter the date by year and then group by month.
This is what I have tried:
db.booking.find({$substr:["$bookingdatetime",5,2]:"14"}).aggregate([ {$group: { _id: {$substr: ['$bookingdatetime', 5, 2]}, numberofbookings: {$sum: 1} }} ])
Where am I going wrong ?
$substr can be used only as a string aggregation operation. You cannot use it as a operator in a find query. You can form the aggregation pipeline as below:
db.booking.aggregate([
{$project:{"year":{$substr:["$bookingdatetime",5,2]},
"numberofbookings":1,
"bookingdatetime":1}},
{$match:{"year":14}},
{$group:{"_id":{$substr: ['$bookingdatetime', 5, 2]},
numberofbookings: {$sum: 1}}}
])
or,
use a regex to match the date pattern with specific year.

Retrieve Array Of Documents in MongoDB

I have a MongoDB Document like as follows
{
"_id":1,
"name":"XYZ"
ExamScores:[
{ExamName:"Maths", UnitTest:1, Score:100},
{ExamName:"Maths", UnitTest:2, Score:80},
{ExamName:"Science", UnitTest:1, Score:90}
]
}
I Need to retrieve this document so that it has to show only Maths Array. Like as follows
{
"_id":1,
"name":"XYZ"
ExamScores:[
{ExamName:"Maths", UnitTest:1, Score:100},
{ExamName:"Maths", UnitTest:2, Score:80},
]
}
How Can I Do That ?
As #karin states there is no, normal, in query method of doing this.
In version 2.2 you can use $elemMatch to project the first matching result from ExamScores but you cannot get multiple.
That being said, the aggregation framework can do this:
db.col.aggregate([
{$unwind: '$ExamScores'},
{$match: {'ExamScores.ExamName':"Maths"}},
{$group: {_id: '$_id', name: '$name', ExamScores: {$push: '$ExamScores'}}}
])
Something like that anyway.
This has been asked before MongoDB query to limit values based on condition, the only answer there says it is not possible, but that there is a request to implement that.