How to dynamically add parameters to firestore query and map to object - google-cloud-firestore

What is the recommended way to map the data to an object and return it as a promise/observable while being able to add dynamic/conditional parameters to the query.
In getCompanies2 I can dynamically add parameters to the query but I can't figure out how to map the data returned to my object and return it as a promise/observable.
In getCompanies everything works as I want it but I have to duplicate the code (as below) if I have dynamic query parameters to add.
Note: convertDocTimeStampsToDate just does what it says. I have excluded it to reduce the size of the code section.
getCompanies(searchText: string, useWhereActive: boolean): Observable<Company[]> {
if (useWhereActive) {
return this.db.collection('companies', ref => ref
.orderBy('name').startAt(searchText).endAt(searchText + '\uf8ff')
.where('active', '==', true)
)
.snapshotChanges()
.pipe(
map(snaps => convertSnaps<Company>(snaps)),
first()
);
} else {
return this.db.collection('companies', ref => ref
.orderBy('name').startAt(searchText).endAt(searchText + '\uf8ff')
)
.snapshotChanges()
.pipe(
map(snaps => convertSnaps<Company>(snaps)),
first()
);
}
}
​
getCompanies2(searchText: string, useWhereActive: boolean) {
let query = this.db.collection('companies').ref
.orderBy('name').startAt(searchText).endAt(searchText + '\uf8ff');
​
if (useWhereActive) {
query.where('active', '==', true);
}
​
query.get().then(querySnapshot => {
const results = this.convertDocuments<Company>(querySnapshot.docs);
console.log(results);
});
}
convertDocuments<T>(docs) {
return <T[]>docs.map(doc => {
return {
id: doc.id,
...doc.data()
};
});
}
export function convertSnaps<T>(snaps) {
return <T[]>snaps.map(snap => {
const data = convertDocTimeStampsToDate(snap.payload.doc.data());
return {
id: snap.payload.doc.id,
...data
};
});
}

I got it to work like below, I guess I am still getting my head around promises.
Any better solutions will be accepted though as I am still learning and don't know if this is the best method.
getCompanies2(searchText: string, useWhereActive: boolean) {
let query = this.db.collection('companies').ref
.orderBy('name').startAt(searchText).endAt(searchText + '\uf8ff');
if (useWhereActive) {
query.where('active', '==', true);
}
return query.get().then(querySnapshot => {
return this.convertDocuments<Company>(querySnapshot.docs);
});
}

Related

Redux toolkit createAsyncThunk using Parameter

I need your help.
We implemented createAsyncThunk using the Redux Toolkit.
However, as shown in the picture, I need a function to change ChannelId flexibly. Can't I use the parameter in createAsyncThunk?
How can I use it if I can?
Or is there any other way?
I am sorry that the quality of the question is low because I am Korean using a translator.
enter image description here
// ACTION
export const getYoutubeList_PlayList = createAsyncThunk(
"GET/YOUTUBE_PLAYLIST",
async (data, thunkAPI) => {
try {
const { data } = await axios.get<youtubeResponse>(
`https://www.googleapis.com/youtube/v3/playlists?key=${youTubeAcsses.apiKey}&channelId=${channelId}&part=snippet&maxResults=30`
)
return data
} catch (err: any) {
return thunkAPI.rejectWithValue({
errorMessage: '호출에 실패했습니다.'
})
}
}
);
// SLICE
const youtube_PlaylistSlice = createSlice({
name: "YOUTUBE_PLAYLIST",
initialState,
reducers: {},
// createAsyncThunk 호출 처리 = extraReducers
extraReducers(builder) {
builder
.addCase(getYoutubeList_PlayList.pending, (state, action) => {
state.loading = true;
})
.addCase(getYoutubeList_PlayList.fulfilled, (state, action: PayloadAction<youtubeResponse>) => {
state.loading = false;
state.data = action.payload;
})
.addCase(getYoutubeList_PlayList.rejected, (state, action: PayloadAction<any>) => {
state.error = action.payload;
});
},
});
You named both the incoming argument data as well as the result of your axios call. That will "shadow" the original data and you cannot access it any more. Give those two variables different names.
Here I called it arg, which allows you to access arg.channelId.
export const getYoutubeList_PlayList = createAsyncThunk(
"GET/YOUTUBE_PLAYLIST",
async (arg, thunkAPI) => {
try {
const { data } = await axios.get<youtubeResponse>(
`https://www.googleapis.com/youtube/v3/playlists?key=${youTubeAcsses.apiKey}&channelId=${arg.channelId}&part=snippet&maxResults=30`
)
return data
} catch (err: any) {
return thunkAPI.rejectWithValue({
errorMessage: '호출에 실패했습니다.'
})
}
}
);
You would now dispatch this as dispatch(getYoutubeList_PlayList({ channelId: 5 }))

Curious why we can't get at the args in a query, in the onSuccess?

So, I have some ancilliary behaviors in the onSuccess, like analytics and such. And I need to pass in to the tracking, not only the result of the query/mutation (mutation in this case), BUT also an arg I passed in. Seems I can only do it if I attach it to the return "data"?
export default function useProductToWishList () {
const queryClient = useQueryClient();
return useMutation(
async ({ product, email }) => {
const data = await product.addWishList({ product, email });
if (data.status === 500 || data.err) throw new Error(data.err);
return data;
},
{
onSuccess:(data) => {
const { product: response = {} } = data?.data ?? {};
queryClient.setQueryData(['products'], {...response });
analytics(response, email); // HERE. How can I get at email?
}
}
)
}
seems odd to do, when I don't need it for the response, but for a side effect. Any thoughts?
return { ...data, email }
for useMutation, the variables are passed as the second argument to onSuccess. This is documented in the api docs. So in your example, it's simply:
onSuccess: (data, { product, email }) =>

MongoDB query - pass the function to the Model.find()

I have issue with querying MongoDB (Mongoose) by passing the function as parameter in Model.find() -> like this Model.find(searchCondition). I hope that you can help me.
// Fetching patients from the database
exports.getPatients = (req, res, next) => {
const criterionSearchCategory = req.query.kriterijumPretrage;
const ageSearchCategory = req.query.kriterijumGodina;
const searchInputValue = req.query.pojamPretrage;
console.log({ [criterionSearchCategory]: { [ageSearchCategory]: Number([searchInputValue]) }});
// Patient search condition, based on selected option from select dropdown
function searchCondition() {
if (criterionSearchCategory == 'undefined') {
return {};
} else if (criterionSearchCategory == 'age') {
return { [criterionSearchCategory]: { [ageSearchCategory] : Number([searchInputValue]) }}
} else {
return { [criterionSearchCategory]: { $in: [ "/^" + searchInputValue + "/i" ]}}
}
}
...
const patientQuery = Patient.find(searchCondition);
getPatients(patientsPerPage: number, currentPage: number, criterionSearchCategory: string, searchInputValue: string, ageSearchCategory: any) {
const queryParams = `?pacijenataPoStranici=${patientsPerPage}&trenutnaStranica=${currentPage}&kriterijumPretrage=${criterionSearchCategory}&pojamPretrage=${searchInputValue}&kriterijumGodina=${ageSearchCategory}`;
this.http
.get<{ message: string, patients: any, maxPatients: number }>( BACKEND_URL + queryParams)
// Execute map on every data that makes it through Observable stream
.pipe(map((patientData) => {
I want to menton when I pass the query params manually, for example const patientQuery = Patient.find({ age: { '$gt': 30 } }); appropriate patients will be fetched correctly , but when I pass the function , like this const patientQuery = Patient.find(searchCondition); then does not work.
The first question, is it possible to pass the function as parameter like this?
Any suggestion will be appreciate. Thank you

Unable to return an array of json from Cloud Firestore via Cloud Functions (onCall) to Swift

I have a problem getting the result from a Cloud Function.
This is my Cloud Function:
exports.retrieveTrips = functions.https.onCall((data, context) => {
const uidNumber = context.auth.uid;
var arrayOfResults = new Array();
var idOfFoundDoc;
var query = admin.firestore().collection('Users').where('UID','==', uidNumber);
query.get().then(snapshot =>
{
snapshot.forEach(documentSnapshot =>
{
idOfFoundDoc = documentSnapshot.id;
});
var queryDoc = admin.firestore().collection('Users').doc(idOfFoundDoc).collection('Trips');
queryDoc.get().then(snapshot =>
{
snapshot.forEach(documentSnapshot =>
{
arrayOfResults.push(documentSnapshot.data());
});
console.log('ARRAY: ' , arrayOfResults);
return arrayOfResults;
})
.catch (err =>
{
console.log ('Error adding document: ', err);
});
})
.catch (err => {
//response.send('Error getting documents', err);
console.log ('Error getting documents', err);
});
And this is the code that I have in my application.
#IBAction func RetrieveTripsButton(_ sender: Any)
{
self.functions.httpsCallable("retrieveTrips").call() {(result, error) in
if let error = error as NSError? {
if error.domain == FunctionsErrorDomain
{
let message = error.localizedDescription
print ("Message: " + message)
}
return
}
print ("Result: -> \(type(of: result))")
print("Result.data type: \(type(of: result?.data))");
print ("Result.data -> \(result?.data)")
}
}
And this is the printed result.
Result: -> Optional<FIRHTTPSCallableResult>
Result.data type: Optional<Any>
Result.data -> Optional(<null>)
The console log is able to print arrayOfResults correctly. Furthermore, when I change this functions to onRequest and feed it the relevant information, the res.status(200).send(arrayOfResults) is able to display the array of JSON in the page.
If I placed the return arrayOfResults; outside of the .then function, I would get a result along with an empty array. My issue is similar to this problem here but I'm unable to receive even that when I return { text: "some_data" }; .
Any help would be great, thank you!
You have to chain the different promises and return the result of the promises chain, as follows.
Note that it is actually what the OP explains in his answer to the SO post you mention "The issue was that I forgot to return the actual promise from the cloud function".
exports.retrieveTrips = functions.https.onCall((data, context) => {
const uidNumber = context.auth.uid;
const arrayOfResults = new Array();
let idOfFoundDoc;
const query = admin.firestore().collection('Users').where('UID','==', uidNumber);
return query.get().then(snapshot => { // here add return
snapshot.forEach(documentSnapshot =>
{
idOfFoundDoc = documentSnapshot.id;
});
const queryDoc = admin.firestore().collection('Users').doc(idOfFoundDoc).collection('Trips');
return queryDoc.get(); // here add return and chain with then()
})
.then(snapshot => {
snapshot.forEach(documentSnapshot => {
arrayOfResults.push(documentSnapshot.data());
});
console.log('ARRAY: ' , arrayOfResults);
return { arrayOfResults : arrayOfResults }; //return an object
})
.catch (err => {
console.log ('Error getting documents', err);
//Here you may return an error as per the documentation https://firebase.google.com/docs/functions/callable#handle_errors, i.e. by throwing an instance of functions.https.HttpsError
});
});
I would also suggest that you look at these two videos from the Firebase team, about Cloud Functions and promises: https://www.youtube.com/watch?v=7IkUgCLr5oA and https://www.youtube.com/watch?v=652XeeKNHSk.

Return data in json after subscribe

I am using Angular 5 and want to return data from function getDionaeaResults in json format after subscribing to service
getDionaeaResults(sql) : any {
this.dionaeaService.getDionaeaConnectionLogs(sql).subscribe(res => {
this.data = res;
}),
(error: any) => {
console.log(error);
});
return this.data;
}
After calling this function, this.totalAttacks prints undefined.
getTotalAttack() {
this.totalAttacks = this.getDionaeaResults("some query")
console.log(this.totalAttacks,'attacks')
}
Would suggest using the Obseravable .map() function.
getDionaeaResults(sql) : Observable<any> {
return this.dionaeaService
.getDionaeaConnectionLogs(sql)
.map(res => res);
}
getTotalAttack(sql){
this.getDionaeaResults("some query")
.subscribe(
res => { this.totalAttacks = res; },
err => { console.log(err); }
);
}
this.getDionaeaResults is returning undefined because the service you're calling is asynchronous you have to wait for the subscribe callback. as Observables are asynchronous calls
this.data=res
might execute after the return statement. You can perhaps call that dionaeaService directly inside getTotalAttack() function, like this:
getTotalAttack(sql){
this.dionaeaService.getDionaeaConnectionLogs(sql).subscribe(res => {
this.totalAttacks = res;
}),
(error: any) => {
console.log(error);
});
}