I have a Firebase Realtime database query written using Firebase version 8 that I'm looking to migrate over to the v9 SDK.
export const getSingleDataWithQuery = async ({ key, email, criteria }) => {
if (!criteria) return;
const snapshot = await realTimeDb
.ref()
.child(key)
.orderByChild(query)
.equalTo(criteria)
.get();
const val = snapshot.val();
if (val) {
const keys = Object.keys(val);
return val[keys[0]];
}
return null;
};
In this example:
key would be the 'users' collection
the email field is looking for users by their email
and the criteria is the user's actual email (jane.doe#gmail.com)
Using Firebase's Read data once and Sorting data documentation I managed to narrow it down to perhaps being this, but I'm not sure if it's correct:
export const getSingleDataWithQuery = async ({ key, query, criteria }) => {
if (!criteria) return;
const dbRef = query(ref(realTimeDb, key), orderByChild(email), equalTo(criteria));
get(dbRef).then(snapshot => {
if (snapshot.exists()) {
const val = snapshot.val();
if (val) {
const keys = Object.keys(val);
return val[keys[0]];
}
}
});
return null;
};
Aside from the fact that you may have swapped query and email in the fragments, the only difference is in the way you handle the asynchronous database call and that likely explains the difference. Since you use then in the second snippet, the function doesn't actually return a promise and so calling it with await it in the caller won't have any effect.
To make those the same again, use await in the second snippet too, instead of then:
export const getSingleDataWithQuery = async ({ key, query, criteria }) => {
if (!criteria) return;
const dbRef = query(ref(realTimeDb, key), orderByChild(email), equalTo(criteria));
const snapshot = await get(dbRef); // 👈
if (snapshot.exists()) {
const val = snapshot.val();
if (val) {
const keys = Object.keys(val);
return val[keys[0]];
}
}
return null;
};
Related
I'm trying to get all documents in all sub-collection with Firebase V9, so I used collectionGroup to retrieve the data.
Here is my Firestore architecture :
bots (collection)
| id (doc ID)
| order_history (sub collection)
| id (doc ID)
createdBy: uid (user uid)
And this is how I try to get documents :
const [orders, setOrders] = useState([]);
const { user } = useAuth();
const getOrders = async () => {
const orders = await getDocs(query(collectionGroup(db, 'order_history'), where('createdBy', '==', user.uid)));
setOrders(orders.docs.map((doc) => ({
...doc.data()
})))
}
useEffect(() => {
getOrders();
console.log('orders ', orders); // return []
}, []);
This code returns an empty array.
Did I do something wrong?
I think your getOrders function is asynchronus function.
If you want debug log I think you should waiting for getOrders completed then orders had been updated.
Ex:
useEffect(() => {
console.log('orders ', orders);
}, [orders]);
Your getOrders anonymous method execution requires an explicit return statement if there's more than 1 statement. Implicit returns work when only a single statement exists (and after some testing, return await X doesn't appear to work either).
const getOrders = async () => {
const orders = await getDocs(...);
setOrders(orders.docs.map(...))
}
Needs to be
const getOrders = async () => {
const orders = await getDocs(...);
return setOrders(orders.docs.map(...))
}
I have two functions. One that is used to to insert a phone number (TEL) into Moralis _User collection on the Moralis Database and another function which is used to query data from it.
This is the one I use to insert data. It works perfectly.
const updateTEL = async() => {
const User = Moralis.Object.extend('_User');
const query = new Moralis.Query(User);
query.equalTo("ethAddress", account);
query.exists("TEL");
const object = await query.first();
object.set("TEL", phoneNumber.phone);
object.save();
console.log("phone number updated successfully" , object);
return monster;
}
Now I am having a problem with this function below.
const queryTEL = async() => {
if(isInitialized) {
const User = Moralis.Object.extend('_User');
const query = new Moralis.Query(User);
query.equalTo("ethAddress", account);
const object = await query.first();
if (object) { setExistingTEL(object.TEL);}
return object;
}
}
const basicQuery = async () => {
const results = await queryTEL();
console.log (" results ", results);
};
I don't know why but it returns the result of 'results' as 'undefined'.
Here is how my useEffect looks like.
useEffect(() => {
setVerificationCode(Math.floor(Math.random()*10000));
basicQuery();
}, []);
Am I doing something wrong?
My index.js file:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const Firestore = require('#google-cloud/firestore');
const firestore = new Firestore();
admin.initializeApp();
const db = admin.firestore();
exports.getName = functions.https.onCall((data, context) => {
var docRef = db.collection("dogs").doc("{data.id}");
var getDoc = docRef.get().then(doc => {
return doc.get("name");
})
});
Code in the flutter project:
HttpsCallable callable = FirebaseFunctions.instance.httpsCallable("getName");
var temp = await callable({"id": "11"});
print(temp.data);
The program prints out null, even though document in collection "dogs" with id "11" with a field name exists. I'm trying to get specific data from firestore and return it.
Console doesn't show any errors and if I'd return anything else it would print out normally.
Couldn't find any documentations about getting data from firestore to cloud function other than ones that use triggers like: onWrite.
Have you tried to make the cloud function async?
exports.getName = functions.https.onCall(async (data, context) => {
var doc = await db.collection("dogs").doc("{data.id}").get();
return doc.data().name;
});
andi2.2's answer is correct, but let me explain why it doesn't work with your initial code using then().
By doing:
exports.getName = functions.https.onCall((data, context) => {
var docRef = db.collection("dogs").doc("{data.id}");
var getDoc = docRef.get().then(doc => {
return doc.get("name");
})
});
You actually don't return doc.get("name"); in your Callable Cloud Function. The then() method does return Promise.resolve(doc.get("name")), as explained in the then() doc, but you don't return the Promise chain.
The following will work:
exports.getName = functions.https.onCall((data, context) => {
var docRef = db.collection("dogs").doc("{data.id}");
return docRef.get().then(doc => {
return doc.get("name");
})
});
BTW, are you sure that db.collection("dogs").doc("{data.id}"); is correct? Shouldn't it be db.collection("dogs").doc(data.id);?
Here is how my database is structured:
challenges table &
users table
Here is the error I'm getting: error image
I want to use the "created_by" field which is also a document id for the users table, where I want to retrieve both the display name and the photo URL.
I'm not all the way sure how promises work and I have a feeling that is why I'm struggling, but the code I have thus far is below:
Data Retrieval:
UsersDao.getUserData(ChallengesDao.getChallenges().then(result => {return result['author'][0]})).then(result => {console.log(result)})
Challenges DAO:
export default class ChallengesDao {
static async getChallenges() {
const db = require('firebase').firestore();
// const challenges = db.collection('challenges').limit(number_of_challenges)
// challenges.get().then(())
const snapshot = await db.collection('challenges').get()
const names = snapshot.docs.map(doc => doc.data().name)
const createdBy = snapshot.docs.map(doc => doc.data().created_by)
const highScores = snapshot.docs.map(doc => doc.data().high_score.score)
return {challengeName: names, author: createdBy, score: highScores}
}
Users DAO:
const db = require('firebase').firestore();
export default class UsersDao {
static async getUserData(uid: string) {
let userData = {};
try {
const doc = await db
.collection('users')
.doc(uid)
.get();
if (doc.exists) {
userData = doc.data();
} else {
console.log('User document not found!');
}
} catch (err) {}
return userData;
}
}
You're getting close. All that's left to do is to call getUserData for each UID you get back from getChallenges.
Combining these two would look something like this:
let challenges = await getChallenges();
let users = await Promise.all(challenges.author.map((uid) => getUserData(uid));
console.log(challenges.challengeName, users);
The new thing here is Promise.all(), which combines a number of asynchronous calls and returns a promise that completes when all of them complete.
Your code looks a bit odd to me at first, because of the way you return the data from getChallenges. Instead of returning three arrays with simple values, I'd recommend returning a single array where each object has three values:
static async getChallenges() {
const db = require('firebase').firestore();
const snapshot = await db.collection('challenges').get();
const challenges = snapshot.docs.map(doc => { name: doc.data().name, author: doc.data().created_by, score: doc.data().high_score.score });
return challenges;
}
If you then want to add the user name to each object in this array, in addition to the UID that's already there, you could do:
let challenges = await getChallenges();
await Promise.all(challenges.forEach(async(challenge) => {
challenge.user = await getUserData(challenge.author);
});
console.log(challenges);
I attempted to use an 'OR' || statement in the equalTo() method, but this does not seem to work. Do I have to make a separate call for each value or is there a way to make conditional queries in firebase?
export const startSetContent = () => {
return (dispatch, getState) => {
const ref = database.ref("content");
return ref
.orderByChild("category")
.equalTo('one-act-play' || 'ten-min-play' || 'full-length-play')
.once('value')
.then(snapshot => {
const content = [];
snapshot.forEach(childSnapshot => {
content.push({
id: childSnapshot.key,
...childSnapshot.val()
});
});
dispatch(setContent(content));
});
};
};
What you typed there was a logical OR that JavaScript the programming language interprets. The result of the expression:
'one-act-play' || 'ten-min-play' || 'full-length-play'
is going to be simply:
'one-act-play'
So, Firebase Realtime Database will not see what you're trying to tell it.
Firebase Realtime Database has no concept of a kind of query that has ORs in it. You will have to query each type of thing separately and merge them together in your code as needed.
There is no concept of doing that you are asking for. You can do a turn around in which you can get the values on basis of one filter one-act-play using equalTo and rest you can filter on user end like:
export const startSetContent = () => {
return (dispatch, getState) => {
const ref = database.ref("content");
return ref
.orderByChild("category")
.equalTo('one-act-play')
.once('value')
.then(snapshot => {
const content = [];
Object.keys(snapshot.val()).map(k => {
if(k == 'ten-min-play' || k == 'full-length-play'){
snapshot.forEach(childSnapshot => {
content.push({
id: childSnapshot.key,
...childSnapshot.val()
});
});
}
dispatch(setContent(content));
});
};
};
Or you can use library made to fulfil this kind of requirement Querybase