How to join two firebase queries in the same Stream? - flutter

I am using Streambuilder to display a list of tasks. For this I want to join two queries in Firebase: The tasks that have been completed in the last 7 days (alList1) and all the pending tasks (alList2)
The way I do the query is as follows:
Stream<List<AlarmaModel>> cargarAlarmasStream(int fechaMinima) {
final List<AlarmaModel> alarmaListVacia = [];
Query resp = db.child('alarmas').orderByChild('fechaCompleta').startAt(fechaMinima);
final streamPublish1 = resp.onValue.map((event) {
if(event.snapshot.value != null) {
final alList1 = Map<String,dynamic>.from(event.snapshot.value).entries.map((e) {
AlarmaModel alTemp = new AlarmaModel();
alTemp = AlarmaModel.fromJson(Map<String,dynamic>.from(e.value));
alTemp.idAlarma = e.key;
return alTemp;
}).toList();
return alList1;
} else return alarmaListVacia;
});
Query resp2 = db.child('alarmas').orderByChild('completa').equalTo(false);
final streamPublish2 = resp2.onValue.map((event) {
if(event.snapshot.value != null) {
final alList2 = Map<String,dynamic>.from(event.snapshot.value).entries.map((e) {
AlarmaModel alTemp2 = new AlarmaModel();
alTemp2 = AlarmaModel.fromJson(Map<String,dynamic>.from(e.value));
alTemp2.idAlarma = e.key;
return alTemp2;
}).toList();
return alList2;
} else return alarmaListVacia;
});
return streamPublish1;
}
However, at the time of displaying it on the screen with the StreamBuilder, it is only showing me alList1, which is the one contained in streamPublish1. How can I do in the return to output the result of StreamPublish1 added to StreamPublish2?
UPDATE
I was testing the solution in Stackoverflow 36571924 as follows:
Stream<List<AlarmaModel>> pubStream = StreamGroup.merge([streamPublish1, streamPublish2]);
return pubStream
It was close, but it didn't work. It only retrieves the last list (alList2) and not the combination of both.

Related

Flutter Firestore Pagination (add/remove items)

I'm struggling to get this done, after two days I decided to ask you guys for help.
I'm using Mobx as state management, the issue is related to adding/removing an item to/from the list, e.g. if I retrieve two queries of 5 items each, according to the limit and then remove an item from the first query the first item from the second query is duplicate and if I add a new item, the first item from the second query is hidden. I have also set a scrollListener to the ListView.builder to get the bottom of the list and call for more items.
Thanks in advance,
#override
Stream<QuerySnapshot> teste(DocumentSnapshot lastDoc) {
if (lastDoc == null) {
return firestore.collection('teste')
.orderBy('name')
.limit(5)
.snapshots();
} else {
return firestore.collection('teste')
.orderBy('name')
.limit(5)
.startAfterDocument(lastDoc)
.snapshots();
}
}
#observable
ObservableList<List<TesteModel>> allPagedResults = ObservableList<List<TesteModel>>();
#observable
ObservableList<TesteModel> listTeste = ObservableList<TesteModel>();
#observable
DocumentSnapshot lastDoc;
#observable
bool hasMoreItem;
#action
void teste() {
var _currentRequestIndex = allPagedResults.length;
primaryRepository.teste(lastDoc).listen((query) {
if (query.docs.isNotEmpty) {
var _query = query.docs.map((doc) => TesteModel.fromFirestore(doc))
.toList();
var _pageExists = _currentRequestIndex < allPagedResults.length;
if (_pageExists) allPagedResults[_currentRequestIndex] = _query;
else allPagedResults.add(_query);
listTeste = allPagedResults.fold<List<TesteModel>>(<TesteModel>[],
(initialValue, pageItems) => initialValue..addAll(pageItems)).asObservable();
if (_currentRequestIndex == allPagedResults.length - 1) lastDoc = query.docs.last;
hasMoreItem = _query.length == 5;
}
});
}
Any luck with this issue?
For adding new items and having multiple pages you could do something like this:
if (allPagedResults.contains(allPagedResults[0].last) == false) {
allPagedResults.last.add(allPagedResults[0].last);
allPagedResults.last.remove(allPagedResults[0].first);
}

Filter data from Stream in Flutter/Dart

I have a Stream which contains List of objects. i.e. List<ProductSubCategoryListModel> and
ProductSubCategoryListModel contains isSelected boolean and CategoryId int.
I want to filter Stream which only has isSelected == true and get list type of List<ProductSubCategoryListModel> in return. After this need to get all the CategoryId from `List.
Here's my code.
Future<void> fetchProducts(
TrendingCategoriesModel categoryModel, int pageIndex) async {
_localCategoryModel = categoryModel;
//Stream I want to manipulate to get List<ProductSubCategoryListModel>
var stream = _categoryListController.stream;
var request = ProductListRequestModel();
var searchParameterModel = SearchParams();
searchParameterModel.SearchText = '';
searchParameterModel.CategoryIDs = //List<int> of CategoryIds filtered from List<ProductSubCategoryListModel>;
searchParameterModel.SubCategoryIDs = null;
searchParameterModel.AttributeIDs = null;
searchParameterModel.SubAttributeIDs = null;
searchParameterModel.StartPrice = 0;
searchParameterModel.EndPrice = null;
searchParameterModel.EndPrice = null;
request.PageIndex = pageIndex;
request.PageSize = 10;
request.SortExpression = '';
request.SortType = '';
request.IsGetFilterData = true;
request.searchParams = searchParameterModel;
if (!_productListController.isClosed) _productListController.sink.add(null);
var response =
await _networkHandler.callAPI(NetworkUrl.PRODUCT_LIST, request);
if (response != null) {
}
}
You can use map method https://api.flutter.dev/flutter/dart-async/Stream/map.html. it returns new stream with mapped data. Map will be applied to each event in a stream.
Additionally you can apply distinct method https://api.flutter.dev/flutter/dart-async/Stream/distinct.html just after map, if you need to ensure that only unique values will be emitted to the subscriber

clean future builder for new data

i'm using a builder for a search page in my application, basically i get data from a json file,
my issue is that if i try to search for a new word, the old result will still be shown and the new one are going to be shown under them.
here is how i get data from my website:
Future<List<Note>> fetchNotes() async {
var url = 'https://sample.com/';
var response = await http.get(url + _controller.text.trim());
var notes = List<Note>();
if (response.statusCode == 200) {
var notesJson = json.decode(response.body);
for (var noteJson in notesJson) {
notes.add(Note.fromJson(noteJson));
}
} else {
ercode = 1;
}
return notes;
}
fetchNotes().then((value) {
setState(() {
_notes.addAll(value);
});
});
if (_notes[0] == null) {
ercode = 2;
}
}
and i display data like this:
here is full example for showing that data
I think you should use "clear()".
fetchNotes().then((value) {
setState(() {
_notes.clear();
_notes.addAll(value);
});
});

Trying to Move Cards

I am trying to move a card I have found using the search function from one list to another on the same board.
I have tried to set up a new list using the a new factory.list and searching for the list using FirstorDefault LINQ Query but keep getting an Error.
Below is some code I have tried to move my cards.
string query = jnum;
var search = factory.Search(query, 1, SearchModelType.Cards, new IQueryable[] { board });
await search.Refresh();
var CardList = search.Cards.ToList();
foreach (var card in CardList)
{
string tName = card.Name.Substring(0, 6);
if (tName == jnum.Trim())
{
cardid = card.Id;
}
}
var FoundCard = factory.Card(cardid);
string FoundListid = FoundCard.List.Id;
var fromlist = factory.List(FoundListid);
if (Item != null)
{
if (mail.Body.ToUpper().Contains("Approved for Print".ToUpper()))
{
//var ToList = factory.List("5db19603e4428377d77963b4");
var ToList = board.Lists.FirstOrDefault(l => l.Name == "Signed Off");
FoundCard.List = ToList;
// from on proof
//MessageBox.Show("Approved for Print");
}
else if (mail.Body.ToUpper().Contains("Awaiting Review".ToUpper()))
{
//var ToList = factory.List("5db19603e4428377d77963b3");
var ToList = board.Lists.FirstOrDefault(l => l.Name == "On Proof");
FoundCard.List = ToList;
// from in progress or to start
// MessageBox.Show("Awaiting Review");
}
else if (mail.Body.ToUpper().Contains("Amends".ToUpper()))
{
var ToList = factory.List("5dc9442eb245e60a39b3d4a7");
FoundCard.List = ToList;
// from on proof
//MessageBox.Show("Amends");
}
else
{
// non job mail
}
}
I keep getting a is not a valid value Error.
Thanks for help

How to improve speed of query for Firestore + Flutter?

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;
}
}