MongoDB is it possible to runCommand distinct with substr on key - mongodb

Is it possible to runCommand distinct with substr on the key I'm targeting?
I keep getting missing : after property id :
db.runCommand(
{
distinct: "mycollection",
key: {"myfield" : { $substr: { "$myfield", 0, 10 } }},
}
)

Can't do this with runCommand distinct. You need to use the agg framework to process the field and then get distinct values using $group, thusly:
db.foo.aggregate([
{$group: {_id: {$substr: [ "$myfield",0,10]} }}
]);
Very often it is useful to get the count of those distinct values:
db.foo.aggregate([
{$group: {_id: {$substr: ["$myfield",0,10]}, count: {$sum:1} }}
]);

Related

find missing elements from the passed array to mongodb qyery

for example
animals = ['cat','mat','rat'];
collection contains only 'cat' and 'mat'
I want the query to return 'rat' which is not there in collection..
collection contains
[
{
_id:objectid,
animal:'cat'
},
{
_id:objectid,
animal:'mat'
}
]
db.collection.find({'animal':{$nin:animals}})
(or)
db.collection.find({'animal':{$nin:['cat','mat','rat']}})
EDIT:
One option is:
Use $facet to $group all existing values to a set. using $facet allows to continue even if the db is empty, as #leoll2 mentioned.
$project with $cond to handle both cases: with or without data.
Find the set difference
db.collection.aggregate([
{$facet: {data: [{$group: {_id: 0, animals: {$addToSet: "$animal"}}}]}},
{$project: {
data: {
$cond: [{$gt: [{$size: "$data"}, 0]}, {$first: "$data"}, {animals: []}]
}
}},
{$project: {data: "$data.animals"}},
{$project: {_id: 0, missing: {$setDifference: [animals, "$data"]}}}
])
See how it works on the playground example - with data or playground example - without data

How to batch query by an array in mongodb?

I have a table Thread:
{
userId: String
messageId: String
}
Now I have an array of userIds, I need to query 20 messageIds for each of them, I can do it with a loop:
const messageIds = {}
for (const userId of userIds) {
const results = await Thread.find({ userId }).sort({ _id: -1 }).limit(20).exec()
messageIds[userId] = results.map(result => result.messageId)
}
But of course this doesn't perform well. Is there a better solution?
The problem with your approach is that you are issuing multiple separate queries to MongoDB.
The simplest workaround to this is using the $push and $slice approach. But this has the problem that the intermediate step would creating an array of huge size.
Another way could be to use $facet as part of aggregation query.
So you need a $facet step in the aggregation like -
[
{$facet: {
'userId1': [
{$match: {userId: 'userId1'} },
{$limit: 20},
{$group: {_id: '', msg: {$push: '$messageId'} } }
],
'userId2': [
{$match: {userId: 'userId2'} },
{$limit: 20},
{$group: {_id: '', msg: {$push: '$messageId'} } }
],
.... (for each userId in array)
}}
]
You can easily just generate this query by iterating over the list of users and adding keys for each user.
So you end up with an object where key is the userId and the value is the array of messages (obj[userId].msg)
You can use aggregation to group threads by userId, and return the top 20:
db.threads.aggregate([
{$match: {userId:{$in: userIds}}},
{$sort: {_id: -1}},
{$group: {_id: "$userId", threads: {$push: "$$ROOT"}}},
{$project: {_id:0, userId:"$_id", threads: {$slice:["$threads", 20]}}}
])

MongoDB: How can I get a count of a field in a collection grouped by first character and matching a 2nd field?

Following this question's answer (https://stackoverflow.com/a/20817040/2656506) I was able to group a field based on it's first character with this command:
db.kits.aggregate({ $group: {_id: {$substr: ['$kit', 0, 1]}, count: {$sum: 1}}})
But I can't figure out how I can additionally group only those documents which match an additional condition like _id: 'abc' in the same query. Can it be done in one query?
Thanks in advance!
add $match pipeline stage to your aggregation query:
db.kits.aggregate(
[
{
$match: {
_id: 'abc'
}
},
{
$group: {
_id: {
$substr: ['$kit', 0, 1]
},
count: {$sum: 1}
}
}
]
)

count multiple distinct fields by group with Mongo

I have a data set looks as
{"BrandId":"a","SessionId":100,"UserName":"tom"}
{"BrandId":"a","SessionId":200,"UserName":"tom"}
{"BrandId":"b","SessionId":300,"UserName":"mike"}
I would like to count distinct session and username group by brandid, the sample sql is like:
select brandid,count_distinct(sessionid),count_distinct(username)
from data
group by brandid
I tried to write Mongo DB, my current code is as following and it does not work. Is there anyway to make it work?
db.logs.aggregate([
{$group:{
_id:{brand:"$BrandId",user:"$UserName",session:"$SessionId"},
count:{$sum:1}}},
{$group:{
_id:"$_id.brand",
users:{$sum:"$_id.user"},
sessions:{$sum:"$_id.session"}
}}
])
for the certain example, the expected count is
{"BrandId:"a","countSession":2,"countUser":1}
{"BrandId:"b","countSession":1,"countUser":1}
if you know SQL, the expect result is as same as the SQL I mentioned.
You can do this by using $addToSet to accumulate the distinct set of SessionId and UserName values during the $group, and then adding a $project stage to your pipeline that uses the $size operator to get the size of each set:
db.logs.aggregate([
{$group: {
_id: '$BrandId',
sessionIds: {$addToSet: '$SessionId'},
userNames: {$addToSet: '$UserName'}
}},
{$project: {
_id: 0,
BrandId: '$_id',
countSession: {$size: '$sessionIds'},
countUser: {$size: '$userNames'}
}}
])
Result:
{
"BrandId" : "b",
"countSession" : 1,
"countUser" : 1
},
{
"BrandId" : "a",
"countSession" : 2,
"countUser" : 1
}

mongoDB, sum the product of two fields

I have a list of items, and I want mongoDB return the result of the sum of their price*quantity, in other words, the total value of my items.
Schema = {
_id: ObjectId,
price: Number,
quantity: Number
}
I'm trying using the aggregation framework, or map reduce, but I can't figure out how correctly use it.
Here an there is an example for finding the sum of prices,
db.items.aggregate([
{$group: {
_id: null,
prices: {$sum: "$price"}
}}
])
Here is what I would like to obtain:
db.items.aggregate([
{$group: {
_id: null,
prices: {$sum: "$price"*"$quantity"}
}}
])
You don't need to use map-reduce for this. You can use aggregation framework and combine multiple aggregation operators. You almost got it you were just missing the final piece - $multiply operator:
db.items.aggregate([{
"$group" : {
"_id" : null,
"prices" : {
"$sum" : {
"$multiply" : ["$price", "$quantity"]
}
}
}
}]);