I am using firebase firestore as my database and i have written firebase functions to retrieve data from the firestore database.
What i am trying to achieve is pagination and as per the docs i have implemented the code for my firebase function. Below is the code:
exports.getBillList = functions.https.onRequest((req, res) => {
let docs=[];
let limit = 15;
return cors(req, res, () => {
let lastBillInList=req.query.lastBillInList;
console.log("lastBillInList value: " + lastBillInList);
if (lastBillInList === null || lastBillInList === undefined) lastBillInList = 0;
//var lastVisible = documentSnapshots.docs[documentSnapshots.docs.length - 1];
if(lastBillInList==0){
console.log('first time call: as no lastbillseqq');
db.collection("bills").orderBy('billNo','desc').limit(limit).get().then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
docs.push(doc.data());
});
res.status(200).send(docs);
}).catch(function (error) {
console.error("Error getting list: ", error);
res.status(500).send();
});
}else{
console.log('second time call: as no lastbillseqq'+ lastBillInList);
db.collection("bills").orderBy('billNo', 'desc').startAfter(lastBillInList).limit(limit).get().then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
docs.push(doc.data());
});
res.status(200).send(docs);
}).catch(function (error) {
console.error("Error getting list: ", error);
res.status(500).send();
});
}
});
});
I have added condition to my firebase function where it checks whether a last bill number is provided.
If yes then retrieve all bill records after the last bill till the set limit or else if no last bill number is provided then consider it as first request and retrieve the initial records upto the limit
However the problem I am facing is that irrespective of code executing the else part, when the last bill number is provided , the query always return the records from the start of the result upto the limit specified. For some reason StartAfter is not working
e.g i have records with bill number form 1 to 25 , i have arranged them in descending order in above code ,so the result is from bill number 25 to 1.
When no bill number is provided i get result of bill numbers from 25 to 11.
When I provide the bill number i get result from bill numbers 25 to 11 instead of expected bill numbers from 10 to 1.
Can anyone help me out in this?
I could manage to paginate by below code. In my model object I have used the Document ID , based on which i find the documentReference -> documentSnapshot. I finally use the documentSnapshot to compare(this was my mistake as earlier i was not using documentSnapshot) to get the desired output.
Below is my code:
exports.getBillList = functions.https.onRequest((req, res) => {
console.log("----------------------------------function start");
let docs = [];
let lastBillInList=req.query.lastBillInList;
if(lastBillInList===undefined){
lastBillInList="noDocument";
}
let limit = 15;
return cors(req, res, () => {
var lastVisible = db.collection("bills").doc(lastBillInList);
lastVisible.get().then(function (doc) {
if (doc.exists) {
db.collection("bills")
.orderBy("billNo", "desc")
.startAfter(doc)
.limit(limit).get().then(function(querySnapshot){
querySnapshot.forEach(function (doc) {
//console.log(doc.data().billNo + " pushed.." + ": last bill was: " + tempBill);
docs.push(doc.data());
});
return res.status(200).send(docs);
});
} else {
db.collection("bills")
.orderBy("billNo", "desc")
.limit(limit).get().then(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
docs.push(doc.data());
});
return res.status(200).send(docs);
});
}
}).catch(function (error) {
console.log("Error getting document:", error);
return res.status(500).send();
});
console.log("----------------------------------function end");
});
});
Ok, I've found an interesting point. You have to call start(afterDocument:) before limit(to:) otherwise it won't work as expected.
So it should be something like following:
db.collection("collection")
.order(by: "createdAt", descending: true)
.start(afterDocument: snapshot) // This is where you need to put start
.limit(to: Constants.Database.Feed.limit)
.getDocuments { (snapshot, error) in
// some useful stuff
}
Related
Im having trouble incrementing a count of post "likes". The following is what I have right now:
addLike(pid, uid) {
const data = {
uid: uid,
};
this.afs.doc('posts/' + pid + '/likes/' + uid).set(data)
.then(() => console.log('post ', pid, ' liked by user ', uid));
const totalLikes = {
count : 0
};
const likeRef = this.afs.collection('posts').doc(pid);
.query.ref.transaction((count => {
if (count === null) {
return count = 1;
} else {
return count + 1;
}
}))
}
this obviously throws and error.
My goal is to "like" a post and increment a "counter" in another location. Possibly as a field of each Pid?
What am I missing here? I'm certain my path is correct..
Thanks in advance
You're to use the Firebase Realtime Database API for transactions on Cloud Firestore. While both databases are part of Firebase, they are completely different, and you cannot use the API from one on the other.
To learn more about how to run transactions on Cloud Firestore, see updating data with transactions in the documentation.
It'll look something like this:
return db.runTransaction(function(transaction) {
// This code may get re-run multiple times if there are conflicts.
return transaction.get(likeRef).then(function(likeDoc) {
if (!likeDoc.exists) {
throw "Document does not exist!";
}
var newCount = (likeDoc.data().count || 0) + 1;
transaction.update(likeDoc, { count: newCount });
});
}).then(function() {
console.log("Transaction successfully committed!");
}).catch(function(error) {
console.log("Transaction failed: ", error);
});
I am trying to update the values of the orders placed by users on the Corporate's page without a refresh. For this, I used the jQuery .on method. However, this returns the values in the array that I generated for the orders one by one rather than all at once. Is this just an issue with firebase or is it just my code.
Here is my code:
When I get the values:
firebase.database().ref('Orders/' + user_id).on('value', function(snapshot) {
// Check if the user has any pending orders
if (snapshot.val() === null) {
// No Pending Orders are Present
$('.order_content-parent').html(' <div class="order_content">Hooray! You have no pending orders!</div>');
} else {
// One or more pending orders are present
console.log(snapshot.val());
snapshot.forEach(function(child){
$('.order_content-parent').html(' <div class="order_content"></div>');
var order = child.val().Order;
var key = child.key;
console.log('Key is : '+key);
getOrders(key);
});
When I insert the values into the database:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
var myId = user.uid;
const orders = ['Orders: '];
$('.postData').each(function() {
var data = $(this).html();
orders.push(data);
var database = firebase.database();
database.ref('Orders/' + user_id + '/' + myId).set({
Order: orders
}, function(error) {
if (error) {
// The write failed...
alert(error);
} else {
$('.postData').html('Connecting...');
}
});
database.ref('Orders/' + myId).set({
Order: orders,
For: user_id
}, function(error) {
if (error) {
// The write failed...
alert(error);
} else {
$('.postData').html('Order Successfully Placed!');
}
});
});
} else {
// No user is signed in.
}
});
Here is my console when I print the values from the database:
Here is my database structure:
Can anyone help
Thanks in advance,
Tom
I think this is expected behaviour, as the documentation states:
The value event is called every time data is changed at the specified database reference, including changes to children.
Since your inserts are on a each loop, they get inserted one by one, triggering the .on() listener multiple times.
You could try inserting all the orders at once. Please try this approach and let me know if it works:
firebase.auth().onAuthStateChanged(function (user) {
if (!user) {
console.log("No user is signed in");
return;
}
var myId = user.uid;
var orders = [];
// Get the orders to insert first
$('.postData').each(function () {
var data = $(this).html();
orders.push(data);
});
// Then, insert them all at once
var database = firebase.database();
database.ref('Orders/' + user_id + '/' + myId).set({
Order: orders
}, function (error) {
if (error) {
// The write failed...
alert(error);
return;
}
$('.postData').html('Connecting...');
database.ref('Orders/' + myId).set({
Order: orders,
For: user_id
}, function (error) {
if (error) {
// The write failed...
alert(error);
return;
}
$('.postData').html('Order Successfully Placed!');
});
});
});
I am using Web3 to get a list of smart contract and then iterate (loop) through each of them to get multiple variables of the smart contracts. Unfortunately, I am not able to execute a function once all the async calls within my loop are done.
Logic:
Get the number of Games
For i = 0 until i < Games
Get the smart contract address (from Smart Contract)
Get the Start Time value (from Smart Contract)
Get the End Time value (from Smart Contract)
(Once all calls of the loop are done)
Order the Games by Start Time
Display the Games
When I do console.log(contractInstanceGame) (step 3) after my loop, the array is empty as the previous calls are not completed.
Code:
var contractInstanceGame = [];
contractAddressRegistry = '0xc0b55bff524b953a5248ccb5a60b00647052ae8b';
// Fetch all the contract addresses
let contractRegistry = web3.eth.contract(contractAbiRegistry);
let contractInstanceRegistry = contractRegistry.at(contractAddressRegistry);
contractInstanceRegistry.numberOfGames(function(err, res) {
if (!err) {
let numberOfGames = res.toNumber();
for (i = 0; i < numberOfGames; i++) {
let contractGame = web3.eth.contract(contractAbiGame);
contractInstanceRegistry.games(i, function(err, res) {
if (!err) {
// Create the object
contractInstanceGame[i] = [];
contractInstanceGame[i]['Caller'] = contractGame.at(res);
contractInstanceGame[i]['Address'] = res;
// Get the Start Time
contractInstanceGame[i]['Caller'].startTime(function(err, res) {
if (!err) {
contractInstanceGame[i]['StartTime'] = res.toNumber();
} else {
console.error("Could not get the Game start time: " + err);
}
});
// Get the End Time
contractInstanceGame[i]['Caller'].endTime(function(err, res) {
if (!err) {
contractInstanceGame[i]['EndTime'] = res.toNumber();
} else {
console.error("Could not get the Game end time: " + err);
}
});
} else {
console.error("Could not get the Game contract address: " + err);
}
});
}
console.log(contractInstanceGame);
// Perform the Order of contractInstanceGame by Start Time`
// Display contractInstanceGame
} else {
console.error("Could not get the number of Games: " + err);
}
EDIT:
Examples of the solutions I tried:
Using then() on the call itself does not work as I am facing the following error:
inpage.js:14 Uncaught Error: The MetaMask Web3 object does not support synchronous methods like eth_call without a callback parameter. See https://github.com/MetaMask/faq/blob/master/DEVELOPERS.md#dizzy-all-async---think-of-metamask-as-a-light-client for details.
contractInstanceRegistry.numberOfGames()
.then(function(x){
console.log(x);
});
I also tried to Promisifed and use await, but I am facing the error: Uncaught SyntaxError: await is only valid in async function
let numberOfGames = promisify(cb => contractInstanceRegistry.numberOfGames(cb));
let numberOfGamesX = await numberOfGames;
I have a database class with some methods to easily do some queries without formatting the values into a sql query string. So here is my delete method where the script is stopping.
// Other Module
let filter = {
url: 'http://www.example.com/some/route',
},
DB = new MySQL();
DB.delete(filter, 'found_urls').then(result => {
console.log('The row/s are deleted.');
}).catch(error => {
console.log('An error occured.');
console.log(error);
});
// MySQL.js
class MySQL {
// some methods....
delete(filter, table, operator = '=') {
// Parse where clauses and create query string.
let whereCondition = MySQL.parseColumnValue(filter, operator),
sql = `DELETE FROM ${table}`;
// whereCondition is an array: ["url = 'http://www.example.com/some/route'"]
if (whereCondition.length > 1) {
sql += ` WHERE ${whereCondition.join(' AND ')}`;
}
console.log(sql);
// I get "DELETE FROM found_urls WHERE url = 'http://www.example.com/some/route'"
return new Promise((resolve, reject) => {
console.log('You can see me.');
this.database.query(sql, (error, result) => {
console.log('You never see me because the script stopped.');
if (error) {
reject(error);
return;
}
resolve(result);
});
});
}
}
Some days ago it worked perfect but now it stops. I don't know what's wrong with this code.
UPDATE
This class would be used in a webcrawler so I think it's not necessary to protect it for SQL injection.
The table found_urls has about 200 rows, nothing is locked.
Bug I found my problem: A failure in another module. Without this module the crawler is running (with the delete operations).
I am trying to check to see if a document is already in the database before posting. I am posting via a jQuery.post() method to my endpoint which is api/songs.
What I want to check is to see if the song is already in the database. I can check that by querying the songId parameter that I am pushing to the database. If the song is already in the database I want to increment a number by 1.
What I have been trying to do is use mongo's findOne(), but I can't get it to work in the POST. My post route looks like this:
router.post('/api/songs', function(req, res){
if(Song.findOne({songId: req.body.songId}).length > -1){
console.log('already in there fam')
} else{
var song = new Song();
song.artistName = req.body.artistName;
song.titleName = req.body.titleName;
song.songId = req.body.songId;
song.songImg = req.body.songImg;
song.save(function(err) {
if (err) {
console.log("Error: " + err)
} else {
console.log("created fam")
}
})
};
console.log(song);
return res.json({message: "SongCreated"})
})
My problem is that I can't figure out what the findOne is returning. I want it to return a boolean so i've tried count() and length() but can't get it to return true. The req posts to the DB no matter what.
Any ideas?
All i/o operations in node.js are asynchronous. Your Song.findOne operation is asynchronous as well. You should wait for it to complete via callback functionality and then check the result of it.
Mongoose findOne method returns a promise. You can read more info about it here.
Example of promise execution:
var query = Song.findOne({songId: req.body.songId})
query.exec(function(err, song) {
})
Try the following code:
router.post('/api/songs', function(req, res){
Song.findOne({songId: req.body.songId}, function(err, song){
if (err) {
return console.log("Error: " + err)
}
if(song){
return console.log('already in there fam')
}
var song = new Song();
song.artistName = req.body.artistName;
song.titleName = req.body.titleName;
song.songId = req.body.songId;
song.songImg = req.body.songImg;
song.save(function(err) {
if (err) {
console.log("Error: " + err)
} else {
console.log("created fam")
}
})
console.log(song);
return res.json({message: "SongCreated"})
})
})