MongoDB find $in sorting issue - node.js

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

Related

Count the number of item without duplicate in mongodb by node js

Here is my json
[
{
id:1,
name:'rakib'
},
{
id:2,
name:'sakib'
},
{
id:3,
name:'sakib'
},
{
id:4,
name:'akib'
}
]
In this, I want to count the number of names but not duplicate by node js in MongoDB
My count will be: 3 ('rakib', 'sakib', 'akib')
$group - Group by null and with $addToSet to add name to names array without duplicates.
$project - Decorate the output documents. With $size to count the size of the names array.
db.collection.aggregate([
{
$group: {
_id: null,
names: {
$addToSet: "$name"
}
}
},
{
$project: {
count: {
$size: "$names"
}
}
}
])
Sample Mongo Playground

Using $elemMatch to compare object fields

I have a need to use $elemMatch in an aggregation pipeline and I need to compare 2 fields of an object from a nested array of objects:
Example collection:
name: 'xxx',
steps: [
{
userId: 'abc',
senderId: 'abc'
},
...
]
What I'm trying to do is return all that have at least 1 step where userId = senderId.
I have tried the following, but I get an error that $expr isn't allowed as a child of $elemMatch:
{
$match: {
steps: {
$elemMatch: {
$expr: { $eq: ['$userId', '$senderId'] },
},
},
},
}
Thanks.
$elemMatch can only be used in projection.
You can workaround for comparing fields in the array as below:
$set - Create new field filteredCount with get the array size $size of filtered array.
$match - Get filteredCount greater than 0.
db.collection.aggregate({
$set: {
filteredCount: {
$size: {
$filter: {
input: "$steps",
cond: {
$eq: [
"$$this.userId",
"$$this.senderId"
]
}
}
}
}
}
},
{
$match: {
"filteredCount": {
$gt: 0
}
}
})
Sample Mongo Playground

Mongoose get position of a data

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!

How do I get an object that is inside another with nodejs and mongodb?

I need to obtain the information contained in the array named detalleInsumos through the _id of that object.
I have tried many ways, but I still have not found the solution to my problem, since it always shows me all the objects of that document, which does not work for me.
This is the document from which I need to get that information:
{
"_id":{"$oid":"5f9041196462be3c5ca1e53d"},
"codigoFinca":"000",
"nombreFinca":"PROVINCIANA",
"fechaRegistro":"2020-10-21",
"semanaRegistro":"43",
"usuarioRegistro":"cotorreo",
"trabajadoresFinca":[
{
"porcentajeRecargo":0,
"_id":{"$oid":"5f9041196462be3c5ca1e53e"},
"udpTrabajador":[
{
"unidadesAPagar":null,
"valorUnidad":"",
"areaLaborada":"2",
"semanaNormal":null,
"semanaAtrazos":null,
"_id":{"$oid":"5f9041196462be3c5ca1e53f"},
"detalleInsumos":[
{"_id":{"$oid":"5f9041196462be3c5ca1e540"},
"codigoInsumo":"20000001",
"descripcionInsumo":"NYLON X 5 KILOS",
"cantidadAplicada":"153",
"idRDI":"426715",
"idDetalleSaldo":"24070"
}
],
"codigoLabor":"101",
"nombreLabor":"AMARRE",
"loteLaboro":"1"
}
],
"codigoTrabajador":"0000",
"nombresTrabajador":"HUMBERTO MENA MOSQUERA",
"horasJornada":"10",
"horasLaboradas":"10"
}
],
"createdAt":{"$date":"2020-10-21T14:09:29.876Z"},
"updatedAt":{"$date":"2020-10-21T15:09:51.657Z"},
"__v":0
}
And this is what I have tried from nodejs:
const consultauno = await Modelo.findOne({
'trabajadoresFinca.udpTrabajador.detalleInsumos._id': new ObjectId(idInsumo)
},
{
"trabajadoresFinca.udpTrabajador.detalleInsumos": 1
});
console.log(consultauno);
You can try,
$match your condition
$reduce to iterate loop of trabajadoresFinca array, second $reduce to iterate loop of udpTrabajador array, $filter to get matching object from detalleInsumos array,
$arrayElemAt will get first object from array when condition match
$mergeObjects will merge initialValue or reduce and matching object
const consultauno = await Modelo.aggregate([
{
$match: {
"trabajadoresFinca.udpTrabajador.detalleInsumos._id": ObjectId(idInsumo)
}
},
{
$project: {
detalleInsumos: {
$reduce: {
input: "$trabajadoresFinca",
initialValue: {},
in: {
$mergeObjects: [
"$$value",
{
$reduce: {
input: "$$this.udpTrabajador",
initialValue: {},
in: {
$mergeObjects: [
"$$value",
{
$arrayElemAt: [
{
$filter: {
input: "$$this.detalleInsumos",
cond: {
$eq: ["$$this._id", ObjectId(idInsumo)]
}
}
},
0
]
}
]
}
}
}
]
}
}
}
}
}
])
Playground
Second approach, you can use $unwind,
$match your conditions
$unwind deconstruct trabajadoresFinca array
$unwind deconstruct udpTrabajador array
$unwind deconstruct detalleInsumos array
$match your conditions
$project to show required fields
const consultauno = await Modelo.aggregate([
{
$match: {
"trabajadoresFinca.udpTrabajador.detalleInsumos._id": ObjectId(idInsumo)
}
},
{ $unwind: "$trabajadoresFinca" },
{ $unwind: "$trabajadoresFinca.udpTrabajador" },
{ $unwind: "$trabajadoresFinca.udpTrabajador.detalleInsumos" },
{
$match: {
"trabajadoresFinca.udpTrabajador.detalleInsumos._id": ObjectId(idInsumo)
}
},
{
$project: {
trabajadoresFinca: "$trabajadoresFinca._id",
udpTrabajador: "$trabajadoresFinca.udpTrabajador._id",
detalleInsumos: "$trabajadoresFinca.udpTrabajador.detalleInsumos"
}
}
])
Playground

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