Flutter Firebase update will not stop updating node? - firebase

I'm searching for an int value in my firebase node and decreasing it. It successfully decreases and prints the correct info to my log once. When I attempt to update the node with the new int it repeats as if it where in a loop. How can I get it to update a single time? Here is my code...
if (vidRank == 1) {
await fb.child('UserVideo/${userid}/Vid1').onValue.listen((Event event){
if (event.snapshot != null){
var vid1id = event.snapshot.value['videoID'].toString();
fb.child('NumberOnes/${vid1id}').onValue.listen((Event onesEvent){
if (onesEvent.snapshot != null){
var onesValue = (onesEvent.snapshot.value['Value'] as int);
final vidValue = onesValue - 1;
print("Inside ${vidValue}");
fb.child('NumberOnes/${vid1id}').update({
'Value': vidValue
});
}
});
}
});

If you only want a single action, use .once()
if (vidRank == 1) {
var event = await fb.child('UserVideo/${userid}/Vid1').once();
if (event.snapshot != null){
var vid1id = event.snapshot.value['videoID'].toString();
var onesEvent = await fb.child('NumberOnes/${vid1id}').once();
if (onesEvent.snapshot != null){
var onesValue = (onesEvent.snapshot.value['Value'] as int);
final vidValue = onesValue - 1;
print("Inside ${vidValue}");
fb.child('NumberOnes/${vid1id}').update({
'Value': vidValue
});
}
}
}
otherwise an update will cause another event for listen(...) and you have a perfect loop.

Related

firestore keyword working in the code in flutter

Hi guys i am trying write firestore.instance but it give error. I can not under stand why this error occur in the below code.
void _fetchMarkersFromDb() {
// TODO: improve this
print('_fetchMarkersFromDb() called');
***Firestore***.instance.collection('markers').getDocuments().then((docs) async {
final docLength = docs.documents.length;
final clients = List(docLength);
for (int i = 0; i < docLength; i++) {
clients[i] = docs.documents[i];
}
if (!isFirstCycle && isMyMarkerFetched) {
currentLocation = await Geolocator.getCurrentPosition();
}
_populateMarkers(clients);
});
}
enter image description here
Use FirebaseFirestore instead.
Like:
FirebaseFirestore.instance.collection('markers').get().then((value) async {
var docs = value.docs;
final docLength = docs.length;
final clients = List(docLength);
for (int i = 0; i < docLength; i++) {
clients[i] = docs[i];
}
if (!isFirstCycle && isMyMarkerFetched) {
currentLocation = await Geolocator.getCurrentPosition();
}
_populateMarkers(clients);
});
To delete a document use:
FirebaseFirestore.instance.collection('markers').doc(documentId).delete()

How to cancel Flutter RealTime Database Transactions

I have a firebase object that a user increments but can't increment to a certain number.
I want to have a condition in the transaction to prevent the user from incrementing past a set number.
How do I cancel the transaction and return an error to the user i.e
try {
int setLimit = 12; //example limit... Can vary
final TransactionResult transactionResult = await myRef.runTransaction((MutableData mutableData) async {
var currentValue = mutableData.value ?? 0;
if (currentValue >= setLimit) {
throw 'full'; // .... how do I return from this (return throws error ... Expects MutableData)
}
mutableData.value = (mutableData.value ?? 0) + 1;
return mutableData;
});
Adding the transactionResult handlers seems to work
int setLimit = 12;
final TransactionResult transactionResult = await myRef.runTransaction((MutableData mutableData) async {
var currentValue = mutableData.value ?? 0;
if (currentValue >= setLimit) {
throw 'full';
}
mutableData.value = (mutableData.value ?? 0) + 1;
return mutableData;
});
if (transactionResult.committed) {
return transactionResult.dataSnapshot.value;
} else {
if (transactionResult.error != null) {
return transactionResult.error.details;
} else {
return 'full';
}
}

using futures with flutter maps

I have this code which is supposed to loop through images to upload them to firebase, then take their links and put it inside the Product class so the product can have its images link. Then, upload the product too.
The problem is it doesn't wait for the upload to happen to insert its links to the product.
Code
List<String> imgUrls = [];
Future<void> loopThroughMap()async{
_map.forEach((storedImage, inGallery) async {
if (!inGallery && storedImage != null && storedImage.path != null) {
await GallerySaver.saveImage(storedImage.path);
}
String urlString = await FirestoreHelper.uploadImage(storedImage);
imgUrls.add(urlString);
});
}
This function is called in here
`
await loopThroughMap();
print('FINISHED THIS ONE, imgs Urls length ${imgUrls.length}');
for (int i = 0; i < imgUrls.length; i++) {
if (i == 0)
_editedProduct.imageUrl = imgUrls[i];
else if (i == 1)
_editedProduct.imageUrl2 = imgUrls[i];
else if (i == 2)
_editedProduct.imageUrl3 = imgUrls[i];
else if (i == 3) _editedProduct.imageUrl4 = imgUrls[i];
}`
The imgUrls list length is ALWAYS zero.
Could it be a problem with the map.foreach?
Ok, I changed the uploading from the ForEach loop to a for loop, and it works fine!
List<File> _listedImages = [];
Future<void> loopThroughMap(){
_map.forEach((storedImage, inGallery) async {
if (!inGallery && storedImage != null && storedImage.path != null) {
GallerySaver.saveImage(storedImage.path);
}
_listedImages.add(storedImage);
});
}
/////
for(var img in _listedImages){
String url = await FirestoreHelper.uploadImage(img);
imgUrls.add(url);
}
After a lot of iterations, the only variable that was causing the error was the ForEach loop.

how to use loop inside query on firestore?

Is it possible to use loop inside query?
Future<void> addServiceConfig(String uid, List<ServiceConfigModel> model) {
_db.collection('users').document(uid).updateData({
'businessDetails':{
'serviceConfig':FieldValue.arrayUnion([
{
for(int i = 0;i <model.length;i++){
if (model[i].inShop != null) 'inShop': model[i].inShop,
if (model[i].inShopAndClientLocation != null)
'inShopAndClientLocation': model[i].inShopAndClientLocation,
if (model[i].clientLocation != null)
'clientLocation': model[i].clientLocation,
'serviceLocations': model[i].serviceLocations,
'subCategoryId': model[i].subCategoryId
}
}
])
}
});
}
I got this error:
[ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: Invalid argument: Instance of '_CompactLinkedHashSet>'
You can't use a loop (or many other statements) inside an expression, like the value of a field. One solution is to pull construct the value outside of the update() all and then pass it in:
//var config = new Map()
var result = new List();
for(int i = 0;i < model.length; i++){
var config = new Map();
if (model[i].inShop != null) config['inShop'] = model[i].inShop;
if (model[i].inShopAndClientLocation != null)
config['inShopAndClientLocation'] = model[i].inShopAndClientLocation;
if (model[i].clientLocation != null)
config['clientLocation'] = model[i].clientLocation;
config['serviceLocations'] = model[i].serviceLocations,
config['subCategoryId'] = model[i].subCategoryId;
result.add(config);
}
_db.collection('users').document(uid).updateData({
'businessDetails':{
'serviceConfig': FieldValue.arrayUnion(result)
}
});

Cosmosdb store procedure get less documents than real

I am preparing store procedure on cosmosdb by Javascript, however, it gets less documents than the real number of documents in collection.
The sproc is called by C#, C# pass a parameter "transmitterMMSI" which is also the partition key of this collection.
First, the following query is executed in sproc:
var query = 'SELECT COUNT(1) AS Num FROM AISData a WHERE a.TransmitterMMSI="' + transmitterMMSI + '"';
The result is output in response, and the value is 5761, which is the same as the real number of documents in collection.
However, when I change the query to the following:
var query = 'SELECT * FROM AISData a WHERE a.TransmitterMMSI="' + transmitterMMSI + '"';
The documents.length is output as 5574, which is smaller than the real number.
I have already changed the pageSize: -1, which should mean unlimited.
I did some search with google and stack overflow, it seems that continuation can be help. However, I tried some examples, and they don't work.
Anyone familiar with this can help?
The following list the scripts.
The sproc js script is here, which is also the file "DownSampling.js" used in the C# code:
function DownSampling(transmitterMMSI, interval) {
var context = getContext();
var collection = context.getCollection();
var response = context.getResponse();
var receiverTime;
var tempTime;
var groupKey;
var aggGroup = new Object();
var query = 'SELECT * FROM AISData a WHERE a.TransmitterMMSI="' + transmitterMMSI + '"';
var accept = collection.queryDocuments(collection.getSelfLink(), query, { pageSize: -1},
function (err, documents, responseOptions) {
if (err) throw new Error("Error" + err.message);
// Find the smallest deviation comparting to IntervalTime in each group
for (i = 0; i < documents.length; i++) {
receiverTime = Date.parse(documents[i].ReceiverTime);
tempTime = receiverTime / 1000 + interval / 2;
documents[i].IntervalTime = (tempTime - tempTime % interval) * 1000;
documents[i].Deviation = Math.abs(receiverTime - documents[i].IntervalTime);
// Generate a group key for each group, combinated of TransmitterMMSI and IntervalTime
groupKey = documents[i].IntervalTime.toString();
if (typeof aggGroup[groupKey] === 'undefined' || aggGroup[groupKey] > documents[i].Deviation) {
aggGroup[groupKey] = documents[i].Deviation;
}
}
// Tag the downsampling
for (i = 0; i < documents.length; i++) {
groupKey = documents[i].IntervalTime;
if (aggGroup[groupKey] == documents[i].Deviation) {
documents[i].DownSamplingTag = 1;
} else {
documents[i].DownSamplingTag = 0;
}
// Remove the items that are not used
delete documents[i].IntervalTime;
delete documents[i].Deviation;
// Replace the document
var acceptDoc = collection.replaceDocument(documents[i]._self, documents[i], {},
function (errDoc, docReplaced) {
if (errDoc) {
throw new Error("Update documents error:" + errDoc.message);
}
});
if (!acceptDoc) {
throw "Update documents not accepted, abort ";
}
}
response.setBody(documents.length);
});
if (!accept) {
throw new Error("The stored procedure timed out.");
}
}
And the C# code is here:
private async Task DownSampling()
{
Database database = this.client.CreateDatabaseQuery().Where(db => db.Id == DatabaseId).ToArray().FirstOrDefault();
DocumentCollection collection = this.client.CreateDocumentCollectionQuery(database.SelfLink).Where(c => c.Id == AISTestCollectionId).ToArray().FirstOrDefault();
string scriptFileName = #"..\..\StoredProcedures\DownSampling.js";
string scriptId = Path.GetFileNameWithoutExtension(scriptFileName);
var sproc = new StoredProcedure
{
Id = scriptId,
Body = File.ReadAllText(scriptFileName)
};
await TryDeleteStoredProcedure(collection.SelfLink, sproc.Id);
sproc = await this.client.CreateStoredProcedureAsync(collection.SelfLink, sproc);
IQueryable<dynamic> query = this.client.CreateDocumentQuery(
UriFactory.CreateDocumentCollectionUri(DatabaseId, AISTestCollectionId),
new SqlQuerySpec()
{
//QueryText = "SELECT a.TransmitterMMSI FROM " + AISTestCollectionId + " a",
QueryText = "SELECT a.TransmitterMMSI FROM " + AISTestCollectionId + " a WHERE a.TransmitterMMSI=\"219633000\"",
}, new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true, MaxDegreeOfParallelism = -1, MaxBufferedItemCount = -1 });
List<dynamic> transmitterMMSIList = query.ToList(); //TODO: Remove duplicates
Console.WriteLine("TransmitterMMSI count: {0}", transmitterMMSIList.Count());
HashSet<string> exist = new HashSet<string>();
foreach (var item in transmitterMMSIList)
{
//int transmitterMMSI = Int32.Parse(item.TransmitterMMSI.ToString());
string transmitterMMSI = item.TransmitterMMSI.ToString();
if (exist.Contains(transmitterMMSI))
{
continue;
}
exist.Add(transmitterMMSI);
Console.WriteLine("TransmitterMMSI: {0} is being processed.", transmitterMMSI);
var response = await this.client.ExecuteStoredProcedureAsync<string>(sproc.SelfLink,
new RequestOptions { PartitionKey = new PartitionKey(transmitterMMSI) }, transmitterMMSI, 30);
string s = response.Response;
Console.WriteLine("TransmitterMMSI: {0} is processed completely.", transmitterMMSI);
}
}
private async Task TryDeleteStoredProcedure(string collectionSelfLink, string sprocId)
{
StoredProcedure sproc = this.client.CreateStoredProcedureQuery(collectionSelfLink).Where(s => s.Id == sprocId).AsEnumerable().FirstOrDefault();
if (sproc != null)
{
await client.DeleteStoredProcedureAsync(sproc.SelfLink);
}
}
I tried to comment the 2 loops in the JS codes, only the documents.length output, while the response number is still less. However, I changed the query to SELECT a.id, the documents.length is correct. Looks like it is the continuation issue.
The sproc is probably timing out. To use a continuation token in these circumstances, you will need to return it to your C# calling code then make another call to the sproc passing in your token. If you show us your sproc code we can help more.
You can use a continuation token to make repeated calls to queryDocuments() from within the sproc without additional roundtrips to the client. Keep in mind that if you do this too many times your sproc will eventually timeout, though. In your case, it sounds like you're already very close to getting all of the documents you're seeking so maybe you will be OK.
Here is an example of using a continuation token within a sproc to query multiple pages of data:
function getManyThings() {
var collection = getContext().getCollection();
var query = {
query: 'SELECT r.id, r.FieldOne, r.FieldTwo FROM root r WHERE r.FieldThree="sought"'
};
var continuationToken;
getThings(continuationToken);
function getThings(continuationToken) {
var requestOptions = {
continuation: continuationToken,
pageSize: 1000 // Adjust this to suit your needs
};
var isAccepted = collection.queryDocuments(collection.getSelfLink(), query, requestOptions, function (err, feed, responseOptions) {
if (err) {
throw err;
}
for (var i = 0, len = feed.length; i < len; i++) {
var thing = feed[i];
// Do your logic on thing...
}
if (responseOptions.continuation) {
getThings(responseOptions.continuation);
}
else {
var response = getContext().getResponse();
response.setBody("RESULTS OF YOUR LOGIC");
}
});
if (!isAccepted) {
var response = getContext().getResponse();
response.setBody("Server rejected query - please narrow search criteria");
}
}
}

Resources