I have Flutter + Firestore app with a perfomance problem: large database query execution time (about a 5 sec.). I have a small database size, I think that if it increases, the query execution speed will be even greater. How can I improve application performance?
import 'package:carstat/models/entry.dart';
import 'package:carstat/models/operation.dart';
import 'package:carstat/services/data_service.dart';
class DashboardService {
DataService dataService = DataService();
getMarkers(List<Entry> entries, String carId) async {
var _markers = [];
for (int i = 0; i < entries.length; i++) {
List<Operation> _operations = [];
_operations =
await dataService.getEntryOperations(entries[i].entryId, carId);
_markers.add({'entry': entries[i], 'operations': _operations});
}
return _markers;
}
}
My data structure for example:
.document(docId)
.collection('cars')
.document(carId)
.collection('entries')
.document(entryId)
.collection('operations')
.document();
DataService code:
getEntryOperations(String entryId, String carId) async {
List<Operation> _operations = [];
Future<QuerySnapshot> _userDoc =
fs.where('userId', isEqualTo: _userId).getDocuments();
await _userDoc.then((res) {
docId = res.documents[0].documentID;
});
Future<QuerySnapshot> _entryOperations = fs
.document(docId)
.collection('cars')
.document(carId)
.collection('entries')
.document(entryId)
.collection('operations')
.getDocuments();
await _entryOperations.then((val) {
for (int i = 0; i < val.documents.length; i++) {
var _operation = Operation();
_operation.entryId = entryId;
_operation.operationNote = val.documents[i].data['operationNote'];
_operation.operationDate =
val.documents[i].data['operationDate'].toDate();
_operation.operationMileage = val.documents[i].data['operationMileage'];
_operation.operationPartName =
val.documents[i].data['operationPartName'];
_operation.operationPrice = val.documents[i].data['operationPrice'];
_operation.partPrice = val.documents[i]['partPrice'];
_operation.operationId = val.documents[i]['operationId'];
_operations.add(_operation);
}
});
return _operations;
}
The query you're showing is unconditionally getting all of the documents in a specific subcollection. Of course, that will take more time as the collection grows. There is no secret trick or special flag to pass to make this query happen any faster.
In fact, there is not much you can do about this at all, other than to limit the size of the collection, or limit the number of documents in the query. You might also want to reconsider your database structure to reduce the number of documents you're fetching.
My answer, much faster
class DashboardService {
DataService dataService = DataService();
getMarkers(List<Entry> entries, String carId) async {
var _marker = []; // коллекция списков операторов для каждого регламента ТО
final opsForEntries = await Future.wait(
entries.map((value) {
return dataService.getEntryOperations(value.entryId, carId);
})
);
for(int i = 0; i < entries.length; i++) {
_marker.add(
{
'entry': entries[i],
'operations': opsForEntries[i]
}
);
}
return _marker;
}
}
Related
Firestore with Dart requires query operators to be passed as objects.
Docs: https://firebase.google.com/docs/firestore/query-data/queries
Ex Dart:
.where("key", isEqualTo: value)
Ex Go:
.where("key", "==", value).
In the case of go, passing a string "==" as the query operator is pretty straight forward.
For dart, i am trying to figure out how to store isEqualTo: as a variable, to then pass into the function.
Ok here is my code. Any help is really appreciated thank you!
Here is the DataModel
class FirestoreQueryModel extends Equatable {
//Variables
final String property;
final FirestoreOperatorEnum operator;
// This is the value where i want to store the list of operators
final dynamic value;
}
Here is the current repository
class CustomerRepository{
late Query golbalQuery
Stream <List<CustomerModel>> dynmamicCollectonStream(List<FirestoreQueryModel> queryList,) { //If query list is null, do not apply where clause
if (queryList.isEmpty) { return collectionRef().snapshots().map((doc) {var returnedList = doc.docs; var mappedList = returnedList.map((doc) => CustomerModel.fromDocument(doc)).toList();
return mappedList;
});
} else {var count = queryList.length; CollectionReference cRef = collectionRef();
for (var i = 0; i < count; i++) { golbalQuery = cRef.where(queryList[i].property, isEqualTo: queryList[i].value); }
var list = golbalQuery.snapshots().map((doc) { var returnedList = doc.docs; var mappedList = returnedList.map((doc) =\> CustomerModel.fromDocument(doc)).toList(); return mappedList; }); return list; } }}
In the for loop, where we convert the data model to a where clause I currently have hardcoded isEqualTo:
The goal is to convert
for (var i = 0; i < count; i++) { golbalQuery = cRef.where(queryList[i].property, isEqualTo: queryList[i].value); }
to
for (var i = 0; i < count; i++) { golbalQuery = cRef.where(queryList[i].property, qyeryList[i].operator, queryList[i].value); }
Ok so I have a problem in which I use a collection to gather some ratings data and work with it, by the time I finish the rating update process, I have new ratings that I would like to update the collection with. However I can't call update because I get the error "Cannot overwrite model once compiled." I understand that I already called once the model to work with the data and that's why I get the error. is there any way I can update the collection? Or I will just have to workaround by creating a new collection with the latest rating, and then matching the latest ratings collection with the one I use to work with the data.
This is my code
let calculateRating = async () => {
const getData = await matchesCollection().find().lean();
const playerCollection = await playersCollection();
const getDataPlayer = await playerCollection.find().lean();
let gamesCounting = [];
getDataPlayer.forEach((player) => {
player.makePlayer = ranking.makePlayer(1500);
});
for (let i = 0; i < getData.length; i++) {
const resultA = getDataPlayer.findIndex(({ userId }, index) => {
if (userId === getData[i].userA) {
return index;
}
});
const resultB = getDataPlayer.findIndex(
({ userId }) => userId === getData[i].userB
);
const winner = getData[i].winner;
if (getDataPlayer[resultA] === undefined) {
continue;
} else if (getDataPlayer[resultB] === undefined) {
continue;
}
gamesCounting.push([
getDataPlayer[resultA].makePlayer,
getDataPlayer[resultB].makePlayer,
winner,
]);
}
ranking.updateRatings(gamesCounting);
let ratingsUpdate = [];
getDataPlayer.forEach((item) => {
let newRating = item.makePlayer.getRating();
let newDeviation = item.makePlayer.getRd();
let newVolatility = item.makePlayer.getVol();
item.rating = newRating;
item.rd = newDeviation;
item.vol = newVolatility;
ratingsUpdate.push(item);
});
};
I try the work around with creating the new collection
I use firebase firestore to store multiple documents with auto-generated IDs. In my code I generate a random ID and then query the database with "isGreaterThanOrEqualTo: _randomIndex" to read 3 random documents. My issue is: My database is not very big yet. When I use this approach, I sometimes get 0, only 1 or only 2 documents back although I iterate through it 3 times to get 3 documents. I think the reason is because of the limited amount of data there is sometimes no "greaterThanOrEqualTo" ID in my database so I get nothing back.
How can I enhance my code to get always 3 documents back? Is there a way to change the "where query" to
.where('id', isGreaterThanOrEqualTo OR isLessThanOrEqualTo: _randomIndex
My whole Code:
String getRandomGeneratedId() {
const int AUTO_ID_LENGTH = 20;
const String AUTO_ID_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const int maxRandom = AUTO_ID_ALPHABET.length;
final Random randomGen = Random();
String id = '';
for (int i = 0; i < AUTO_ID_LENGTH; i++) {
id = id + AUTO_ID_ALPHABET[randomGen.nextInt(maxRandom)];
print('RandomID is $id');
}
return id;
}
Future<List> getData() async {
List<Recipe> dataList = [];
CollectionReference myRef = FirebaseFirestore.instance.collection('recipes');
// Retrieves 3 random data in a loop
for (int i = 0; i < 3; i++) {
// generate a random index based on the list length
// and use it to retrieve the element
String _randomIndex = getRandomGeneratedId();
print('RandomIndex is $_randomIndex');
QuerySnapshot querySnapshot = await myRef
.where('id', isGreaterThanOrEqualTo: _randomIndex) // CHANGE THIS PART?
.orderBy('id', descending: false)
.limit(1)
.get();
print('QUERYSNAP is $querySnapshot');
dataList.addAll(querySnapshot.docs.map((d)=> Recipe.fromJson(d.data() as Map<String, dynamic>)));
I need to iterate through all the documents in the "exercise" collection and update them with new values. I wrote a function that works but it's an inefficient solution. This is the whole function:
final thisWorkout = await FirebaseFirestore.instance
.collection('workouts')
.doc(widget.docRef!.id);
final allExercises = await thisWorkout.collection('exercise').get();
thisWorkout.update({
'name': workoutName,
});
List allIds = [];
allExercises.docs.forEach((DocumentSnapshot value) {
allIds.add(value.reference.id);
});
for (var i = 0; i < exercises.length; i++) {
thisWorkout.collection('exercise').doc(allIds[i]).set(exercises[i]);
}
This is the portion of the function that is inefficient:
List allIds = [];
allExercises.docs.forEach((DocumentSnapshot value) {
allIds.add(value.reference.id);
});
for (var i = 0; i < exercises.length; i++) {
thisWorkout.collection('exercise').doc(allIds[i]).set(exercises[i]);
}
Is there a way to just iterate through each doc AND update it without having store all the ids then run a separate for loop for the update?
You mean this?
allExercises.docs.forEach((DocumentSnapshot value) {
thisWorkout.collection('exercise').doc(value.reference.id).set(exercises[i]);
});
I've build an List with 44 places:
List<String> departmentdes = new List(44);
after that I've called a function:
postDepartment();
The function is an api call:
postDepartment() async {
final response = await http.get('url');
final jsonresponse = json.decode(response.body);
List<Department> departments = [];
for(var d in jsonresponse) {
Department department = Department(
fid: d["fid"].toString(),
);
departments.add(department);
}
int index = departments.length -1;
for(int i = 0; i<=index; i++) {
departmentdes[i] = departments[i].fid;
}
return departmentdes;
}
After the postDepartment(); I want to print the departmentdes but it always returns null. Why?
i < index
You're already defining index to be length -1
Just a little logic error.
Change your postDepartment to this and see if it helps:
Future<void> postDepartment() async {
final response = await http.get('url');
final jsonresponse = json.decode(response.body);
List<Department> departments = [];
for(var d in jsonresponse) {
Department department = Department(
fid: d["fid"].toString(),
);
departments.add(department);
}
int index = departments.length -1;
for(int i = 0; i<=index; i++) {
departmentdes[i] = departments[i].fid;
}
return departmentdes;
}
Also check if your departments is not null.