How to do pagination with a mongodb aggregate? - node.js

While working on MongoDB.I have a problem with doing Pagination.When I'm trying to include Paginaiton with aggerate.I'm also trying to include facets in this.
My code: Just for doing search
app.get("/search", async(req, res) => {
try {
const text = req.query.text
let result = await collection.aggregate([
{
'$search': {
'text': {
'query': `${text}`,
'path': 'title'
}
}
}
]).toArray();
res.send(result)
} catch (error) {
console.error(error)
}
})
This works for both search as well as pagination.
like this, see, It doesn't require any optional request.query.page.
http://localhost:4000/search?text=mango
http://localhost:4000/search?text=mango?page=1
Now, I want to include the pagination with facets search as well...So,
server.get("/search", async(req, res) => {
try {
const key = req.query.key;
const value = req.query.value;
const text = req.query.text;
const page = req.query.page; //Page query create
let result = await collection.aggregate([
{
'$search': {
'text': {
'query': `${text}`,
'path': 'title'
}
}
},
{
'$match': {
[key]: `${value}`
}
}
]).toArray();
res.send(result)
} catch (error) {
console.error(error)
}
})
work for this: without no.of Pages
http://localhost:4000/search?text=Mango&key=Brand&value=rasna
Doesn't work for Pagination:
http://localhost:4000/search?text=Mango&key=Brand&value=rasna&page=2
where I'm wrong here? Do I need to create any additional function to make this works or Something else?

you can use both $skip and $limit aggregation pipelines to achieve this purpose. imagine that we want to have only 20 items per page. so our code looks like this:
server.get("/search", async(req, res) => {
try {
const key = req.query.key;
const value = req.query.value;
const text = req.query.text;
const page = req.query.page - 1; //We subtract one because we don't want skip first twenty items in first page
let result = await collection.aggregate([
{
'$search': {
'text': {
'query': `${text}`,
'path': 'title'
}
}
},
{
'$match': {
[key]: `${value}`
}
},
{ $skip: page * 20 },
{ $limit: 20 }
]).toArray();
res.send(result)
} catch (error) {
console.error(error)
}
})

Related

Submitting without files resets the all images in the array when making a PATCH request

I'm trying to make a dynamic field for adding team members using Formik.
In my backend, if I do not choose any file and edit only other field such as "memberName" I'm getting message saying;
"Cast to embedded failed for value "{
_id: '63c5687832a80d5d8f717715',
memberName: 'qqaa',
memberJobTitle: 'qq',
memberDescription: 'qq',
images: [ 'undefined' ]
}" (type Object) at path "team" because of "CastError""
I want to keep the existing images if there is no changes in the input field. I'm having this issue for a week and couldn't figure it out.
This is my controller for making a PATCH request;
const updateSection = async (req, res, next) => {
const files = req.files;
const {
pageId,
welcomeTitle,
welcomeDescription,
aboutUsTitle,
aboutUsDescription,
team,
teamTitle,
} = req.body;
let updates = {};
//update other fields if they are provided in the request body
if (welcomeTitle) {
updates.welcomeTitle = welcomeTitle;
}
if (welcomeDescription) {
updates.welcomeDescription = welcomeDescription;
}
if (aboutUsTitle) {
updates.aboutUsTitle = aboutUsTitle;
}
if (aboutUsDescription) {
updates.aboutUsDescription = aboutUsDescription;
}
if (teamTitle) {
updates.teamTitle = teamTitle;
}
if (team) {
let teamPromises = []; //create an empty array to store promises for updating or creating team members
// updates.team.images = [];
team.forEach((item, i) => {
// item -> current team member being processed, i-> index in the array
let teamMember = {
_id: item._id,
memberName: item.memberName,
memberJobTitle: item.memberJobTitle,
memberDescription: item.memberDescription,
images: item.images,
};
if (files && files[i]) {
let file = files[i];
let img = fs.readFileSync(file.path);
let decode_image = img.toString("base64");
teamMember.images = [
{
filename: file.originalname,
contentType: file.mimetype,
imageBase64: decode_image,
},
];
} else {
teamMember.images = item.images;
}
teamPromises.push(
Section.updateOne(
{ pageId: pageId, "team._id": item._id },
{ $set: { "team.$": teamMember } },
{ new: false }
)
);
});
Promise.all(teamPromises)
.then((result) => {
res.status(200).json({ message: "Section updated successfully!" });
})
.catch((error) => {
res.status(500).json({ error: error.message });
});
} else {
//update other fields if no team member provided
Section.findOneAndUpdate({ pageId: pageId }, updates).then(() => {
res.status(200).json({ message: "Section updated successfully!" });
});
}
};

NodeJS Array not filling up

Im using mongodb as a database after getting the valuation data which are more then one , i loop inside them and get the offers according to the offres_id , I asure you that the database has data inside and to querys are working correctly in fact if i console log them in each iteration i get this result
{
offre: [
{
_id: new ObjectId("63320bf87123db5691c51392"),
user_id: '63304e44aa63c519d887dac1',
brand: 'HP',
model: 'AZGGH-89855A',
photo: '16642242480661659650294043-bs-5.png',
scan_method: 'manual',
__v: 0
}
],
valuation: {
_id: new ObjectId("63320d39a5677df3cebcbdae"),
user_id: '63304e44aa63c519d887dac1',
offre_id: '63320bf87123db5691c51392',
given_price: '1236',
comment: 'no comment',
__v: 0
}
}
{
offre: [
{
_id: new ObjectId("6334202a8c7e6d90b35ee999"),
user_id: '63304e44aa63c519d887dac1',
brand: 'DELL',
model: 'AZGGH-89855A',
photo: '1664360490280Capture.PNG',
scan_method: 'manual',
__v: 0
}
],
valuation: {
_id: new ObjectId("633420be8c7e6d90b35ee99e"),
user_id: '63304e44aa63c519d887dac1',
offre_id: '6334202a8c7e6d90b35ee999',
__v: 0
}
}
but when i try storing each offre & valuation at the same array cell and return it i get this as a result
[]
However this is the code
router.get('/get', async (req, res) => {
try {
Valuation.find({ user_id: req.session.userID })
.exec()
.then(valuation => {
let myData = [];
if (valuation) {
for (let i = 0; i < valuation.length; i++) {
Offre.find({_id : valuation[i].offre_id})
.exec()
.then(offre=>{
myData.push({offre : offre, valuation : valuation[i]})
})
}
res.status(200).json(myData)
} else {
res.status(404).json('no valuations found')
}
})
.catch(error => {
res.status(500).json(error.message)
})
} catch (error) {
res.status(500).json({ error: error.message })
}
})
Since you're already using async, it would be a shame to not use await to simplify your code:
router.get("/get", async (req, res) => {
try {
const valuation = await Valuation.find({ user_id: req.session.userID }).exec();
if (valuation) {
let myData = [];
for (let i = 0; i < valuation.length; i++) {
const offre = await Offre.find({ _id: valuation[i].offre_id }).exec();
myData.push({ offre: offre, valuation: valuation[i] });
}
res.status(200).json(myData);
} else {
res.status(404).json("no valuations found");
}
} catch (error) {
res.status(500).json({ error: error.message });
}
});
You can probably also speed up the Offre query by using an $in query to retrieve all offres with one query, but that's another thing.

Fetching data axios

I am fetching data from an api and its response is something like this
{
"id": "BTC",
"currency": "BTC",
"symbol": "BTC",
"name": "Bitcoin",
"1d": {
"volume": "47854345429.71",
"volume_change": "42758948175.92",
},
"7d": {
"volume": "308524476736.00",
"volume_change": "42758948175.92",
},
Here, I am using inquirer to get specific option.. and I am trying fetch information for a specific interval i.e ( 1d, 7d, 30d,..) from an API
async volumechng(cmd) {
try {
keyManager = new KeyManager();
const key = keyManager.getKey();
const api = new CryptoAPI(key)
const opts = await inquirer.prompt([
{
type: 'rawlist',
name: 'volumechng',
message: 'Volume change for interval ?',
choices: [
'1d',
'7d',
],
}
])
const volumeChange = await api.getVolumeChange(cmd,opts.volumechng)
} catch (err) {
console.log(err)
}
}
async getVolumeChange(coinOption,interval){
try{
const res = await axios.get(
`${this.baseUrl}?key=${this.apiKey}&ids=${coinOption}`
)
let output = ''
// console.log(interval) --> getting an output
res.data.forEach(coin => {
output += `
${coin.interval.volume_change}
`
})
return output
}
catch (err) {
handleAPIError(err);
}
}
I am getting the value of interval on console.log() but I am not getting the output
Let check:
console.log('Data response', res.data)
console.log("Interval: ", interval)
If you provide more output of them, I can help you more.
Please log to console too
res.data.forEach(coin => {
console.log("Coin ", coin)
output += `
${coin.interval.volume_change}
`
})
to check what your output is.

NodeJS nested promises not working properly

I got a problem with my code.It jumps to second .then section without completing first job. After that, it goes back to first promise but never executes code inside second .then
Promise.all(jobs).then((values) => {
console.log("First!")
values.forEach(function(vals) {
vals.forEach(function(doc) {
if (doc.properties.location_i.toString() == request.body.id) {
jobs_do.push(dbo.collection("zones").find({
"geometry": {
$geoIntersects: {
$geometry: {
type: "Point",
coordinates: [
docs[values.indexOf(vals)].geometry_do.coordinates[0],
docs[values.indexOf(vals)].geometry_do.coordinates[1]
]
}
}
}
}))
}
})
})
}).then(function() {
console.log("Second!")
Promise.all(jobs_do).then((values) => {
values.forEach(function(vals) {
vals.forEach(function(doc) {
console.log(doc.properties.objectid);
});
})
});
});
It fulfills jobs_do array but Promise.all(jobs_do).then((values)) executed once when jobs_do is empty.
Console log is:
First!
Second!
Although I can't exactly replicate the setup you have above, here's a simple example that should guide you how to re-write your code so that it works as it's supposed to.
const jobs = Array.from({ length: 2 })
.map((_, idx) => {
return Promise.resolve({
id: idx + 1,
title: `Job ${idx + 1}`
})
})
const jobs_do = [];
Promise.all(jobs)
.then(values => {
console.log("first!");
for (const value of values) {
if (true) {
jobs_do.push(
Promise.resolve({
...value,
description: `This is a description for job ${value.id}`
})
);
}
}
return Promise.all(jobs_do);
})
.then(results => {
console.log("second!");
results.forEach(result => {
console.log(`[${result.id}] | ${result.title} | ${result.description}`);
});
});

React-table. How to server-side filter. Query string

I am using react-table and server-side filtering.
The problem is that when filtering filters are not apply together. If I filter by name and the by category for example, the name filter resets.
the url looks like this:
http://localhost:3001/api/v1/products?pages=0&pageSize=20&filtered[]=%7B%22id%22:%22name%22,%22value%22:%22j%22%7D&filtered[]=%7B%22id%22:%22comment%22,%22value%22:%22f%22%7D".
I don't know if it is normal that filter[]= is called each time I try to filter by different column or if there is a problem in server-side.
This is the controller:
exports.index = function (req, res) {
let filtered = {}
let filters = {}
if (req.query.filtered) {
req.query.filtered.map(result => {
filtered = JSON.parse(result)
})
let id = filtered.id
let value = filtered.value
if (filtered['id'] === 'name' || filtered['id'] === 'comment') {
filters[id] = { '$regex': value, '$options': 'i' }
} else {
filters[id] = value
}
}
Product.listWithCategory(filters).then(response => {
res.json({ result: response })
})
}
React onFetchData:
onFetchData = (state, instance) => {
const jwt = getJwt()
if (!jwt) {
this.props.history.push('/login')
}
console.log(state.filtered)
let config = {
headers: { 'Authorization': `Bearer ${jwt}` },
params: {
pages: state.page,
pageSize: state.pageSize,
sorted: state.sorted,
filtered: state.filtered
}
}
this.setState({ loading: true })
axios.get('http://localhost:3001/api/v1/products', config)
.then(response => {
this.setState({
data: response.data.result,
pages: response.data.pages,
loading: false
})
})
}

Resources