mongodb - check a collection for mutual likes - mongodb

I had a query that worked before when likes collection had users stored as from and to fields. The query below checked for the two fields and if like was true.
Since then I changed the way users are stored in the likes collection.
.collection('likes')
.aggregate([ {$match: {$and : [{$or: [{from: userId}, {to: userId}]}, {like: true}]}},
{$group: {
_id: 0,
from: {$addToSet: "$from"},
to: {$addToSet: "$to"},
}
},
{$project: {
_id: 0,
users: {
$filter: {
input: {$setIntersection: ["$from", "$to"]},
cond: {$ne: ["$$this", userId]}
}
}
}
},
This is how likes collection used to store data (and above query worked) in figuring out mutual likers for a userId passed in req.body
{
"_id": "xw5vk1s_PpJaal46di",
"from": "xw5vk1s",
"to": "PpJaal46di"
"like": true,
}
and now I changed users to an array.
{
"_id": "xw5vk1s_PpJaal46di",
"users": [
"xw5vk1s",//first element equivalent to from
"PpJaal46di"//equivalent to previously to
],
"like": true,
}
I am not sure how to modify the query to now check for array elements in users field now that from and to is not where the two users liking each other are stored.

For MongoDB v5.2+, you can use $sortArray to create a common key for the field users and $group to get the count. You will get mutual likes by count > 1.
db.collection.aggregate([
{
$match: {
users: "xw5vk1s"
}
},
{
$group: {
_id: {
$sortArray: {
input: "$users",
sortBy: 1
}
},
count: {
$sum: 1
}
}
},
{
"$match": {
count: {
$gt: 1
}
}
}
])
Mongo Playground
Prior to MongoDB v5.2, you can create sorted key for grouping through $unwind and $sort then $push back the users in $group.
db.collection.aggregate([
{
$match: {
users: "xw5vk1s"
}
},
{
"$unwind": "$users"
},
{
$sort: {
users: 1
}
},
{
$group: {
_id: "$_id",
users: {
$push: "$users"
},
like: {
$first: "$like"
}
}
},
{
"$group": {
"_id": "$users",
"count": {
"$sum": 1
}
}
},
{
"$match": {
count: {
$gt: 1
}
}
}
])
Mongo Playground

Related

Sort Mongodb documents by seeing if the _id is in another array

I have two collections - "users" and "follows". "Follows" simply contains documents with a "follower" field and a "followee" field that represent when a user follows another user. What I want to do is to be able to query the users but display the users that I (or whatever user is making the request) follow first. For example if I follow users "5" and "14", when I search the list of users, I want users "5" and "14" to be at the top of the list, followed by the rest of the users in the database.
If I were to first query all the users that I follow from the "Follows" collection and get an array of those userIDs, is there a way that I can sort by using something like {$in: [userIDs]}? I don't want to filter out the users that I do not follow, I simply want to sort the list by showing the users that I do follow first.
I am using nodejs and mongoose for this.
Any help would be greatly appreciated. Thank you!
Answer
db.users.aggregate([
{
$addFields: {
sortBy: {
$cond: {
if: {
$in: [ "$_id", [ 5, 14 ] ]
},
then: 0,
else: 1
}
}
}
},
{
$sort: {
sortBy: 1
}
},
{
$unset: "sortBy"
}
])
Test Here
If you don't want you on the list, then
db.users.aggregate([
{
$addFields: {
sortBy: {
$cond: {
if: {
$in: [ "$_id", [ 5, 14 ] ]
},
then: 0,
else: 1
}
}
}
},
{
$sort: {
sortBy: 1
}
},
{
$unset: "sortBy"
},
{
$match: {
"_id": { $ne: 1 }
}
}
])
Test Here
If you want to sort users first
db.users.aggregate([
{
$sort: {
_id: 1
}
},
{
$addFields: {
sortBy: {
$cond: {
if: {
$in: [
"$_id",
[
5,
14
]
]
},
then: 0,
else: 1
}
}
}
},
{
$sort: {
sortBy: 1,
}
},
{
$unset: "sortBy"
},
{
$match: {
"_id": {
$ne: 1
}
}
}
])
Test Here

when i use aggregate match before and after group in mongodb, all result will appear (before + after) match filter?

Here is my mongo query:
I have order collection. I have to count those order which customer first-time order comes from affillite.
db.Orders.aggregate([
{ $match:{ "Status": { $ne: "PaymentFailed" } }},
{
"$group": {
_id: "$UserId",
count: {$sum: 1},
Order: {
$first: {
OrderNumber: "$OrderNumber",
CreatedOn:"$CreatedOn",
TrackingId:"$Tracking.AffiliateId"
}
}
},
"$match":{ "Order.TrackingId": { $not: { $type: 10 } }, "Order.TrackingId": { $exists: true } }
},
])

Return original documents only from mongoose group/aggregation operation

I have a filter + group operation on a bunch of documents (books). The grouping is to return only latest versions of books that share the same book_id (name). The below code works, but it's untidy since it returns redundant information:
return Book.aggregate([
{ $match: generateMLabQuery(rawQuery) },
{
$sort: {
"published_date": -1
}
},
{
$group: {
_id: "$book_id",
books: {
$first: "$$ROOT"
}
}
}
])
I end up with an array of objects that looks like this:
[{ _id: "aedrtgt6854earg864", books: { singleBookObject } }, {...}, {...}]
Essentially I only need the singleBookObject part, which is the original document (and what I'd be getting if I had done only the $match operation). Is there a way to get rid of the redundant _id and books parts within the aggregation pipeline?
You can use $replaceRoot
Book.aggregate([
{ "$match": generateMLabQuery(rawQuery) },
{ "$sort": { "published_date": -1 }},
{ "$group": {
"_id": "$book_id",
"books": { "$first": "$$ROOT" }
}},
{ "$replaceRoot": { "newRoot": "$books" } }
])

Project the size of a set type array in mongodb

I am not able to display the size of an array in projection and groupby. please find my below query.
[
{
"$unwind":"$a"
},
{
$group:{
_id:"$a.acid",
"sessions":{
$sum:1
},
"users":{
$addToSet:"$cid"
}
}
},
{
$project:{
"sessions":1,
"users":1
}
}
]
By the above query I can display all the users, But I want to display the size of the users set(array) only. Could anyone help me on this
You can use $size in mongo like following query:
db.collection.aggregate[{
"$unwind": "$a"
}, {
$group: {
_id: "$a.acid",
"sessions": {
$sum: 1
},
"users": {
$addToSet: "$cid"
}
}
}, {
$project: {
"sessions": 1,
"users": 1,
"size": {
$size: "$users"
}
}
}
])
EDIT AFTER OP's comment that it gives invalid operator error-
You can do something like again unwind users array and count number of users. But this involves again unwinding array so not recommended, The query will be something like following (Not tested):
db.collection.aggregate([{
"$unwind": "$a"
}, {
$group: {
_id: "$a.acid",
"sessions": {
$sum: 1
},
"users": {
$addToSet: "$cid"
}
}
}, {
$unwind: "$users"
}, {
$group: {
_id: "$_id",
"count": {
$sum: 1
},
"sessions": {$push:"$sessions"},// u can use "sessions":{$first:"$sessions"} also.
"users": {
$push: "$users"
}
}
}])

Mongodb - count of items using addToSet

I grouped by organization and used $addToSet to show the distinct machineIds associated with that organization. I would like to get the count of machineIds for each organization. However the code below is returning a count of all machineIds, not the count of distinct ones. Is there another way to get the total unique machineIds?
db.getCollection('newcollections').aggregate([{
$group: {
_id: {
organization: "$user.organization"
},
machineId: {
"$addToSet": "$user.machineId"
},
count: {
$sum: 1
}
}
}])
You need to use $size operator in projection like following:
db.collection.aggregate([{
$group: {
_id: {
organization: "$user.organization"
},
machineId: {
"$addToSet": "$user.machineId"
}
}
}, {
$project: {
"organization": "$_id.organization",
"machineId": 1,
"_id": 0,
"size": {
$size: "$machineId"
}
}
}])