FIresotre query entire db and limit results - database

I know I can query the db like this:
final invoicesDocsRef = await invoicesRef
.orderBy('invoiceNo', descending: true)
.limit(30)
.get()
.then((value) => value.docs.map((doc) => Invoice.fromSnapshot(doc)));
But I want it paginated cause I dont want to call 200 rows for every user, so this was my next approach:
Future<Iterable<Invoice>> getInvoices(
{required int limit, String? lastDocID}) async {
final invoicesDocsRef = invoicesRef.orderBy('invoiceNo', descending: true);
if (lastDocID == null) {
return await invoicesDocsRef.get().then(
(value) => value.docs.map((doc) => Invoice.fromSnapshot(doc)),
);
} else {
final lastDoc = await invoicesRef.doc(lastDocID).get();
return await invoicesDocsRef.startAfterDocument(lastDoc).get().then(
(value) => value.docs.map((doc) => Invoice.fromSnapshot(doc)),
);
}
}
How this is not ordered at all. I thought single indexs were automatic?

Related

React Native - Not able to add and delete data from array correctly

I am creating an App in which I have two screens -- One is Feed and Bookmark. Each Feed has bookmark icon from which we can add and delete bookmark. If I add bookmark from feed then It will be added to list and if I delete that then list will be updated with existing once but the issue is that sometime data added double at the time of addition and sometimes at time of deletion array did not get the index of selected item and stays there and sometimes my list got disturbed with showing half items.
Code to click bookmark button on feed
const selectBookmark = () => {
item.bookmarked = !item.bookmarked;
setBookMarkSelected(!item.bookmarked);
setBookmarkClicked(true);
setBookmarkLoader(true);
};
Call API each time at updating bookmark
const bookmarkResponse = await addDeleteBookmark(
email,
userId,
item.cardId,
userSelectedChannel,
token,
item.bookmarked,
item.createdAt,
item.cardType,
)
.then(res => {
setBookmarkLoader(false);
if (res !== 'error') {
console.log('not in else');
updatebookMark();
} else {
console.log(' in else');
item.bookmarked = !item.bookmarked;
}
})
.catch(function (error) {
setBookmarkLoader(false);
});
};
**when I get response from API I call updateBookmark function too update local database **
const updatebookMark = () => {
// code to update feed Array locally
let newArray = [...feedArray];
let id = item.cardId;
const index = newArray.findIndex(object => {
return object.cardId === id;
});
if (index !== -1) {
newArray[index].bookmarked = item.bookmarked;
addFeedsToLocalDB(newArray);
}
// code to update bookmark Array locally
let bookmarks = [...bookmarkArray];
// bookmark added then add new bookmark
if (item.bookmarked) {
const index = bookmarkArray.findIndex(object => {
return object.cardId === id;
});
if (index === -1) {
bookmarks.push(item);
addBookMarksTOLocalDB(bookmarks);
}
} else {
// if deletion then delete from bookmark
const indexBookmark = bookmarks.findIndex(object => {
return object.cardId === id;
});
console.log('bookmark card indexBookmark image', indexBookmark);
bookmarks.splice(indexBookmark, 1);
// console.log('bookmarked after splicing image', bookmarks);
addBookMarksTOLocalDB(bookmarks);
// setBookmarksArray(bookmarks);
}
let homeArray = [...homeCards];
const indexHome = newArray.findIndex(object => {
return object.cardId === id;
});
if (indexHome !== -1) {
homeArray[indexHome].bookmarked = item.bookmarked;
addHomeToLocalDB(homeArray);
}
};
but the issue is that this addition and deletion bookmark is causing issue and I am not able to get that.

React Firestore - Retrieve all documents in an Array of document IDs

I am trying to retrieve an all documents of which document ID is within the given array. I have no issues retrieving the list of documentIDs and storing in the array. However, I am not sure on how to retrieve the documents of which ids are in the array. Please help Thanks!
portfolio doc:
docId{
docId: docId,
...
}
const getData = (bookmarkIds) => {
console.log("this is " )
console.log(bookmarkIds)
console.log("entering" )
const portfolioQuery = database.portfolioRef.where("docId", 'in', bookmarkIds);
portfolioQuery.onSnapshot((snapshot) => {
console.log(snapshot.docs)
if (snapshot.docs.length !== 0) {
var id = 1;
const tempItem = [];
snapshot.docs.forEach((doc) => {
tempItem.push({
id: id,
intervieweeName: doc.data().intervieweeName,
intervieweeEmail: doc.data().intervieweeEmail,
projectTitle: doc.data().projectTitle,
portfolioTitle: doc.data().portfolioTitle,
dateCreated: doc.data().dateCreated,
fbId: doc.id
})
id++;
})
setPortfolioData(tempItem)
}
})
}
useEffect(() => {
const getUserData = database.usersRef.where("email", "==", currentUser.email);
const bookmarkArray = [];
const unsub = getUserData.onSnapshot((snapshot) => {
snapshot.docs.forEach((doc) =>{
bookmarkArray.push(doc.data().bookmarkIds);
})
console.log(bookmarkArray);
getData(bookmarkArray)
})
return unsub;
}, [currentUser.email]);
Based on my current code above, I am receiving the following (Not getting any error just a blank return):
I realised I made a double array by doing
bookmarkArray.push(doc.data().bookmarkIds);
solved by doing
bookmarkArray = doc.data().bookmarkIds;

Change table data set based on Google Maps API results

I am trying to implement a filter which allows me to filter users based on the distance between a location and their address. Data is provided to the table using useMemo, basically like this:
const data = useMemo(
() =>
contacts.filter(contact => {
var shouldReturn = true;
clientFilter.map((filter, i) => {
if (filter.condition === 'max_10km') {
const originAddress = `${contact['street']} ${contact['number']}, ${contact['zip']} ${contact['city']}, ${contact['country']}`;
calculateDistance(originAddress, filter.value, function(distance) {
console.log('distance is calculated: ', distance);
if (distance > 10000) {
console.log('distance is > for', contact['name']);
shouldReturn = false;
}
});
}
});
return shouldReturn;
}),
[clientFilter]
);
This works fine, in console the results return as I expect them to be. However, my table doesn't update. I suspect it is because the result of the API calls are async, and thus the table is re-rendered before the results are in.
I have tried updating the data using useEffect, but this brings me in a loop which constant re-renders, and thus exceeding the maximum (Maximum update depth exceeded.).
How should I go about this? Should I try async functions? If so, how can I wait to update my data until all promises are resolved?
EDIT 14 NOV
So, I have been looking further into this today. I have managed to switch the filtering to useEffect() instead of useMemo(), so currently, it looks like this:
const [filteredContacts, setFilteredContacts] = useState(contacts);
const data = useMemo(() => filteredContacts, [filteredContacts]);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
console.log('Log: In useEffect()');
if (!isLoading) {
setIsLoading(true);
(async () => {
console.log('Log: State has changed, filtering will start');
filterContacts().then(() => setIsLoading(false));
})();
}
}, [clientFilter]);
async function filterContacts() {
setFilteredContacts(
contacts.filter(contact => {
var shouldReturn = true;
clientFilter.map((filter, i) => {
if (
filter.condition === 'equal' &&
contact[filter.field] != filter.value &&
shouldReturn
) {
shouldReturn = false;
}
if (filter.condition === 'max_10km' && shouldReturn) {
const originAddress = `${contact['street']} ${contact['number']}, ${contact['zip']} ${contact['city']}, ${contact['country']}`;
calculateDistance(originAddress, filter.value, async function(
distance
) {
console.log('Log: Distance is calculated: ', distance);
if (distance > 10000) {
console.log(
'Log: Distance is further away for',
contact['title']
);
shouldReturn = false;
}
});
}
});
console.log('Log: About to return shouldReturn value');
return shouldReturn;
})
);
}
Now, this works for my other filters, but the async distance calculation still runs after the return of shouldReturn has been done. So my logs look like this (I have 16 contacts/users currently):
Log: In useEffect()
Log: State has changed, filtering will start
(16) Log: About to return shouldReturn value
Log: Distance is calculated: 1324
Log: Distance is calculated: 4326
...
So basically, it still ignores the async state of my function calculateDistance. Any ideas?
EDIT 15/11
Might be useful as well, this is my calculateDistance() function:
function calculateDistance(origin, destination, callback) {
const google = window.google;
const directionsService = new google.maps.DirectionsService();
directionsService.route(
{
origin: origin,
destination: destination,
travelMode: google.maps.TravelMode.DRIVING
},
(result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
callback(result.routes[0].legs[0].distance.value);
} else {
console.error('Google Maps API error: ', status);
callback(null);
}
}
);
}
EDIT 18/11
After #Secret's suggestion, I changed the code to:
const shouldRemoveContact = await(filters, contact) = () => {
for (const filter of filters) {
if (
filter.condition === 'equal' &&
contact[filter.field] != filter.value
) {
return true
}
if (filter.condition === 'max_10km') {
const originAddress = `${contact['street']} ${contact['number']}, ${contact['zip']} ${contact['city']}, ${contact['country']}`
// please update calculateDistance to return a promise
const distance = await calculateDistance(originAddress, filter.value)
return distance > 10000
}
return false
}
}
async function filterContacts (filters, contact) {
// for every contact, run them to shouldRemoveContact
// since shouldRemoveContact is async, we use Promise.all
// to wait for the array of removeables to be ready
const shouldRemove = await Promise.all(
contact.map(c => shouldRemoveContact(filters, c))
)
// use shouldRemove to check if contact should be removed
// and voila!
return contacts.filter((c, i) => !shouldRemove[i])
}
This results in:
Syntax error: Unexpected reserved word 'await'.
on the line:
const shouldRemoveContact = await(filters, contact) = () => {
You are correct in your mistake - your calculateDistance is async and therefore you can't filter your data properly. Essentially, your code can be boiled down to:
setFilteredContacts(
contacts.filter(contact => {
var shouldReturn = true;
// THIS IS WHERE CALCULATE IS
// but it doesn't do anything because it is async
// so effectively, it does nothing like a comment
console.log('Log: About to return shouldReturn value');
return shouldReturn;
})
);
As you can see, your contacts will never be filtered out as shouldReturn will always return true - it never changes in the middle of that function!
What you can do is calculate the list of filterables beforehand, and then use that to run your filter. Something like this (psuedocode):
// given a contact and a list of filters
// asynchronously return if it should or should not be filtered
const shouldRemoveContact = async () => {}
// THEN, in the filterContacts part:
// let's generate an array of calls to shouldRemoveContact
// i.e. [true, true, false, true], where `true` means it should be removed:
// note that we use await Promise.all here, waiting for all the data to be finished.
const shouldRemove = await Promise.all(
contact.map(c => shouldRemoveContact(filters, c))
)
// we then simply shouldRemove to filter it all out
return contacts.filter((c, i) => !shouldRemove[i])
All together:
// given a list of filters and one contact
// asynchronously returns true or false depending on wheter or not
// the contact should be removed
const shouldRemoveContact = async (filters, contact) => {
for (const filter of filters) {
if (
filter.condition === 'equal' &&
contact[filter.field] != filter.value
) {
return true
}
if (filter.condition === 'max_10km') {
const originAddress = `${contact['street']} ${contact['number']}, ${contact['zip']} ${contact['city']}, ${contact['country']}`
// please update calculateDistance to return a promise
const distance = await calculateDistance(originAddress, filter.value)
return distance > 10000
, async function(
distance
) {
console.log('Log: Distance is calculated: ', distance);
if (distance > 10000) {
console.log(
'Log: Distance is further away for',
contact['title']
);
shouldReturn = false;
}
});
}
}
return false
}
async function filterContacts (filters, contact) {
// for every contact, run them to shouldRemoveContact
// since shouldRemoveContact is async, we use Promise.all
// to wait for the array of removeables to be ready
const shouldRemove = await Promise.all(
contact.map(c => shouldRemoveContact(filters, c))
)
// use shouldRemove to check if contact should be removed
// and voila!
return contacts.filter((c, i) => !shouldRemove[i])
}

How to Verify if each element the Array contains the search string in Typescript/protractor

How do i Verify if each element of the Array contains the search string in Typescript/ Protractor??
All the console statements returned false as they were looking for complete text rather than a search string. Please suggest a solution.
arr = [ 'Citibank, N.A.', 'Citi China Companies', 'Citibank Ireland' ]
search string = 'citi'
Then('I enter search text where the highlighted search results will include a Client Company Name {string}, {string}', async (searchText, companyName) => {
await acctmgrclientselection.deleteSearchText().then(async () => {
await acctmgrclientselection.getSelectClientSearchInputEl().sendKeys(searchText).then(async () => {
await acctmgrclientselection.getSelectClientSearchInputEl().sendKeys(protractor.Key.ENTER).then(async () => {
await dashboardFilter.getEmployeeListGrid().count().then( async ( CountVal ) => {
if(CountVal >1)
{
var strArr: Array<string> = [];
await acctmgrclientselection.getClientTblCompanyName().getText().then(async (text) => {
await strArr.push(text)
//strArr.forEach(function(value){
var sortable = [];
strArr.forEach(value => {
sortable.push([value]);
let sorted_array: Array<string> = sortable.map(arr => arr[0])
let result = sorted_array.every(element => element.includes(searchText))
console.log(result)
});
});
}
else
{
//clear criteria
console.log('clear criteria');
await element(by.cssContainingText('mat-card.empty-results.mat-card p','0 results matching your criteria')).isDisplayed().then(async()=>{
await element(by.cssContainingText('mat-card.empty-results.mat-card a','Clear Criteria')).isDisplayed();
});
}
});
});
});
});
});
You need to check for the regex with search string.
const pattern = new RegExp("Citi");
const result = sorted_array.every(element => pattern.test(element));
console.log(result);

Promise chaining not returning expected result

I have to send an e-mail for some users. I have a promise that returns the users Ids and another that returns the users e-mails (based on user id).
I have all of this chained but my parent function get an empty array.
I tried promises and async await but i have little experience with this and i dont know where im missing.
private async _getUserFromContatos(_subtipoEmergenciaID: number):Promise<string[]>{
const _arrTo: string[] = [];
sp.web.lists.getByTitle("Contato").items.get().then((items:any[]) => {
let _contatos = items.filter((i) => i.SubtipoEmergenciaId == _subtipoEmergenciaID);
_contatos.map(c => {
sp.web.getUserById(c.FuncionarioId).get().then(_userInfo => {
_arrTo.push(_userInfo.Email);
});
});
});
return _arrTo;
}
private _sendMail(){
this._getUserFromContatos(this.state.selectedSubtipoEmergencia).then(
_arrTo => {
console.log(_arrTo); //Returns the array as if its filled ok
console.log(_arrTo.length); //Returns 0 (empty array)
});
}
The first console.log at the end return the array filled but the second one returns 0.
I cant access the array items.
Your problem is that the array is not guaranteed to be filled when the first function returns. That is because you never await the result of the getUserById call.
Here are two possible solutions (one using await / one without await)
function _getUserFromContatos(_subtipoEmergenciaID: number): Promise<string[]> {
return sp.web.lists
.getByTitle("Contato")
.items
.get()
.then((items: any[]) => {
let _contatos = items.filter(i => i.SubtipoEmergenciaId == _subtipoEmergenciaID);
return Promise.all(
_contatos.map(c => {
sp.web
.getUserById(c.FuncionarioId)
.get()
.then(_userInfo => _userInfo.Email);
})
);
});
}
async function _getUserFromContatos(_subtipoEmergenciaID: number): Promise<string[]> {
var items = await sp.web.lists.getByTitle("Contato").items.get(); // await the list
let _contatos = items.filter(i => i.SubtipoEmergenciaId == _subtipoEmergenciaID);
var _arrTo: string[] = [];
for (var c of _contatos) {
var userInfo = await sp.web.getUserById(c.FuncionarioId).get(); // query every user and await that result
_arrTo.push(userInfo.Email);
}
return _arrTo;
}
function _sendMail() {
this._getUserFromContatos(this.state.selectedSubtipoEmergencia).then(
_arrTo => {
console.log(_arrTo); //Returns the array as if its filled ok
console.log(_arrTo.length); //Returns 0 (empty array)
}
);
}

Resources