I have a node in Firebase that has multiple childs and childs of childs. I want to move all from one place to another, then I made a function with Firebase REST API, but I would like to optimize it and migrate it to SDK.
The function I'm using with API REST is:
Future<bool> moverLoteActual() async {
//TO READ FROM SOURCE PATH
final urlLoteActual = '$_PATH_ORIGEN.../loteActual.json?auth=${_prefs.token}';
final resp = await http.get(urlLoteActual);
//TODO: Probably I neew an error management here
//TO COPY IN DESTINATION
final urlLotesCerrados = '$PATH_DESTINO.../lotesCerrados.json?auth=${_prefs.token}';
final resp2 = await http.post(urlLotesCerrados, body: resp.body);
final decodedData2 = json.decode(resp2.body);
print(decodedData2);
//TO DELETE THE SOURCE NODE
final resp3 = await http.delete(urlLoteActual);
print(json.decode(resp3.body));
return true;
}
I tried with SDK but I get a lot of parsing errors and finally I think a sequential approach as I implemented with API REST is not the best way.
How can I get this change of location using SDK?
I found a solution.
First, you need to understand that snapshot.value is a Map<dynamic, dynamic>, then you need to assign the answer (resp) to this type of map.
Later, you need to copy the node in a new location, then you need to convert the previous Map to a new one of other type (Map<String, dynamic> to write)
The last step (delete the original node) is easy... the standard function.
Future<bool> moverLoteActual() async {
//TO READ FROM SOURCE PATH
Map<dynamic, dynamic> _map = new Map<dynamic, dynamic>();
Query resp = db.child('$_PATH_ORIGEN.../loteActual');
final snapshot = await resp.once();
_map = snapshot.value;
//TO COPY IN DESTINATION
db.child('$PATH_DESTINO.../lotesCerrados')
.push().update(Map<String, dynamic>.from(_map));
//TO DELETE THE SOURCE NODE
db.child('$_PATH_ORIGEN.../loteActual').remove();
return true;
}
PD. db is the Firebase Database Reference
Related
I want to check if a user has a string ’Level' with any number in his document.
Level: int
If this is the case, the future should return true and false if not.
That’s the code I’m trying it with:
class StufenService{
String userID;
StufenService(this.userID);
final CollectionReference userTodos =
FirebaseFirestore.instance.collection('userTodos');
Future checkIfStufeExists() async {
await userTodos.where('Stufe' is int).get();
final data = QuerySnapshot.data.documents.where(userID);
if (data.exists){
return true;
} else
{return false;}
}
}
First I filter out all users who have ’Level': int in their firebased document. Then I want to check if the current user is among the users.
The data after the QuerySnapshot is underlined in red:
The getter 'data' isn't defined for the type 'QuerySnapshot'.
Can someone help me to implement my plan?
Maybe the whole thing has to be done differently?
For cases like this, I find it most useful to keep the FlutterFire documentation and Firestore reference handy. Based on those, you code should be something like:
final CollectionReference userTodos =
FirebaseFirestore.instance.collection('userTodos');
Future checkIfStufeExists() async {
var query = userTodos.where('Stufe', isEqualTo: 42); // First condition
query = query.where("userId", isEqualTo: userID); // Second condition
final querySnapshot = await query.get(); // Read from the database
return querySnapshot.size > 0; // Check if there are any results
}
You are not doing anything with the returned value of the where statement.
The class QuerySnapshot does not have .data static getter. In order to access the returned value from firestore you need to do something like that:
...
final snapshot = await userTodos.where('Stufe' is int).get();
final data = snapshot.data;
...
Okay, so I have multiple specific document ids that are all the same, but each one is used for different collections and purposes. Using a collection group as these documents are nested in subcollections, I was able to locate these documents and print them. How would I go about updating a specific field in all of these documents at the same time with some other data? In my case these documents despite being in different collections have a field called, plastics and I want to update them with int data.
Here is the code I used to retrieve these documents and filter to only print the one's I specificly need:
final String uid = FirebaseAuth.instance.currentUser.uid;
QuerySnapshot querySnapshot = await FirebaseFirestore
.instance
.collectionGroup("Members")
.get();
for (int i = 0; i < querySnapshot.docs.length; i++) {
var a = querySnapshot.docs[i];
if (a.id == uid) {
print(a.id);
}
}
Also here is some code I've used before for updating a field in a single document, but not all of them like I need to in this case.
Future<bool> addPlastic(String amount) async {
try {
String uid = auth.currentUser.uid;
var value = double.parse(amount);
DocumentReference documentReference =
FirebaseFirestore.instance.collection('some collection').doc(some document);
FirebaseFirestore.instance.runTransaction((transaction) async {
DocumentSnapshot snapshot = await transaction.get(documentReference);
if (!snapshot.exists) {
documentReference.set({'plastics': value});
return true;
}
double newAmount = snapshot.data()['plastics'] + value;
transaction.update(documentReference, {'plastics': newAmount});
return true;
});
} catch (e) {
return false;
}
}
You have two options, either loop through all the collections and sub collections (tedious), or store a list document references in one of the documents and change the data by looping through all of these document references ( better option). If you need some code or guidelines on how to do that, let me know.
EDIT
To use the document reference part, first, when creating the document, you have to do something like this
document(id).set({
"value1":"oko",
"plastic":"240",
//And the rest of your values
"otherSnapshots":[/*Your list of documentSnapshots with the same ID. Update it in this whenever you have new one by following the next snippet*/]
})
When you create a new document, navigate to this and add in the document Reference by
snapshot.reference.update({
'otherSnapshots':FieldValue.arrayUnion([/*Document Reference*/])
});
And next time, when you want to update all of them, use this field, loop through it and then you will get all the document references. I cannot create a code that you can directly copy paste into your code without seeing how u store data
I'm trying to get the key of a child that I've already returned from the database. In the example below, I have the user selected as Carlos Sainz and I'm trying to get "csainz" however the code below returns "email".
DatabaseReference keyRef = FirebaseDatabase.instance.reference();
await keyRef.child('users')
.child('email')
.equalTo(userList[0].email) // userList[0].email returns Carlos' email
.once()
.then((DataSnapshot dataSnapshot) {
String newKey = dataSnapshot.key;
print(newKey);
});
Here is how my db is setup:
Two problems:
To order/filter on a specific property of each child node, you need to use orderByChild(...) and not just child(...). Right now your code reads /users/email, which doesn't exist.
When you execute a query against the Firebase Database, there will potentially be multiple results. So the snapshot contains a list of those results. Even if there is only a single result, the snapshot will contain a list of one result.
Your code doesn't handle the list, but prints the key of the location against which the query was executed: users.
So to fix both would look something like this:
DatabaseReference keyRef = FirebaseDatabase.instance.reference();
await keyRef.child('users')
.orderByChild('email')
.equalTo(userList[0].email)
.onChildAdded.listen((Event event) {
print('${event.snapshot.key}');
}, onError: (Object o) {
final DatabaseError error = o;
print('Error: ${error.code} ${error.message}');
});
});
You can also use .once().then(...) but will then have convert dataSnapshot.value to a map, and show the key(s) from that map. Not check, but it should be something like this:
DatabaseReference keyRef = FirebaseDatabase.instance.reference();
await keyRef.child('users')
.orderByChild('email')
.equalTo(userList[0].email)
.once()
.then((DataSnapshot dataSnapshot) {
String newKey = dataSnapshot.value.keys[0];
print(newKey);
});
I was trying to simulate a situation where two users (on seperate devices) both run a Transaction at the same time. To imitate this, I made a List<String> of strings which would be added to the database without a delay between them.
However, only the first item in the List was added to the database, the second never arrived. What am I doing wrong? I am trying to have both items added to the database.
The call to the Transaction happens in the code below, along with the creation of the list:
List<String> items = new List<String>();
items.add("A test String 1");
items.add("A test String 2");
for (String q in questions)
{
database.updateDoc( q );
}
The code I use for updating the data in my database:
void updateDoc( String item ) async
{
var data = new Map<String, dynamic>();
data['item'] = item;
Firestore.instance.runTransaction((Transaction transaction) async {
/// Generate a unique ID
String uniqueID = await _generateUniqueQuestionCode();
/// Update existing list
DocumentReference docRef = Firestore.instance
.collection("questions")
.document("questionList");
List<String> questions;
await transaction.get(docRef)
.then (
(document) {
/// Convert List<dynamic> to List<String>
List<dynamic> existing = document.data['questions'];
questions = existing.cast<String>().toList();
}
);
if ( ! questions.contains(uniqueID) )
{
questions.add( uniqueID );
var newData = new Map<String, dynamic>();
newData['questions'] = questions;
transaction.set(docRef, newData );
}
/// Save question
docRef = Firestore.instance
.collection("questions")
.document( uniqueID );
transaction.set(docRef, data);
});
}
In reality, I have a few fields in the document I'm saving but they would only complicate the code.
I keep track of a list of documents because I need to be able to retreive a random document from the database.
When executing the first code snippet, only the first item in the list will be added to the database and to the list that keeps track of the documents.
No error is thrown in the debug screen.
What am I missing here?
As explained by Doug Stevenson in the comments under my question:
That's not a typical use case for a single app instance. If you're
trying to find out if transactions work, be assured that they do.
They're meant to defend against cases where multiple apps or processes
are making changes to a document, not a single app instance.
And also:
The way the Firestore SDK works is that it keeps a single connection
open and pipelines each request through that connection. When there
are multiple clients, you have multiple connection, and each request
can hit the service at a different time. I'd suspect that what you're
trying to simulate isn't really close to the real thing.
I want to use JSON as a local database for my firebase data ,I don't want offline data store.I am using path_provider to store my data on the phone
So I tried storing data with
file.writeAsStringSync(json.encode(snapshot.toString()))
And it worked the stored data file looked like a JSON file
"{-key:{name:name,age:age},-key:{name:name,age:age}}"
The " " where missing and I can't decode it.
So what can be done here ?
(Edit:Does Firestore provide anything that can help me with this ?)
You can use the shared_preferences package to store JSON,
or if you want to write to a custom file use the path_provider package to get paths to directories where you app has permissions to write to.
You might also need to use the async versions of the dart:io API to access files in Flutter (not sure about that tough)
"{-key:{name:name,age:age},-key:{name:name,age:age}}" is not valid JSON
Either use
import 'dart:convert';
...
var myJson = {'-key':{'name:name','age:age'},'-key':{'name:name','age:age'}}";
var myJsonString = jsonEncode(myJson);
await file.writeAsString(myJsonString);
or
var myJsonString = '{"-key":{"name:name","age:age"},"-key":{"name:name","age:age"}}';
await file.writeAsString(myJsonString);
Create a JSON File
Future<String> get _localPath async {
final directory = await getExternalStorageDirectory();
return directory.path;
}
Future<File> get _dbFile async {
final path = await _localPath;
return new File("$path/demo.json");
}
Write data into a file
Future<File> write(DataSnapshot snapshot,String chatId) async {
final path = await _dbFile;
final String key = snapshot.key;
final String name = snapshot.value['senderUid'];
final int age= snapshot.value['receivedUid'];
String content = '{"$key":{"name":"$name","age":"$age"}}';
return file.writeAsStringSync(content);
}
Read Data
Future<Null> read() async {
try {
final file = await _dbFile;
String content = file.readAsStringSync();
Map<String, dynamic> chatMap=json.decode(content);
chatMap.keys.forEach((E){debugPrint(E);});
} catch (e) {
debugPrint('Error : '+e.toString());
}
}