Count from mongo of the same query with limit 1 (Node.js) - mongodb

My query is :
event.find({
id:{$get:id},
num:"5"
}).limit(1)
but i also do the same query and get count result (from the same query)
so 1 result is the last one with limit 1 and second is the count without the limit
how do i do that in MongoDB ?
one option i was think about is do the same query but without limit , for example, get 2000 results and count in node.js code.
my node code:
result = await this.collection.find({
'val':obj.val,
'id' : {$lt: id}
}).sort({id:-1}).limit(1).project({_id:0})
let count = await result.count()
but count always return 1 (because the count should ignore the id option)
is it possible?
example:
request is name=yy and id =3
1- { id :3 , name:yy },
2 - {id : 2 , name:yy}
another example
result will be : row 2 (3 greater than 2) . with count = 2
1- { id :3 , name:yy },
2 - {id : 2 , name:xx}
result will be : [] with count = 1 . (the same query without id < id)

You can try below aggregation using $facet
result = await this.collection.aggregate([
{ "$facet": {
"count": [
{ "$match": { "id": { "$lte": id }}},
{ "$count": "count" }
],
"data": [
{ "$match": { "val": obj.val, "id" : id }},
{ "$sort": { "id": -1 }},
{ "$limit": 1 },
{ "$project": { "_id": 0 }}
]
}}
])
result = result[0].data
const count = result[0].count

Related

MongoDB - Homework Help. How to group and count

For each subject type, list the name of the subject type and the total number of subjects that belong to the subject type.
Below is the database:
Pastebin subject.js
What I've tried
For the first statement:
db.Subject.aggregate([{"$group":{"_id":"subject.$type","count":{"$sum":1}}}])
result: { "_id" : "subject.$type", "count" : 7 }
db.Subject.aggregate([{"$unwind":"$subject"},{"$group":{"_id":"$type"}}])
result: { "_id" : null }
db.Subject.aggregate([{"$unwind":"$subject"},{"$group":{"_id":{"subject.type":"$subject.type"}},"count":{"$sum":1}}])
Group by _id with "$subject.type".
db.Subject.aggregate([
{
"$group": {
"_id": "$subject.type",
"count": {
"$sum": 1
}
}
}
])
Demo # Mongo Playground

How to group and return empty results, even if not matching documents were found?

I have 2 queries:
aggregate( [
{
$match: {"user_id": 5918725, "shop_id": 1775207, "$text": {"$search" : "API"}}
},
{
$group: {
_id: "_id",
count: {
$sum: {
$cond: [ { $eq: [ "$deleted_at", null ] }, 1, 0 ]
}
}
}
}
]);
and
aggregate( [
{
$match: {"user_id": 5918725, "shop_id": 1775207, "$text": {"$search" : "APIU"}}
},
{
$group: {
_id: "_id",
count: {
$sum: {
$cond: [ { $eq: [ "$deleted_at", null ] }, 1, 0 ]
}
}
}
}
]);
they are the same, only difference is a search keyword, but they behave differently,
in case of the first query, result is :
{
"_id" : null,
"count" : 0.0
}
which is expected result, but in case of the second one, result is Fetched 0 record(s) (that is what my GUI shows). So for some reason in case of the second query it simply ignore group/sum and trying to get actual records. How can I solve it, thank you in advance!
Query
the easy way to do it its on the driver, if no results you make this document with code
this is the way to do it on the database, with 2 extra stages, union and group, and one extra collection (in mongodb 5.3 we wouldnt need the extra collection)
put for example instead of 5 a negative number and even if no results you still get count : 0
Test code here
aggregate(
[{"$match":{"$expr":{"$lt":["$a", 5]}}},
{"$group":{"_id":null, "count":{"$sum":1}}},
{"$unionWith":{"coll":"2"}},
{"$group":{"_id":null, "count":{"$sum":"$count"}}}])

How to select custom data in mongo

Is there a way how to include custom data in the mongo query response?
What I mean is a mongo alternative for something like this in MySQL code:
SELECT
value,
'7' AS min_value
FORM
my_table
WHERE
value >= 7
...while the 7 should probably be a variable in the language where the mongo query is being called from.
Try the $literal operator if using the aggregation framework with a $match pipeline step as your query filter. For example, create a sample collection in mongo shell that has 10 test documents with the value field as an increasing integer (0 to 9):
for(x=0;x<10;x++){ db.my_table.insert({value: x }) }
Running the following aggregation pipeline:
var base = 7;
db.my_table.aggregate([
{
"$match": {
"value": { "$gte": base }
}
},
{
"$project": {
"value": 1,
"min_value": { "$literal": base }
}
}
])
would produce the result:
/* 0 */
{
"result" : [
{
"_id" : ObjectId("561e2bcc3d8f561c1548d39b"),
"value" : 7,
"min_value" : 7
},
{
"_id" : ObjectId("561e2bcc3d8f561c1548d39c"),
"value" : 8,
"min_value" : 7
},
{
"_id" : ObjectId("561e2bcc3d8f561c1548d39d"),
"value" : 9,
"min_value" : 7
}
],
"ok" : 1
}
The only things in MongoDB query actions that actuallly "modify" the results returned other than the original document or "field selection" are the .aggregate() method or the JavaScript manipulation alternate in mapReduce.
Otherwise documents are returned "as is", or at least with just the selected fields or array entry specified.
So if you want something else returned from the server, then you need to use one of those methods:
var seven = 7;
db.collection.aggregate([
{ "$match": {
"value": { "$gt": seven }
}},
{ "$project": {
"value": 1,
"min_value": { "$literal": seven }
}}
])
Where the $literal operator comes into play, or in versions prior to 2.6 and greater or equal to 2.2 ( aggregation framework introduced ) can use $const instead:
var seven = 7;
db.collection.aggregate([
{ "$match": {
"value": { "$gt": seven }
}},
{ "$project": {
"value": 1,
"min_value": { "$const": seven }
}}
])
Or just use mapReduce and it's JavaScript translation:
var seven = 7;
db.mapReduce(
function() {
emit(this._id,{ "value": this.value, "min_value": seven });
},
function() {}, // no reduce at all since all _id unique
{
"out": { "inline": 1 },
"query": { "value": { "$gt": seven } },
"scope": { "seven": seven }
}
);
Those are basically your options.

Mongodb: find documents with array field that contains more than one SAME specified value

There is three documents in collection test:
// document 1
{
"id": 1,
"score": [3,2,5,4,5]
}
// document 2
{
"id": 2,
"score": [5,5]
}
// document 3
{
"id": 3,
"score": [5,3,3]
}
I want to fetch documents that score field contains [5,5].
query:
db.test.find( {"score": {"$all": [5,5]}} )
will return document 1, 2 and 3, but I only want to fetch document 1 and 2.
How can I do this?
After reading your problem I personally think mongodb not supported yet this kind of query. If any one knows about how to find this using mongo query they defiantly post answers here.
But I think this will possible using mongo forEach method, so below code will match your criteria
db.collectionName.find().forEach(function(myDoc) {
var scoreCounts = {};
var arr = myDoc.score;
for (var i = 0; i < arr.length; i++) {
var num = arr[i];
scoreCounts[num] = scoreCounts[num] ? scoreCounts[num] + 1 : 1;
}
if (scoreCounts[5] >= 2) { //scoreCounts[5] this find occurrence of 5
printjsononeline(myDoc);
}
});
Changed in version 2.6.
The $all is equivalent to an $and operation of the specified values; i.e. the following statement:
{ tags: { $all: [ "ssl" , "security" ] } }
is equivalent to:
{ $and: [ { tags: "ssl" }, { tags: "security" } ] }
I think you need to pass in a nested array -
So try
db.test.find( {"score": {"$all": [[5,5]]}} )
Source
Changed in version 2.6.
When passed an array of a nested array (e.g. [ [ "A" ] ] ), $all can now match documents where the field contains the nested array as an element (e.g. field: [ [ "A" ], ... ]), or the field equals the nested array (e.g. field: [ "A" ]).
http://docs.mongodb.org/manual/reference/operator/query/all/
You can do it with an aggregation. The first step can use an index on { "score" : 1 } but the rest is hard work.
db.test.aggregate([
{ "$match" : { "score" : 5 } },
{ "$unwind" : "$score" },
{ "$match" : { "score" : 5 } },
{ "$group" : { "_id" : "$_id", "sz" : { "$sum" : 1 } } }, // use $first here to include other fields in the results
{ "$match" : { "sz" : { "$gte" : 2 } } }
])

MongoDB - Query all documents createdAt within last hours, and group by minute?

From reading various articles out there, I believe this should be possible, but I'm not sure where exactly to start.
This is what I'm trying to do:
I want to run a query, where it finds all documents createAt within the last hour, and groups all of them by minute, and since each document has a tweet value, like 5, 6, or 19, add them up for each one of those minutes and provides a sum.
Here's a sample of the collection:
{
"createdAt": { "$date": 1385064947832 },
"updatedAt": null,
"tweets": 47,
"id": "06E72EBD-D6F4-42B6-B79B-DB700CCD4E3F",
"_id": "06E72EBD-D6F4-42B6-B79B-DB700CCD4E3F"
}
Is this possible to do in mongodb?
#zero323 - I first tried just grouping the last hour like so:
db.tweetdatas.group( {
key: { tweets: 1, 'createdAt': 1 },
cond: { createdAt: { $gt: new Date("2013-11-20T19:44:58.435Z"), $lt: new Date("2013-11-20T20:44:58.435Z") } },
reduce: function ( curr, result ) { },
initial: { }
} )
But that just returns all the tweets within the timeframe, which technically is what I want, but now I want to group them all by each minute, and add up the sum of tweets for each minute.
#almypal
Here is the query that I'm using, based off your suggestion:
db.tweetdatas.aggregate(
{$match:{ "createdAt":{$gt: "2013-11-22T14:59:18.748Z"}, }},
{$project: { "createdAt":1, "createdAt_Minutes": { $minute : "$createdAt" }, "tweets":1, }},
{$group:{ "_id":"$createdAt_Minutes", "sum_tweets":{$sum:"$tweets"} }}
)
However, it's displaying this response:
{ "result" : [ ], "ok" : 1 }
Update: The response from #almypal is working. Apparently, putting in the date like I have in the above example does not work. While I'm running this query from Node, in the shell, I thought it would be easier to convert the var date to a string, and use that in the shell.
Use aggregation as below:
var lastHour = new Date();
lastHour.setHours(lastHour.getHours()-1);
db.tweetdatas.aggregate(
{$match:{ "createdAt":{$gt: lastHour}, }},
{$project: { "createdAt":1, "createdAt_Minutes": { $minute : "$createdAt" }, "tweets":1, }},
{$group:{ "_id":"$createdAt_Minutes", "sum_tweets":{$sum:"$tweets"} }}
)
and the result would be like this
{
"result" : [
{
"_id" : 1,
"sum_tweets" : 117
},
{
"_id" : 2,
"sum_tweets" : 40
},
{
"_id" : 3,
"sum_tweets" : 73
}
],
"ok" : 1
}
where _id corresponds to the specific minute and sum_tweets is the total number of tweets in that minute.