Mongoose/Mongo Pagination - javascript

Using Express Node.JS along with Mongoose, and I'm trying to add pagination to my get request, however the order in which I'm getting messages is invalid. I get the correct messages with createdAt: -1/createdAt: 'desc', -createdAt, but in reverse order. (Changing to : 1 or 'asc' gives me the oldest messages, and thats not what I need)
const fetchMessages = async (req, res, chatId) => {
try {
const page = req.query.page || 0;
const limit = 25;
const take = req.query.take || limit;
const filter = { chatId: { $eq: chatId } };
let query = await Message.find(filter)
.skip(take * page)
.limit(200)
.sort('-createdAt');
// query = query.sort({ createdAt: 1 });
return res.status(200).json({ data: query });
} catch (e) {
console.log(e);
return res.status(500).json({ message: e });
}
};
Solved it..
const fetchMessages = async (req, res, chatId) => {
try {
// Required pagination param
const page = req.query.page || 0;
// Optional pagination params
const limit = req.query.limit || 25;
const take = req.query.take || limit;
const filter = { chatId: { $eq: chatId } };
let query = await Message.find(filter)
.sort('-createdAt')
.skip(take * page)
.limit(limit);
// Making newest messages come first
query = query.reverse();
return res.status(200).json({ data: query });
} catch (e) {
console.log(e);
return res.status(500).json({ message: e })

Related

isAxiosError.js:12 POST http://localhost:3000/cartdata 500 (Internal Server Error)

Error Image
I am trying to create a shopping cart and once I get the data from the client side to the server side after each update, I am getting an internal server error even though everything is working fine and the cart data is being updated in the session.
here is the code from the client side:
const fetchData = async () => {
const res = await axios.get(`http://localhost:3000/cartdata`);
const data = res.data;
console.log(data);
calculateTotals(data);
remove(data);
// postData(data);
}
fetchData();
const postData = async (data) => {
// checkSlice(data);
axios.post('http://localhost:3000/cartdata',{
data: data
}).then((data) => {
console.log('posted', data)
}).catch((err) => {
console.log('error');
});
}
here is the code from the backend:
router.get('/cart', (req, res) => {
let sess = req.session;
let cart = (typeof sess.cart !== 'undefined') ? sess.cart : false;
console.log(cart)
res.render('tarpit/cart', {
pageTitle: 'Cart',
cart: cart,
nonce: Security.md5(req.sessionID + req.headers['user-agent'])
});
});
router.get('/cartdata', (req, res)=>{
let sess = req.session;
let cart = (typeof sess.cart !== 'undefined') ? sess.cart : false;
res.json(cart);
})
router.post('/cartdata',asyncError(async (req, res) =>{
req.session.cart = req.body.data
console.log(req.session.cart);
await req.session.cart.save();
}))
router.post('/cart',asyncError(async(req, res) => {
let qty = parseInt(req.body.qty, 10);
// console.log(qty);
let product = req.body.product_id;
// let format = new Intl.NumberFormat('en-US', {style: 'currency', currency: 'USD'});
if(qty > 0 && Security.isValidNonce(req.body.nonce, req)) {
const mypro = await Products.findOne({_id: product})
let cart = (req.session.cart) ? req.session.cart : null;
const prod = {
id: mypro._id,
title: mypro.title,
price: mypro.price,
qty: qty,
image: mypro.image[0].url,
}
// res.send(prod)
Cart.addToCart(prod, qty, cart);
res.redirect('/cart');
}
else {
res.redirect('/');
}
}));
thanks
Assuming you are using express-session for session please remove await req.session.cart.save(); from your /cardata post method and use following
await req.session.save()

Handling complex query parameters Express.Js

I'm making REST APIS with Express.js
I have the following express route:
/api/customer
I added multiple query params to the route like this:
/api/customer?name=jake
/api/customer?country=america
/api/customer?name=jake&country=america
/api/customer?name=jake&limit=10
In my controllers I handle all of these with If and there are so many cases I feel that this method would not scale, is there a better way of handling this ?
This is the code for my controller, I'm using Sequelize to query the database:
async function getAllCustomer(queryLimit, page) {
const customers = await Customer.findAll({
limit: queryLimit ? parseInt(queryLimit) : null,
offset: page ? parseInt(queryLimit) * parseInt(page) : null
});
return customers;
}
async function getCustomerByFirstName(name, queryLimit, page) {
return await Customer.findAll({
where: {
firstName: name,
}
})
}
async function getCustomerByAddress(address) {
return await Customer.findAll({
where: {
customerAddress: address
}
})
}
async function getCustomerByNameAddress(name, address) {
return await Customer.findAll({
where: {
[Op.and]: [
{firstName: name},
{customerAddress: address}
]
}
})
}
async function getCustomer(req, res) {
const page = req.query.page;
const queryLimit = req.query.limit;
const name = req.query.name;
const address = req.query.address;
let customers;
/* User want to find first names */
if (name && !address) {
const names = name.split(",")
customers = await getCustomerByFirstName(names, queryLimit, page)
res.status(200).send(customers)
return;
}
/* User want to find addresses */
if (!name && address) {
const addresses = address.split(",")
customers = await getCustomerByAddress(addresses, queryLimit, page)
res.status(200).send(customers)
return;
}
/* User want to mix both */
if (name && address) {
const names = name.split(",")
const addresses = address.split(",")
customers = await getCustomerByNameAddress(names, addresses, queryLimit, page)
res.status(200).send(customers)
return;
}
if (!name && !address) {
customers = await getAllCustomer(queryLimit, page)
res.status(200).send(customers)
return;
}
}
You could do something like this:
async function getCustomer(req, res) {
const page = req.query.page;
const queryLimit = req.query.limit;
const name = req.query.name;
const address = req.query.address;
let query = { };
if(name) {
query.firstName = name;
}
if(address) {
query.address = address;
}
let customers = await getCustomers(query, queryLimit, page);
res.status(200).send(customers)
return;
}
async function getCustomers(query, queryLimit, page) {
const customers = await Customer.findAll({
where: query,
limit: queryLimit ? parseInt(queryLimit) : null,
offset: page ? parseInt(queryLimit) * parseInt(page) : null
});
return customers;
}
BTW, in your code, the functions getCustomerByFirstName, getCustomerByAddress and getCustomerByNameAddress are expecting to receive name and address as string parameter, but you are passing names and addresses array. This might lead to errors...

Algolia - get mass records & delete with filter

I am using Algolia for search purposes and we got a huge pile of records. We want to delete some records and have decided to delete records that are older than X date.
First I was using this
const records = [];
const deleteRecordsBeforeDateAlgolia = (date) => {
let client;
*****
const index = client.initIndex('function_message');
//get the records before the given date
try {
index.search('',{
filters: `time_stamp < ${date}`
}).then(({hits}) => {
if(hits.length > 0) {
for (const hit of hits) {
index.deleteObject(hit.objectID);
records.push(hit.objectID);
}
}
if(hits.length === 0) {
console.log(`Deleted ${records.length} records`);
} else {
deleteRecordsBeforeDateAlgolia(date);
}
});
} catch (err) {
console.error(err);
}
};
but I realized this isnt that optimized + will be very slow when deleting on prod. Can you tell me how I can get huge amounts of data with a filter (timestamp in this case) and then delete all of them?
EDIT
const records = [];
const deleteRecordsBeforeDateAlgolia = (date) => {
let client;
//creds stuff
const index = client.initIndex('function_message');
//get the records before the given date
try {
const search = index.browseObjects({
filters: `time_stamp < ${date}`
}).then(res => {
//IT SHOWS RESPONSE IS UNDEFINED
res.forEach(record => {
records.push(record);
});
console.log(`found ${records.length} records`);
});
} catch (err) {
console.error(err);
}
};
browseObjects takes a batch callback function that's called on every batch of hits where you can specify what to do with the batch. The optional parameter list can be found here
Something like this should work
const records = [];
const deleteFromIndex = (idArray,index) => {
index.deleteObjects(idArray).then(({ objectIDs }) => {
console.log(objectIDs);
});
}
const deleteRecordsBeforeDateAlgolia = (date) => {
let client;
client = algoliasearch('algoliaApp', 'algoliaKey');
const index = client.initIndex('function_message');
try {
index.browseObjects({
filters: `time_stamp<${date}`,
query: '',
batch: (batch) => {
records.push(...batch); //push each batch into records array
}
})
.then(() => {
const idArray = records.map(({objectID}) => objectID) //get an array of objectIDs
deleteFromIndex(idArray, index)
});
} catch (err) {
console.error(err);
}
};
deleteRecordsBeforeDateAlgolia('some date')

Filtering by find() - Javascript

I am working with my first NodeJS server and I am struggling. Until now I have a GET working perfectly as it is
WORKING
exports.getPosts = (req, res, next) => {
const pageSize = +req.query.pagesize;
const currentPage = req.query.page;
const postMode = req.query.mode;
postQuery = POST.find();
let fetchedposts;
if (pageSize && currentPage) {
postQuery.skip(pageSize * (currentPage - 1))
.limit(pageSize);
}
postQuery.find()
.then(documents => {
fetchedposts = documents;
return post.count();
}).then(count => {
res.status(200).json({
message: 'posts fetched successfully',
posts: fetchedposts,
maxposts: count
});
})
.catch(error => {
res.status(500).json({
message: "Fetching posts failed"
});
});
};
but now I am trying to add a new parameter to the query, mode. What I am trying to achieve is to filter the list by adding extra attributes to the method find(). I have tried to add them like the following code:
NOT WORKING
exports.getposts = (req, res, next) => {
const pageSize = +req.query.pagesize;
const currentPage = req.query.page;
const postMode = req.query.mode;
postQuery = POST.find();
let fetchedposts;
if (pageSize && currentPage) {
postQuery.skip(pageSize * (currentPage - 1))
.limit(pageSize);
}
postQuery.find(**(post) => post.private === true**)
.then(documents => {
fetchedposts = documents;
return post.count();
}).then(count => {
res.status(200).json({
message: 'posts fetched successfully',
posts: fetchedposts,
maxposts: count
});
})
.catch(error => {
res.status(500).json({
message: "Fetching posts failed"
});
});
};
but my server crashed.
ERROR
postQuery.find((post) => post.private === true) TypeError: Cannot read property 'private' of null
How should I do it?
That error means that post is null at some point. You could probably fix it by checking if it is null before checking the private value:
(post) => post && post.private === true

Can't figure out why my app.get is being run twice?

I have a app.get which inside of it is quite a bit of logic. Which everything works great aside from some of the logic being called twice for some reason. I have noticed when I was saving something to by db that it would save two rows.
So I put a console.log in that area and sure enough it was logging it twice.
Any reason why this is happening?
app.get('/shopify/callback', (req, res) => {
const { shop, hmac, code, state } = req.query;
const stateCookie = cookie.parse(req.headers.cookie).state;
if (state !== stateCookie) {
return res.status(403).send('Request origin cannot be verified');
}
if (shop && hmac && code) {
// DONE: Validate request is from Shopify
const map = Object.assign({}, req.query);
delete map['signature'];
delete map['hmac'];
const message = querystring.stringify(map);
const providedHmac = Buffer.from(hmac, 'utf-8');
const generatedHash = Buffer.from(
crypto
.createHmac('sha256', config.oauth.client_secret)
.update(message)
.digest('hex'),
'utf-8'
);
let hashEquals = false;
try {
hashEquals = crypto.timingSafeEqual(generatedHash, providedHmac)
} catch (e) {
hashEquals = false;
};
if (!hashEquals) {
return res.status(400).send('HMAC validation failed');
}
// DONE: Exchange temporary code for a permanent access token
const accessTokenRequestUrl = 'https://' + shop + '/admin/oauth/access_token';
const accessTokenPayload = {
client_id: config.oauth.api_key,
client_secret: config.oauth.client_secret,
code,
};
request.post(accessTokenRequestUrl, { json: accessTokenPayload })
.then((accessTokenResponse) => {
const accessToken = accessTokenResponse.access_token;
// DONE: Use access token to make API call to 'shop' endpoint
const shopRequestUrl = 'https://' + shop + '/admin/shop.json';
const shopRequestHeaders = {
'X-Shopify-Access-Token': accessToken,
}
request.get(shopRequestUrl, { headers: shopRequestHeaders })
.then((shopResponse) => {
const response = JSON.parse(shopResponse);
const shopData = response.shop;
console.log('BEING CALLED TWICE...')
res.render('pages/brand_signup',{
shop: shopData.name
})
})
.catch((error) => {
res.status(error.statusCode).send(error.error.error_description);
});
})
.catch((error) => {
res.status(error.statusCode).send(error.error.error_description);
});
} else {
res.status(400).send('Required parameters missing');
}
});

Categories