I needed in my app firebase (server) time millisecond. because my local time will be change by mobile date time setting,
i will be see this question by not so helpful,
Flutter Firestore Server side Timestamp
var timestamp=FieldValue.serverTimestamp();
print("timestamp"+timestamp.toString());
but they print below value
====>Instance of 'FieldValue'
you should use
timestamp() {
Firestore.instance.collection('timestamp').document('time').setData({
"timestamp": FieldValue.serverTimestamp(),
});
}
Before reading the timestamp you have to set it to the server since you are generating it from there. According to this
serverTimestamp() Returns a sentinel for use with set() or update() to include a server-generated timestamp in the written data.
so first set it
Firestore.instance.collection('timestamp').document('docId').setData({'time' : FieldValue.serverTimestamp()});
then get it using basic firestore query. Finally, check out this answer on the details of the why.
For more general way of getting timestamp
getTimestamp() async{
await Firestore.instance.collection('timestamp').document('docId').setData({'time' : FieldValue.serverTimestamp()});
DocumentSnapshot doc = await Firestore.instance.collection('timestamp').document('docId').get();
Timestamp timestamp = doc['time'];
var timestampInMilliSeconds = timestamp.millisecondsSinceEpoch;
return timestampInMilliSeconds;
/* here you can call setState to your own variable before returning and keep in mind this method is async so it will return a Future. */
}
Related
I have a flutter app where I use a StreamProvider in main.dart like so:
...
StreamProvider(
catchError: (context, error) {
print(error);
},
initialData: null,
create: (context) => _quizService.getCurrentQuestionStream()),
...
In the app I have a collection called QuizQuestion, each document has a date as it's id, like 2021-12-15, and so every day the app should fetch the QuizQuestion of the day. My stream function looks like this:
Stream<QuizQuestion> getCurrentQuestionStream() {
String currentDate = DateFormat('yyyy-MM-dd').format(DateTime.now());
try {
return _firebaseFirestore
.collection("QuizQuestion")
.doc(currentDate)
.snapshots()
.map((doc) => QuizQuestion.fromJson(doc.data()));
} catch (e) {
return e.message;
}
}
This works to get the current day's QuizQuestion, but if the user has the app open from one day to the next, the stream function is still "subscribed" to fetch the previous date, since it won't define the currentDate variable again. I'm trying to figure out how to solve this, is is possible to some how to listen on a day change in flutter to reinitialize the stream with a new date, or do I need to rethink the backend here?
There is no way to change an existing query. You will have to construct a new query for the new date.
There are two common ways to do this:
Track the current date in your application code, and construct the new query once you detect the new date there.
Write the current date to a fixed location/documentation in the database, e.g. qotd for question/quiz of the day. You could for example do this when you also write the quiz for the next day. Now have your application listen to that document, and when you detect a change in the document, load the quiz for that day.
Both are valid options, and you can embed either option into a stream of your own, which you then feed to the stream builder. I typically prefer the latter as it also gives me a way to control what quiz(es) can be read through security rules.
I made a local database using sqflite to save data locally and i need to send it to a firebase in need
so far the function i used to get the data is:
Future<List> getNameAndPrice() async{
Database db = await instance.database;
var result = await db.rawQuery('SELECT $columnName , $columnAmount FROM $table');
return result.toList();
}
the other function i use in the other page of flutter to get the data is:
Future sendtofirebase() async {
var orders = await dbHelper.getNameAndPrice();
print(orders);
}
so far the print is just to check the data i get and i get the data in this format:
I/flutter (21542): [{name: Regular burger, amount: 2}, {name: Cheese
burger, amount: 1}]
i just want to find a way to like fetch this data from the variable and then send it to the firebase database i've made firestore collectionreference and everything i just don't know how can i get the name alone and send it then send the amount of the item etc.. for each order (how to get each item individually and send it to the firebase db).
really looking forward for answers cause i've spent time looking and i am stuck there..
ps. i am still a beginner and i even don't know if i should use both sqflite and firebase
other than just saving everything in the firebase itself.. thank you for reading.
I'm looking for a way to prevent writing more than a given limit of documents to a (sub)collection in a given periode.
For example: Messenger A is not allowed to write more then 1000 Messages per 24 hours.
This should be done in the context of an Firebase Function API endpoint because it's called by third parties.
The endpoint
app.post('/message', async function(req:any, res:any) {
// get the messenger's API key
const key = req.query.key
// if no key was provided, return error
if(!key) {
res.status(400).send('Please provide a valid key')
return
}
// get the messenger by the provided key
const getMessengerResult = await admin.firestore().collection('messengers')
.where('key', '==', key).limit(1).get()
// if there is no result the messenger is not valid, return error
if (getMessengerResult.empty){
res.status(400).send('Please provide a valid key')
return
}
// get the messenger from the result
const messenger = getMessengerResult.docs[0]
// TODO: check if messenger hasn't reached limit of 1000 messages per 24 hours
// get message info from the body
const title:String = req.body.title
const body: String = req.body.body
// add message
await messenger.ref.collection('messages').add({
'title':title,
'body':body,
'timestamp': Timestamp.now()
})
// send response
res.status(201).send('The notification has been created');
})
One thing I've tried was the following piece of code in place of the TODO::
// get the limit message and validate its timestamp
const limitMessageResult = await messenger.ref.collection('messages')
.orderBy('timestamp',"desc").limit(1).offset(1000).get()
if(!limitMessageResult.empty) {
const limitMessage = limitMessageResult.docs[0]
const timestamp: Timestamp = limitMessage.data()['timestamp']
// create a date object for 24 hours ago
const 24HoursAgo = new Date()
24HoursAgo.setDate(24HoursAgo.getDate()-1)
if(24HoursAgo < timestamp.toDate()) {
res.status(405).send('You\'ve exceeded your messages limit, please try again later!')
return
}
}
This code works, but there is a big BUT. The offset does indeed skip the 1000 results, but Firebase will still charge you for it! So every time the messenger tries to add 1 message, 1000+ are read... and that's costly.
So I need a better (cheaper) way to do this.
One thing I've come up with, but haven't yet tried would be adding an index/counter field to a message that increases by 1 every message.
Then instead of doing:
const limitMessageResult = await messenger.ref.collection('messages')
.orderBy('timestamp',"desc").limit(1).offset(1000).get()
I could do something like:
const limitMessageResult = await messenger.ref.collection('messages')
.where('index','==', currentIndex-1000).limit(1).get()
But I was wondering if that would be a save way.
For example, what would happen if there are multiple request at the same time.
I would first need to get the current index from the last message and add the new message with index+1. But could two requests read, and thus write the same index? Or could this be handled with transactions?
Or is there a totally different way to solve my problem?
I have a strong aversion against using offset() in my server-side code, precisely because it makes it seem like it's skipping documents, where it's actually reading-and-discarding them.
The simplest way I can think of to implement your maximum-writes-per-day count is to keep a writes-per-day counter for each messenger, that you then update whenever they write a message.
For example, you could do the following whenever you write a message:
await messenger.ref.collection('messages').add({
'title':title,
'body':body,
'timestamp': Timestamp.now()
})
const today = new Date().toISOString().substring(0, 10); // e.g. "2020-04-11"
await messenger.ref.set({
[today]: admin.firestore.FieldValue.increment(1)
}, { merge: true })
So this adds an additional field to your messenger document for each day, whee it then keeps a count of the number of messages that messenger has written for that day.
You'd then use this count instead of your current limitMessageResult.
const messageCount = (await messenger.get()).data()[today] || 0;
if (messageCount < 1000) {
... post the message and increase the counter
}
else {
... reject the message, and return a message
}
Steps left to do:
You'll want to secure write access to the counter fields, as the messenger shouldn't be able to modify these on their own.
You may want to clean out older message counts periodically, if you're worried about the messenger's document becoming too big. I prefer to leave these types of counters, as they give an opportunity to provide some stats cheaply if needed.
hi i am trying to increment a value when clicked the button if data is available in firestore this is my code bloc if you have any suggestion lmk please
int q = 0;
final snapShot = await Firestore.instance.collection('cart').document("LIihBLtbfuJ8Dy640DPd").get();
if(snapShot.exists){
q=q+1;
}
Firestore.instance.runTransaction((Transaction transaction) async {
await transaction.update(
Firestore.instance
.collection("cart")
.document("LIihBLtbfuJ8Dy640DPd"),
{
foodItem.name: {
'itemName': foodItem.name,
'imgUrl': foodItem.imageAssetPath,
'itemPrice': foodItem.price,
'quantity': q,
}
});
});
In November 2021, this worked for me.
FirebaseFirestore.instance.collection('users').doc(currentUser?.id).update({
'bronzeBadges': FieldValue.increment(2),
});
var quantityref = db.collection("cart").document("LIihBLtbfuJ8Dy640DPd");
// Increment the quantity field by 1.
quantityref.update({
"quantity" : firebase.firestore.FieldValue.increment(1)});
If your want to change a value based on the previous one, you have basically two approaches:
Make use of transactions. I see you're doing that but incorrectly, because you're fetching the current value outside of it, and it could change by the moment you run the update, causing data inconsistencies. I don't know about Flutter, but as far as I know, a Transaction in Firebase consists in a read operation followed by one or more write operations, and the value returned from the read will be the very last one and won't be changed before you finish the transaction, so you can be sure you're working with the latest one. I suggest you to read the Transactions docs.
increment method (recommended): See this see this answer for incrementing in Flutter
First of all, you need to get the desired document and its elements to update the document of fields. In your example, it is quantity.
First, get the document:
Firestore.instance
.collection('cart')
.document('documentID')
.get()
.then((DocumentSnapshot ds) {
// use ds, parse ds then access the quantity
});
After doing the job, you need to update the field. Thankfully, there is an updateData function in firestore instance.
db.collection('cart')
.document('documentID')
.updateData({'quantity': someQuantity});
Hope it helps.
In the init, of my application I register a stream to one of the documents stored in firestore. Later I update a timestamp field in the same document. I should be getting one callback from the stream since there is only 1 update.
However, I am getting 2 callbacks -
Where that updated field is null
Where that updated field has the correct updated value
Any ideas why?
CollectionReference collectionReference = FIRESTORE.collection("users");
if(streamSub == null) {
streamSub = collectionReference.document(documentID).snapshots().listen((onData){
onData.data.forEach((k,v) => debugPrint(k + " = " + v.toString()));
});
}
//Update field
Firestore.instance
.collection("users")
.document(documentID)
.updateData({"Time" : FieldValue.serverTimestamp() })
You're getting two callbacks because of your use of FieldValue.serverTimestamp(). That value is actually a token that gets sent to Firestore servers, where the timestamp is determined and finally written to the database. Locally on the client, the value isn't known at the time of the write, however, a write of the document still happens in the local cache.
Your listener is first getting the local cache write (before the timestamp is known), then again from the server after the timestamp is known. You can look at the snapshot metadata to figure out the source of the data if that's important to you.