dexiejs query get slower overtime - progressive-web-apps

So I am using Svelte+Vite with Dexiejs as my offline Db and Routify for the routes, when I go to a page that is making query to Dexiejs the response is pretty quick on the first request, but when I go to another page and go back to to the same page the response time gets slower each query.
I've used the index like this answer suggest. but it still happens. what did i miss ? can anyone recommend any alternatives that have better performance than dexiejs or pouchDB for offline db ? I am currently trying pouchDB as alternative
here is my code
let taskDone;
let taskOngoing;
let clustercount;
let tasks = [];
onMount(async function () {
// #ts-ignore
let clusterQuery = await db.cluster.reverse().sortBy("id");
clustercount = clusterQuery.length;
for (const clusters of clusterQuery) {
// #ts-ignore
taskDone = await db.task
.where("[cluster_id+status]")
.anyOf([clusters.id, 1], [clusters.id, 2])
.toArray();
// #ts-ignore
taskOngoing = await db.task
.where({ cluster_id: clusters.id })
.toArray();
tasks = [
{
cluster_id: clusters.id,
count_done: taskDone.length,
count_ongoing: taskOngoing.length,
cluster_name: clusters.name,
},
...tasks,
];
count++;
}
dispatch("showList", tasks);
});

It looks like the tasks array is being filled more and more for every time onMount is called. Is it a globally declared variable? Was it meant to be local?

Related

Firebase Functions logs working, but Flutter returns null

I'm trying to display Firestore data in a Flutter app. To do that, I created a Firebase function like the following:
exports.getHouses = functions.https.onCall((data, context) => {
let pathBeginning = "users/";
console.log(data.userID);
let path = pathBeginning.concat(data.userID, "/houses");
let houses = [];
admin.firestore().collection(path).get().then(snapshot => {
console.log("COLECTION WHERE DOCUMENTS ARE RETREIVED:");
console.log(path);
snapshot.forEach(doc => {
let newHouse = {
"id": doc.id,
"address": doc.data().address
}
houses = houses.concat(newHouse);
});
console.log("THE FOLLOWING LOG SHOULD RETURN THE FULL LIST OF HOUSES:")
console.log(houses);
return houses;
}).catch(reason => {
response.send(reason);
});
});
I think this part works properly, because logs in Firebase look exactly as expected:
Firebase logs
The problem happens when I try to print this data in my Flutter app. To do so, I use the following code:
Future<void> listHouses() async {
var parameters = {
"userID": "1"
};
HttpsCallable getHouses = FirebaseFunctions.instance.httpsCallable("getHouses");
await getHouses.call(parameters).then((HttpsCallableResult response) {
print(response.data);
});
}
When called the funtion, it always returns null, even when Firebase log displays everything properly.
I would appreciate if anyone can help me solving this issue. Thank you in advance.
You're missing a return in front of your get() call. Without that, the Cloud Functions container doesn't know about the asynchronous get() operation and thus will/may terminate the function before loading the data completes.
To fix this, add return:
return admin.firestore().collection(path).get().then(snapshot => {
...
This is an extremely common problem within Cloud Functions and when dealing with asynchronous APIs in general, so I recommend taking a moment to read the Firebase documentation on terminating functions and watching the video series linked in there.

What is the proper way to run fetch calls which use reactive components from a store?

I am getting two reactive variables I need from a store to use for my fetch calls. I need these fetch calls to rerun when the data in these store values change. I am able to make this work however when I reload the page it causes my app to crash because there are no values that are getting from the store. I am able to make it work if I disable ssr on the +page.js file.
I also believe it is relevant to mention that I am using a relative URL (/api) to make the fetch call because I have a proxy server to bypass CORS
What is the proper way to get this data by rerunning the fetch calls using a reactive component from a store without disabling ssr? Or is this the best/only solution?
+page.svelte
<script>
import { dateStore, shiftStore } from '../../../lib/store';
$: shift = $shiftStore
$: date = $dateStore
/**
* #type {any[]}
*/
export let comments = []
/**
* #type {any[]}
*/
let areas = []
//console.log(date)
async function getComments() {
const response = await fetch(`/api/${date.toISOString().split('T')[0]}/${shift}/1`)
comments = await response.json()
console.log(comments)
}
async function getAreas() {
const response = await fetch(`/api/api/TurnReportArea/1/${date.toISOString().split('T')[0]}/${shift}`)
areas = await response.json()
console.log(areas)
}
// both of these call function if date or shift value changes
$: date && shift && getAreas()
$: date , shift , getComments()
</script>
I tried to use the +page.js file for my fetch calls, however I cannot use the reactive values in the store in the +page.js file. Below the date variable is set as a 'Writble(Date)' When I try to add the $ in front of the value let dare = $dateStore, I get the error 'Cannot find name '$dateSrote'' If i put the $ in the fetch call I get the error 'Cannot find $date'. Even if I were able to make this work, I do not understand how my page would know to rerender if these fetch calls were ran so I do not think this is the solution. As I mentioned, the only solution I have found is to disable ssr on the +page.js, which I do not think is the best way to fix this issue.
import { dateStore, shiftStore } from "../../../lib/store"
export const load = async ({ }) => {
let shift = shiftStore
let date = dateStore
const getComments = async() => {
const commentRes = await fetch(`/api/${date.toISOString().split('T')[0]}/${shift}/1`)
const comments = await commentRes.json()
console.log(comments)
}
const getAreas = async () => {
const areasRes = await fetch(`/api/api/TurnReportArea/1/${date.toISOString().split('T')[0]}/${shift}`)
const areas = await areasRes.json()
console.log(areas)
}
return {
comments: getComments(),
areas: getAreas()
}
}

Can't update Mongo Collection in Meteor from within a function

I have a webhook set up that lives in imports/api in my application. This is a Stripe webhook and I’m trying update Campaign collection. I’m using a Meteor method to make this call however nothing happens. If I add the call back to it, I receive no error or results. However if I take this same Meteor Method an include it in main.js on the server in a Meteor startup function it runs fine. I’ve also tried just using the update function for a collection and it won’t work either. Main.js includes this file and I cannot figure out why this doesn’t work nor will push an error. The most important part of this line is the bottom where Paid == true, this is where the logic is. The console will spit out the information but the method won’t run. What could be the issue?
import bodyParser from 'body-parser';
import { Picker } from 'meteor/meteorhacks:picker';
import {Campaign} from '../api/campaigns';
let campaignID;
let chargeAmount;
let chargeID;
let hasPaid = false;
//This will run outside the updateCampaign function fine and works as expected
Campaign.update({_id: "BAxBhk4ae3AdHxxEQ"}, {$set: {chargeTrx: "chargeID", amount: "555"}});
function updateCampaign(){
//these consoles will print and prove the function runs
console.log('campaignID type:' + campaignID)
console.log('chargeIDtype:' + chargeID)
console.log('amount type:' + chargeAmount)
//doesn't work even with strings prefilled
Campaign.update({_id: "BAxBhk4ae3AdHxxEQ"}, {$set: {chargeTrx: "chargeID", amount: "333"}});
//This didn't run either but would run outside of this function
Meteor.call("updateCampaignForPayment", campaignID, chargeAmount, chargeID, (err, result) => {
if (err){
console.log('error')
}
});
}
// Middleware declaration
Picker.middleware(bodyParser.json({
limit: '10MB',
verify: function(req,res,buf) {
var url = req.originalUrl;
if (url.startsWith('/webhook')) {
req.rawBody = buf.toString();
let newResponse = req.rawBody;
//stripe returns to 2 objects, this makes it two arrays to parse it
let parsedResponse = JSON.parse('[' + newResponse.replace(/}{/g, '},{') + ']');
parsedResponse = parsedResponse[0].data.object;
//break down two further objects
if (parsedResponse.object == "charge"){
chargeID = parsedResponse.id;
hasPaid = parsedResponse.paid;
chargeAmount = parsedResponse.amount / 100;
}
else if (parsedResponse.object == "checkout.session"){
let campaignIDArray = parsedResponse.success_url.split('/');
campaignID = campaignIDArray[5];
}
// If user has paid, update campaign
if (hasPaid == true && chargeID && campaignID && chargeAmount){
console.log(hasPaid, chargeID, campaignID, chargeAmount)
//
updateCampaign();
}
}
}
}));
Any calls outside of the current Meteor environment (such as in callback functions or middleware handlers) to a construct, which requires Meteor environment, are to be bound using the Meteor environment.
In your case this seems to be updateCampaign, because it makes calls to the Meteor-Mongo Collection:
const updateCampaign = Meteor.bindEnvironment(function () {
const updated = Campaign.update({_id: "BAxBhk4ae3AdHxxEQ"}, {$set: {chargeTrx: "chargeID", amount: "333"}});
const callResult = Meteor.call("updateCampaignForPayment", campaignID, chargeAmount, chargeID);
})
Please also note, that the collection inside this function is running in async mode and only within Meteor environment code you can write code in a sync way and let it handle the async. You can always "wait" for the result there using async/await:
function async updateCampaign(){
const updated = await Campaign.update({_id: "BAxBhk4ae3AdHxxEQ"}, {$set: {chargeTrx: "chargeID", amount: "333"}});
//This didn't run either but would run outside of this function
const callResult = await Meteor.call("updateCampaignForPayment", campaignID, chargeAmount, chargeID);
}
or you use Promise.await, if you want to avoid async/await:
function updateCampaign(){
const updated = Promise.await(Campaign.update({_id: "BAxBhk4ae3AdHxxEQ"}, {$set: {chargeTrx: "chargeID", amount: "333"}}))
//This didn't run either but would run outside of this function
const callResult = Promise.await(Meteor.call("updateCampaignForPayment", campaignID, chargeAmount, chargeID))
}
Readings:
https://guide.meteor.com/using-npm-packages.html#bind-environment
https://docs.meteor.com/api/methods.html#Meteor-call
https://guide.meteor.com/using-npm-packages.html#promises
https://github.com/meteor/meteor/tree/devel/packages/promise

Callbacks with node.js/MongoDB/Mongoose

I'm trying to learn the Mongoose ORM for MongoDB and node.js. I want to query documents in my database, perform an action on each one, and then perform another action after all of the previous actions are complete. In other words, I want to do something like this, assuming I have a model called 'User:'
var userArray = [];
User.find({})
.each(function(user) {
user.age += 1;
userArray.push(user);
})
.then(function() {
//do something with userArray
//happens after all the users' ages are modified
});
What is the correct syntax for doing something like this in Mongoose?
If you only need to perform synchronous actions for each document, the solution is fairly simple. (console.warn() is synchronous, so you can use it to verify that all user objects are processed first.)
User.find({}).execFind(function(err, users) {
users.forEach(function(user) {
user.age += 1;
userArray.push(user);
console.warn('x');
});
// then...
console.warn('y');
});
If you need to perform some sort of asynchronous action (perhaps another db query), the solution becomes more complex. I encountered this issue recently and briefly debated using a module such as Step or rolling my own barebones solution. Step provided more functionality than I needed, so I decided that the added overhead was not worth it. Here is my solution:
var count = 0;
A.find({ b : c }, ['d', 'e', 'f']).sort(['d'], -1).execFind(function(err, g) {
g.forEach(function(h) {
h.a = [];
B.find({ b : c }, ['d', 'e', 'f', 'g']).execFind(function(err, z) {
g.v = z;
if (++count == g.length)
res.render('z.jade', { locals : { title : 'Q', j : g } });
});
});
});
Please pardon the obfuscation (I yanked that right from the source of a project that is under-wraps). Essentially, you wait until the final async operation to complete.
There are modules for this. Try using async (https://github.com/caolan/async). It allows for running commands and then executing a callback once all are done.
async.parallel([
function(){ ... },
function(){ ... }
], callback);

How to return Mongoose results from the find method?

Everything I can find for rending a page with mongoose results says to do it like this:
users.find({}, function(err, docs){
res.render('profile/profile', {
users: docs
});
});
How could I return the results from the query, more like this?
var a_users = users.find({}); //non-working example
So that I could get multiple results to publish on the page?
like:
/* non working example */
var a_users = users.find({});
var a_articles = articles.find({});
res.render('profile/profile', {
users: a_users
, articles: a_articles
});
Can this be done?
You're trying to force a synchronous paradigm. Just does't work. node.js is single threaded, for the most part -- when io is done, the execution context is yielded. Signaling is managed with a callback. What this means is that you either have nested callbacks, named functions, or a flow control library to make things nicer looking.
https://github.com/caolan/async#parallel
async.parallel([
function(cb){
users.find({}, cb);
},
function(cb){
articles.find({}, cb);
}
], function(results){
// results contains both users and articles
});
I'll play the necromancer here, as I still see another, better way to do it.
Using wonderful promise library Bluebird and its promisifyAll() method:
var Promise = require('bluebird');
var mongoose = require('mongoose');
Promise.promisifyAll(mongoose); // key part - promisification
var users, articles; // load mongoose models "users" and "articles" here
Promise.props({
users: users.find().execAsync(),
articles: articles.find().execAsync()
})
.then(function(results) {
res.render('profile/profile', results);
})
.catch(function(err) {
res.send(500); // oops - we're even handling errors!
});
Key parts are as follows:
Promise.promisifyAll(mongoose);
Makes all mongoose (and its models) methods available as functions returning promises, with Async suffix (.exec() becomes .execAsync(), and so on). .promisifyAll() method is nearly-universal in Node.JS world - you can use it on anything providing asynchronous functions taking in callback as their last argument.
Promise.props({
users: users.find().execAsync(),
articles: articles.find().execAsync()
})
.props() bluebird method takes in object with promises as its properties, and returns collective promise that gets resolved when both database queries (here - promises) return their results. Resolved value is our results object in the final function:
results.users - users found in the database by mongoose
results.articles - articles found in the database by mongoose (d'uh)
As you can see, we are not even getting near to the indentation callback hell. Both database queries are executed in parallel - no need for one of them to wait for the other. Code is short and readable - practically corresponding in length and complexity (or rather lack of it) to wishful "non-working example" posted in the question itself.
Promises are cool. Use them.
The easy way:
var userModel = mongoose.model('users');
var articleModel = mongoose.model('articles');
userModel.find({}, function (err, db_users) {
if(err) {/*error!!!*/}
articleModel.find({}, function (err, db_articles) {
if(err) {/*error!!!*/}
res.render('profile/profile', {
users: db_users,
articles: db_articles
});
});
});
Practically every function is asynchronous in Node.js. So is Mongoose's find. And if you want to call it serially you should use something like Slide library.
But in your case I think the easiest way is to nest callbacks (this allows f.e. quering articles for selected previously users) or do it completly parallel with help of async libraries (see Flow control / Async goodies).
I have a function that I use quite a bit as a return to Node functions.
function freturn (value, callback){
if(callback){
return callback(value);
}
return value;
};
Then I have an optional callback parameter in all of the signatures.
I was dealing with a very similar thing but using socket.io and DB access from a client. My find was throwing the contents of my DB back to the client before the database had a chance to get the data... So for what it's worth I will share my findings here:
My function for retrieving the DB:
//Read Boards - complete DB
var readBoards = function() {
var callback = function() {
return function(error, data) {
if(error) {
console.log("Error: " + error);
}
console.log("Boards from Server (fct): " + data);
}
};
return boards.find({}, callback());
};
My socket event listener:
socket.on('getBoards', function() {
var query = dbConnection.readBoards();
var promise = query.exec();
promise.addBack(function (err, boards) {
if(err)
console.log("Error: " + err);
socket.emit('onGetBoards', boards);
});
});
So to solve the problem we use the promise that mongoose gives us and then once we have received the data from the DB my socket emits it back to the client...
For what its worth...
You achieve the desired result by the following code. Hope this will help you.
var async = require('async');
// custom imports
var User = require('../models/user');
var Article = require('../models/article');
var List1Objects = User.find({});
var List2Objects = Article.find({});
var resourcesStack = {
usersList: List1Objects.exec.bind(List1Objects),
articlesList: List2Objects.exec.bind(List2Objects),
};
async.parallel(resourcesStack, function (error, resultSet){
if (error) {
res.status(500).send(error);
return;
}
res.render('home', resultSet);
});