Problem with countDocuments Mongoose: its not actually waiting for the callback function to execute - javascript

I've a problem with a Mongoose backend. i guess its an asynchronous problem. Can someone try to explain me how countDocuments works?
albumFields.features = [];
for await (const feature of features) {
await Artist.countDocuments({ _id: feature._id }, (err, count) => {
if (count > 0) {
albumFields.features.push({ _id: feature._id });
}
console.log('artist.count', albumFields.features);
});
console.log('features.forEach', albumFields.features);
}
console.log('!!!', albumFields.features);
the output im getting is:
artist.count [ { _id: '5ec09f9c99339f3ad85973c0' } ]
features.forEach [ { _id: '5ec09f9c99339f3ad85973c0' } ]
features.forEach [ { _id: '5ec09f9c99339f3ad85973c0' } ]
!!! [ { _id: '5ec09f9c99339f3ad85973c0' } ]
artist.count [ { _id: '5ec09f9c99339f3ad85973c0' },
{ _id: '5ec09fb199339f3ad85973c4' } ]
The output i would expect is:
artist.count [ { _id: '5ec09f9c99339f3ad85973c0' } ]
features.forEach [ { _id: '5ec09f9c99339f3ad85973c0' } ]
artist.count [ { _id: '5ec09f9c99339f3ad85973c0' },
{ _id: '5ec09fb199339f3ad85973c4' } ]
features.forEach [ { _id: '5ec09f9c99339f3ad85973c0' },
{ _id: '5ec09fb199339f3ad85973c4' } ]
!!! [ { _id: '5ec09f9c99339f3ad85973c0' },
{ _id: '5ec09fb199339f3ad85973c4' } ]

So the problem is countDocuments() will return a promise only if there is no call back function. In our case we created the callback which gets the number of documents matching the filter.
I am not sure why the code executed the way it did so maybe someone can explain but I did manage to solve the problem.
let arraytest = [];
for (const feature of features) {
await Artist.countDocuments({ _id: feature._id }).then((count) => {
if (count > 0) {
albumFields.features = [
...albumFields.features,
{ _id: feature._id },
];
}
});
}
For anyone else who runs into the same problem, there was a link on the mongoose documentation which is easy to miss.
https://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#countDocuments

Related

how to catch mongo change streams when one key in object gets updated

I am using the mongo change stream to update some of my data in the cache. This is the Documentation i am following.
I want to know when one of the keys in my object gets updated.
Schema :- attributes: { position: { type: Number } },
How will I come to know when the position will get updated?
This is how my updateDescription object looks like after updation
updateDescription = {
updatedFields: { 'attributes.position': 1, randomKey: '1234'},
removedFields: []
}
Tried this and its not working
Collection.watch(
[
{
$match: {
$and: [
{ operationType: { $in: ['update'] } },
{
$or: [
{ 'updateDescription.updatedFields[attributes.position]': { $exists: true } },
],
},
],
},
},
],
{ fullDocument: 'updateLookup' },
);
I was able to fix it like this,
const streamUpdates = <CollectionName>.watch(
[
{
$project:
{
'updateDescription.updatedFields': { $objectToArray: '$updateDescription.updatedFields' },
fullDocument: 1,
operationType: 1,
},
},
{
$match: {
$and: [
{ operationType: 'update' },
{
'updateDescription.updatedFields.k': {
$in: [
'attributes.position',
],
},
},
],
},
},
],
{ fullDocument: 'updateLookup' },
);
and then do something with the stream
streamUpdates.on('change', async (next) => {
//your logic
});
Read more here MongoDB

Javascript mongodb aggregation filter not working

It's my line of code you see below is not working! It always returns an empty array value.
Please help me!
That's working!
const data = await NumbersModel.aggregate([
{
$project: {
Datas: {
$filter: {
input: "$Datas",
as: "data",
cond: { $gte: [ "$$data.time", lastdata ] }
}
}
}
])
That's not working!
const data = await NumbersModel.aggregate([
{
$project: {
Datas: {
$filter: {
input: "$Datas",
as: "data",
cond: {
$and: [
{ $gte: [ "$$data.time", lastdata ], },
{ $eq: [ "$$data.number", req.query.number ] }
]
}
}
}
}
}
])
It's working :)
const data = await NumbersModel.find({ "Datas.time": { $gte: lastdata }, Number: req.query.number })

Map the nested data from other table using promise and async-await

I need the expert advice for this code. I need to know Is there any better way to solve this.
I am using the mongoose for db. I have a dataset like this:
Below is matchTable:
{
_id: 617bc0113176d717f4ddd6ce,
car: [],
status: true
},
{
_id: 617bc0113176d717f4ddd6cg,
car: [
{
aid: '5c1b4ffd18e2d84b7d6febcg',
}
],
status: true
}
And I have a Car table in which car name is there on behalf of id
like this
{ _id: ObjectId('5c1b4ffd18e2d84b7d6febce'), name: 'ford' },
{ _id: ObjectId('5c1b4ffd18e2d84b7d6febcg'), name: 'mitsubishi' },
So I want to make join the data from car table, so that response get name on behalf of aid.
Desired result will be like
{
_id: 617bc0113176d717f4ddd6ce,
car: [],
status: true
},
{
_id: 617bc0113176d717f4ddd6cg,
car: [
{
aid: '5c1b4ffd18e2d84b7d6febcg',
name: 'mitsubishi'
}
],
status: true
}
For that I have to merge the car table on matchTable. I have done this but I want to give some suggestion that is there any better way to do or is it fine. I need expert advice.
const getData = await matchTable.find(
{ status: true }
).lean().exec();
let dataHolder = [];
await Promise.all (
getData.map(async x => {
await Promise.all(
x.car.map(async y => {
let data = await Car.findOne(
{ _id: ObjectId(y.aid) },
{ name: 1 }
).lean().exec();
y.name = '';
if (data) {
y.name = data.name;
}
})
)
// If I return { ...x }, then on response it will return {}, {} on car column
dataHolder.push(x) //So I have chosen this approach
})
);
Please guide me if any better and efficient solution is there. Thanks in advance
You can make use of aggregation here.
const pipeline = [
{
$match : { status : true }
},
{
$unwind: '$matchtable',
},
{
$lookup: {
from: "cars",
localField: "car.aid",
foreignField: "_id",
as: "matchcars"
}
},
{
$addFields: {
"car.carName": { $arrayElemAt: ["$matchcars.name", 0] }
}
},
{
$group: {
_id: "$_id",
cars: { $push: "$matchcars" }
}
}
]
const result = await matchTable.aggregate(pipeline).exec();
Please make sure, aid field inside car array (in matchTable collection) is an ObjectId because its being matched to _id (which is an ObjectId) inside cars collection.

How to delete an element in a sub sub document mongoosejs

how to delete the "extras" with "id_extra = 8523" of the "book" "id_book = 8522" of that particular user?, I currently use mongoosejs but I didn't succeed, I tried many stuffs, but whatever I tried was working just till the first level of nested doc.
{
"_id":{
"$oid":"5e32fce08919b53ad66cf694"
},
"user_id":{
"$numberInt":"258787"
},
"username":"daaaa",
"firstname":"davide",
"api_key":"7b031c21edf1237c554867622ad1154f",
"books":[
{
"_id":{
"$oid":"5e356323a0ef6319ebb60162"
},
"id_book":{
"$numberInt":"8522"
},
"blob_annotation":null,
"extra_id":{
"$numberInt":"995176"
},
"extras":[
{
"_id":{
"$oid":"5e356324a0ef6319ebb60163"
},
"id_extra":{
"$numberInt":"8523"
},
"type":"gallery_audio",
"label":"Inverno a Boscodirovo"
},
{
"_id":{
"$oid":"5e356324a0ef6319ebb60164"
},
"id_extra":{
"$numberInt":"8524"
},
"type":"gallery_audio",
"label":"Storia di Primavera"
}
],
"raccolte":[
]
}
],
}
Try using elemMatch in conjunction with $:
db.books.update(
{
books: {
$elemMatch: {
"id_book": 8522,
"extras.id_extra": 8523
}
},
user_id: 258787
},
{
$pull: {
"books.$.extras": {
"id_extra": 8523
}
}
}
)
You may do this trick: Filter "extras" with "id_extra = 8523" of the "book" "id_book = 8522" and save documents again in MongoDB
db.collection.aggregate([
{
$match: {
"books.id_book": 8522
}
},
{
$addFields: {
"books": {
$reduce: {
input: "$books",
initialValue: [],
in: {
$concatArrays: [
"$$value",
[
{
_id: "$$this._id",
id_book: "$$this.id_book",
blob_annotation: "$$this.blob_annotation",
extra_id: "$$this.extra_id",
raccolte: "$$this.raccolte",
extras: {
$filter: {
input: "$$this.extras",
cond: {
$ne: [
"$$this.id_extra",
8523
]
}
}
}
}
]
]
}
}
}
}
}
])
MongoPlayground

(mongoDB) find with empty or null values

how to make a mongodb collection find (db.collection.find) with empty values?
currently i have:
function test(s) {
if (!s) {
return null;
} else {
return s;
}
}
var content = {
date: {
from: '2017-10-15',
to: '2017-11-15'
},
'name': 'some text', //this can be null or empty
'text': 'some other text' //this can be null or empty
}
col.find({
"date": {
$gte: new Date(content.date.from),
$lte: new Date(content.date.to)
},
"name": {
$ne: {
$type: null
},
$eq: test(content.name)
},
"text": {
$ne: {
$type: null
},
$eq: test(content.text)
},
}).toArray((err, items) => {
console.log(items)
});
but it returns an empty array, because "name" or "text" is null / an empty string,
i want that it query only the values that have something specified or ignore it (like content.name is something in it or its empty)
how do i get it? i already searched ... but didnt found something
thanks!
( already testet mongoDB : multi value field search ignoring null fields)
Versions:
Node: 8.9.0
(npm) mongodb: 2.2.33
mongodb: 3.4
Try using $and, $or operators. Something like.
col.find({
$and:[
{"date": {$gte: new Date(content.date.from),$lte: new Date(content.date.to)}},
{"$or":[{"name": {$ne: {$type: null}}},{"name":test(content.name)}]},
{"$or":[{"text": {$ne: {$type: null}}},{"text":test(content.text)}]}
]
}).toArray((err, items) => {
console.log(items)
});
col.find({
$and: [
{
$and: [
{
"date": {
$gte: new Date(content.date.from)
}
},
{
"date": {
$lte: new Date(content.date.to)
}
},
]
},
{
$or: [
{ 'name': null },
{ 'name': content.name }
]
},
{
$or: [
{ 'text': null },
{ 'text': content.text }
]
}
]
})
Edited:
col.find({
$and: [
{
$and: [
{
"date": {
$gte: new Date(content.date.from)
}
},
{
"date": {
$lte: new Date(content.date.to)
}
},
]
},
{
$or: [
{ 'name': null },
{ 'name':'' }
{ 'name': content.name }
]
},
{
$or: [
{ 'text': null },
{ 'text': '' },
{ 'text': content.text }
]
}
]
})
null and empty is different, you need to add one more condition for empty string in query.
Logical Query Operators

Categories

Resources