Mongoose get position of a data - node.js

I'm using this code to get my user leaderboard
var data = await db.models.user
.find({ points: { $ne: 0 } })
.collation({locale: "en_US", numericOrdering: true})
.sort('-points')
.limit(10)
.skip(page*10-10)
But I can't find how to get users position in the all leaderboard. How can I get it?

I don't think there is any good approach or method to do this, but you still want to do it then try using aggregate() and $unwind stage,
$match your conditions
$sort by points in descending order
$group by null and make array of all documents in docs
$unwind deconstruct docs array and pass includeArrayIndex: 'position' this will create a ordered index field position in each document, starting from zero(0),
$skip and $limit stages
var data = await db.models.user.aggregate([
{ $match: { points: { $ne: 0 } } },
{ $sort: { points: -1 } },
{
$group: {
_id: null,
docs: { $push: "$$ROOT" }
}
},
{
$unwind: {
path: "$docs",
includeArrayIndex: "position"
}
},
{ $skip: 10 },
{ $limit: page*10-10 }
])
Playground
This may heavy operation, might be take more execution time in big data!

Related

Use Mongoose aggregate to retrieve data of array inside of a schema

I have a schema in MongoDB like this.
{
productID:1,
reviews:
[
{
_id:1
likes:[{userID:1},{userID:2}],
dislikes:[{userID:3},{userID:4}],
comment:"first comment"
},
{
_id:2
likes:[{userID:1},{userID:2}],
dislikes:[{userID:3},{userID:4}],
comment:"first comment"
}
]
}
I want to fetch the likes count of a userID of a particular review for example like count of userID 2 of review id 2. I tried to get it with the help of aggregate but got stuck.
this is the code that I tried.
ProductReview.aggregate([
{ $match: { productID: productID } },
{ $match: {reviews._id:_id}}
])
but it looks like I am messing with the mongoose syntax.
To get likes by user on particular reviews then use this query
You will have to pass productID, reviewsID and userID
db.collection.aggregate([
{
$match: {
"productID": 1
}
},
{
$unwind: "$reviews"
},
{
$match: {
"reviews._id": 2
}
},
{
$unwind: "$reviews.likes"
},
{
$match: {
"reviews.likes.userID": 2
}
},
{
$group: {
_id: "$reviews.likes",
count: {
$sum: 1
}
}
},
{
$project: {
_id: 0,
userID: "$_id.userID",
count: 1
}
}
])
Mongo Playground: https://mongoplayground.net/p/wUC5tbnLC47
OLD
This returns for all reviews
Mongo Playground: https://mongoplayground.net/p/Ob5BLCAHrw1
if you want both likes and dislikes of users with one query you can use $facet
Mongo Playground: https://mongoplayground.net/p/LELfQfKjw_h

MongoDB find $in sorting issue

I need to get docs from MongoDB collection where ID's are in array:
[
'5f80a44d0179262f7c2e6a42',
'5f8c00762fae890e9c4d029c',
'5f802cf8abac1116a46bf9d4'
]
The issue is, docs are not coming in sequence of my array ID's. They are coming (1, 0, 2) for above array ID's.
How can I make them in sequence of my ID's array? I am using, NodeJs + Mongoose.
My code:
var ids = ['5f80a44d0179262f7c2e6a42','5f8c00762fae890e9c4d029c','5f802cf8abac1116a46bf9d4']
Product.find({
_id: {
$in: ids
}
})
I don't think its possible with find(), or any functionality available in MongoDB related to this,
It is possible with aggregate() but this will just fulfil your expected result,
I am not recommending to use this because this will affect performance of query response, this could be a heavy transaction.
$match your conditions
$group by null and make array of all matching objects in root
$addFields to add ids array that we search for
$unwind deconstruct ids array, this will deconstruct in sequence as per we provided in ids array
$project, $reduce input as root array and check condition if id match then return object
$replaceWith to replace root object to root
var ids = [
ObjectId("5f802cf8abac1116a46bf9d4"),
ObjectId("5f8c00762fae890e9c4d029c"),
ObjectId("5f80a44d0179262f7c2e6a42")
];
Product.aggregate([
{ $match: { _id: { $in: ids } } },
{
$group: {
_id: null,
root: { $push: "$$ROOT" }
}
},
{ $addFields: { ids: ids } },
{ $unwind: "$ids" },
{
$project: {
root: {
$reduce: {
input: "$root",
initialValue: {},
in: { $cond: [{ $eq: ["$$this._id", "$ids"] }, "$$this", "$$value"] }
}
}
}
},
{ $replaceWith: "$root" }
])
Playground

Sort subdocument using mongoose (MongoDB)

I am trying to do something very simple but an new to MongoDB! I have a document called Device and a sub-document called Movement. I want to get the last two movement sub-documents out of Device ordered by last_seen (a date). Here is what I have along with the error I am getting:
Device.findOne({device_id: "1234"}, {movements: { $sort: {last_seen: -1}, $slice: 2 }}, function(err, device){
...
});
The Error:
MongoError: >1 field in obj: { $sort: { last_seen: -1 }, $slice: 2 }
You can use aggregate:
Device.aggregate(
{ $match: { device_id: "1234"}}, // query documents (can return more than one element)
{ $unwind: '$movements'}, //deconstruct the documents
{ $sort: { '$movements.last_seen': -1}},
{ $limit: 2 },
{ $group: { _id: '$device_id', movements: { $push: '$movements }}} //reconstruct the documents
function(err, devices){
//returns an array, probably with one elements depending on `$match` query
});

Get only the last element of array mongoose

I have array in a document, and I try to receive the last element of this array.
My code is:
Post.find({_id:postId},{'comments':{'$slice':-1}});
this gives me all the object but the comments array contains only the last element.
on the other hand,
Post.find({_id:postId},{'comments':1});
give me only the comments.
I dont find how to combine the two commands together. How it can be done?
{
"users":[],
"comments":["string1","string2","string3"],
"lastValue":"Wow"
"name":"jow"
"_id": {
"$oid": "5747d6bdecfae9d0560077cc"
},
}
Thanks
You might want to use mongodb (version 3.2) aggregation $slice like that:
Post.aggregate([
{
$match: {
'_id.$oid': postId
}
},
{
$project: {
comments: {
$slice: [ "$comments", -1 ]
}
}
}
]);
In earlier versions of mongodb:
Post.aggregate([
{
$match: {
'_id.$oid': postId
}
},
{
$unwind: "$comments"
},
{
$group : {
_id: "$_id.$oid",
comment: { $last: "$comments" }
}
}
]);
I hope this helps.
db.Post.find(
{ _id: postId },
{ comments: { $slice: -1 }, _id: 0, users: 0, lastValue: 0, name: 0 },
);
In case of Mongoose, slice can work this way also,
model.find({
// condition
})
.select('fields')
.slice('array', -1) // <------ Here
.then((data) => {
// handle
})
.catch();
Just wrote pseudo code, as it might help someone.
db.collection_name.find({'name':'how'},{'comments': {$slice: -1}})

How to fetch doc on basis of the existence of another document in mongoose?

I have a collection with name post and I have one doc and its replicated doc but in replicated doc we have one field different ,some doc don't have replicated doc and that situation depends on an array field of the doc ,if that field have the userId of user then the replicated doc will exist otherwise it will not exist.
So what I want if the doc array have that id then get the replicated post and if not then original post
I have made the query but showing error I am using $exist in $cond ?
Post.aggregate([
{
$match: {
socomo_visibility: socomoId
}
},
{
$project: {
"post_stream_type": {
$cond: {
if: {
'following_users_list': {
$exist: [userId]
}
},
then: constants.POST_STREAM_TYPE.FOLLOW.value,
else: constants.POST_STREAM_TYPE.SOCIAL_CURRY_CHANNEL.value
}
}
}
}
]
You can check whether your array has some value in the boolean-expression in such way:
Do intersection of array and value using $setIntersection.
Check size of that intersection array using $size.
If the size is greater than 0 then value is present in the array. $gt will do this check.
Try the following code:
Post.aggregate([
{
$project: {
"post_stream_type": {
$cond: {
if: {$gt: [{$size: {$setIntersection: ["$following_users_list", [userId]] } }, 0] },
then: constants.POST_STREAM_TYPE.FOLLOW.value,
else: constants.POST_STREAM_TYPE.SOCIAL_CURRY_CHANNEL.value
}
}
}
}
])
okay finally I have done this without using the aggregation .
my answer for the query is
Post.find({
$or: [{
socomo_visibility: {
$elemMatch: {
$eq: socomoId
}
},
post_stream_type: constants.POST_STREAM_TYPE.SOCIAL_CURRY_CHANNEL.value,
following_users_list: {
$exists: true,
$nin: [userId]
}
},
{
socomo_visibility: {
$elemMatch: {
$eq: socomoId
}
},
post_stream_type: constants.POST_STREAM_TYPE.FOLLOW.value,
following_users_list: {
$elemMatch: {
$eq: userId
}
}
}]
})

Resources