Attempting to use equalTo on Firebase Realtime Database query - javascript

I have the following node:
attendanceOptions: {
uid1: 'blah#example.com',
uid2: 'tap#example.com'
}
I'm trying to return only the key and value where value is equal to. These are my two attempts.
const getMinistry = await admin.database().ref(`organization/${req.orgId}/attendanceOptions`)
getMinistry
.equalTo(req.memberUid)
.once('value', snapshot => {
functions.logger.log(snapshot.val())
});
This returns null
I also tried:
const getMinistry = await admin.database().ref(`organization/${req.orgId}`)
getMinistry
.orderByChild('attendanceOptions')
.equalTo(req.memberUid)
.once('value', snapshot => {
const data = snapshot.val();
functions.logger.log( `this is data: ${data}` )
return data;
});
The log is this is data: null
In my rules I have:
"attendanceOptions": {
".indexOn": [".value"]
},
What am I doing wrong here?

The correct syntax is:
getMinistry
.child('attendanceOptions')
.orderByValue()
.equalTo(req.memberUid)

Related

Reading a value from Realtime Firebase

I have the following json structure:
Within "all" node I have an attribute "drinkId" and I'm trying to move it outside that child node bringing it one level up.
I'm trying to read the value without any luck
const cocktailRef= firebase
.database()
.ref("Ratings");
cocktailRef.once("value", (snapshot) => {
snapshot.forEach((child) => {
const drinkIdPass = child.ref.child("all").child("drinkId").value();
child.ref.update({ drinkId: drinkIdPass });
})
})
I've tried different variants of ".value()", same problem
There isn't any value() method on a DataSnapshot. It's val() Try refactoring your code like this:
const cocktailRef= firebase.database().ref("Ratings");
cocktailRef.once("value").then(async (snapshot) => {
const updates = { }
snapshot.forEach((child) => {
const drinkIdPass = child.val().all.drinkId
updates[`${child.key}/drinkId`] = drinkIdPass
})
await cocktailRef.update(updates)
console.log("Data updated")
})

How to get a single doc with React/Firestore?

All I want to do is to get a row (so called 'doc') from a data base.
so far, I have tried:
all with the 'aref'
const aref = firebase
.firestore()
.collection("polja")
.where("id", "==", match.params.id);
console.log(aref);
function getIt() {
const item = [];
setLoading(true);
aref.get().then((doc) => {
const data = doc.data();
setItem(item);
console.log(item);
setLoading(false);
});
}
useEffect(() => {
getIt();
}, []);
this gave the following error:
To get a single document, you must specify the document ID:
firebase.firestore().collection("polja").doc(documentId).get().then((snapshot) => {
console.log(snapshot.data())
}).catch((e) => console.log(e))
Also you should not use .where() to get just a single document, but there is an issue I found in your original code.
If you look carefully, the parameter in .where() is a string "match.params.id". That seems to be a dynamic value being fetched from somewhere else. Please remove the quotes and try again.
firebase.firestore().collection("polja").where("id", "==", match.params.id).get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
});
})
.catch((error) => {
console.log("Error getting documents: ", error);
});
Try adding a catch block as shown which might help catch any errors. Make sure your security rules also allow you to fetch the data.
Also if any error is logged in the console, share a screenshot of it.
To get single document form firebase firestore you should first know if you are using the modular firebase 9.+ version or firebase version < 9.
In new modular firebase firestore(version 9.+) it should be like this:
import { getFirestore, collection, query, getDocs } from 'firebase/firestore'
async read(id) {
const firestore = getFirestore()
const docRef = doc(firestore, this.collectionPath, id)
const docSnap = await getDoc(docRef)
const data = docSnap.exists() ? docSnap.data() : null
if (data === null || data === undefined) return null
return { id, ...data }
}
If you are using not modular Firebase firestore( < version 9) then the same function should look something like this:
async read(id) {
const result = await (await firestore())
.collection(this.collectionPath)
.doc(id)
.get()
const data = result.exists ? result.data() : null
if (data === null || data === undefined) return null
return { id, ...data }
}

Cloud Firestore: Query two collection [duplicate]

I have a Cloud Firestore DB with the following structure:
users
[uid]
name: "Test User"
posts
[id]
content: "Just some test post."
timestamp: (Dec. 22, 2017)
uid: [uid]
There is more data present in the actual DB, the above just illustrates the collection/document/field structure.
I have a view in my web app where I'm displaying posts and would like to display the name of the user who posted. I'm using the below query to fetch the posts:
let loadedPosts = {};
posts = db.collection('posts')
.orderBy('timestamp', 'desc')
.limit(3);
posts.get()
.then((docSnaps) => {
const postDocs = docSnaps.docs;
for (let i in postDocs) {
loadedPosts[postDocs[i].id] = postDocs[i].data();
}
});
// Render loadedPosts later
What I want to do is query the user object by the uid stored in the post's uid field, and add the user's name field into the corresponding loadedPosts object. If I was only loading one post at a time this would be no problem, just wait for the query to come back with an object and in the .then() function make another query to the user document, and so on.
However because I'm getting multiple post documents at once, I'm having a hard time figuring out how to map the correct user to the correct post after calling .get() on each post's user/[uid] document due to the asynchronous way they return.
Can anyone think of an elegant solution to this issue?
It seems fairly simple to me:
let loadedPosts = {};
posts = db.collection('posts')
.orderBy('timestamp', 'desc')
.limit(3);
posts.get()
.then((docSnaps) => {
docSnaps.forEach((doc) => {
loadedPosts[doc.id] = doc.data();
db.collection('users').child(doc.data().uid).get().then((userDoc) => {
loadedPosts[doc.id].userName = userDoc.data().name;
});
})
});
If you want to prevent loading a user multiple times, you can cache the user data client side. In that case I'd recommend factoring the user-loading code into a helper function. But it'll be a variation of the above.
I would do 1 user doc call and the needed posts call.
let users = {} ;
let loadedPosts = {};
db.collection('users').get().then((results) => {
results.forEach((doc) => {
users[doc.id] = doc.data();
});
posts = db.collection('posts').orderBy('timestamp', 'desc').limit(3);
posts.get().then((docSnaps) => {
docSnaps.forEach((doc) => {
loadedPosts[doc.id] = doc.data();
loadedPosts[doc.id].userName = users[doc.data().uid].name;
});
});
After trying multiple solution I get it done with RXJS combineLatest, take operator. Using map function we can combine result.
Might not be an optimum solution but here its solve your problem.
combineLatest(
this.firestore.collection('Collection1').snapshotChanges(),
this.firestore.collection('Collection2').snapshotChanges(),
//In collection 2 we have document with reference id of collection 1
)
.pipe(
take(1),
).subscribe(
([dataFromCollection1, dataFromCollection2]) => {
this.dataofCollection1 = dataFromCollection1.map((data) => {
return {
id: data.payload.doc.id,
...data.payload.doc.data() as {},
}
as IdataFromCollection1;
});
this.dataofCollection2 = dataFromCollection2.map((data2) => {
return {
id: data2.payload.doc.id,
...data2.payload.doc.data() as {},
}
as IdataFromCollection2;
});
console.log(this.dataofCollection2, 'all feeess');
const mergeDataFromCollection =
this.dataofCollection1.map(itm => ({
payment: [this.dataofCollection2.find((item) => (item.RefId === itm.id))],
...itm
}))
console.log(mergeDataFromCollection, 'all data');
},
my solution as below.
Concept: You know user id you want to get information, in your posts list, you can request user document and save it as promise in your post item. after promise resolve then you get user information.
Note: i do not test below code, but it is simplify version of my code.
let posts: Observable<{}[]>; // you can display in HTML directly with | async tag
this.posts = this.listenPosts()
.map( posts => {
posts.forEach( post => {
post.promise = this.getUserDoc( post.uid )
.then( (doc: DocumentSnapshot) => {
post.userName = doc.data().name;
});
}); // end forEach
return posts;
});
// normally, i keep in provider
listenPosts(): Observable<any> {
let fsPath = 'posts';
return this.afDb.collection( fsPath ).valueChanges();
}
// to get the document according the user uid
getUserDoc( uid: string ): Promise<any> {
let fsPath = 'users/' + uid;
return this.afDb.doc( fsPath ).ref.get();
}
Note: afDb: AngularFirestore it is initialize in constructor (by angularFire lib)
If you want to join observables instead of promises, use combineLatest. Here is an example joining a user document to a post document:
getPosts(): Observable<Post[]> {
let data: any;
return this.afs.collection<Post>('posts').valueChanges().pipe(
switchMap((r: any[]) => {
data = r;
const docs = r.map(
(d: any) => this.afs.doc<any>(`users/${d.user}`).valueChanges()
);
return combineLatest(docs).pipe(
map((arr: any) => arr.reduce((acc: any, cur: any) => [acc].concat(cur)))
);
}),
map((d: any) => {
let i = 0;
return d.map(
(doc: any) => {
const t = { ...data[i], user: doc };
++i;
return t;
}
);
})
);
}
This example joins each document in a collection, but you could simplify this if you wanted to just join one single document to another.
This assumes your post document has a user variable with the userId of the document.
J

Firestore specific data read does not work with variable but works with static value

I am trying to query specific document from firestore database. The problem seems to be that If I add the doc(id) statically, it works but with variable it does not even tho the variable has correct and exact same value I tested statically with.
The document I am trying to retrieve is a User node/document under /users collection.
read is the function I am using to retrieve the data:
export default class GenericDB {
constructor(collectionPath) {
this.collectionPath = collectionPath
}
/**
* Read a document in the collection
* #param id
*/
async read(id) {
const result = await (await firestore())
.collection(this.collectionPath)
.doc(id)
.get()
const data = result.exists ? result.data() : null
if (isNil(data)) return null
this.convertObjectTimestampPropertiesToDate(data)
return { id, ...data }
}
}
This is my vuex action:
getUser: ({ commit }, userId) => {
return new Promise((resolve, reject) => {
//usin UsersDB() instead of Generic() because my UsersDB() has constructor with correct path to /users
new UsersDB().read(userId).then(user => {
//Empty user if userId value is from variable and not empty if I use static value
resolve(user)
})
})
}
And I do call it out:
mounted() {
if (this.id) {
//getUser function is declared inside ...mapActions('authentication', ['getUser'])
this.getUser(this.id)
}
}
Update 1: I did compare static string against my variable with logical operator and it turns out that the variable userId has space at the end. I have no clue why and where does it come.
There is no error just empty data. I can not see what can be wrong with this simple query. Any help is appreciated!
Try making connection this way instead of directly using it.
const db = firebase.firestore();
async function read(id) {
const result = await db
.collection(this.collectionPath)
.doc(id)
.get()
const data = result.exists ? result.data() : null
if (isNil(data)) return null
this.convertObjectTimestampPropertiesToDate(data)
return { id, ...data }
}
Generally, standard format we mostly use to get document is :
const db = firebase.firestore();
const result = await db
.collection("collection_name")
.doc("document_id")
.get();
I hope this helps you. Please let me know for any issues.
After you edited the question I tried passing a valid variable and I am getting response. This is giving me data of document.
//Firebase
const admin = require("firebase-admin");
let serviceAccount = require("./firebase.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
let db = admin.firestore();
//End of Firebase
id = "lWxkvqZnBxNRke4SFyJj"
async function getData(id) {
const result = await db
.collection("users")
.doc(id)
.get();
data = result.data()
console.log(data)
return data
}
getData(id)
It turned out that the userId was not exatctly the same if I compared them with logical operator. The variable version had space at the end.
So the solution was to use userId.replace(/\s/g, '')
I was facing the same issue recently. Then I figured out that my string has quotes. The following code solved the problem.
roomId.trim().replace(/['"]+/g, '')

JavaScript Promise - Object defined but specific property undefined

I'm trying to get data from firebase. I receive the data as an Object and it shows the data I want. However when I try to get a specific value from an Object property it says it's undefined. What am I doing wrong or what am I missing?
export const createChat = () => {
return async dispatch => {
dispatch({ type: CREATE_SINGLE_CHAT });
const creator = firebase.auth().currentUser.uid;
const userRef = firebase.database().ref(`/users/${creator}`);
const currUser = await userRef.once('value');
console.log(currUser);
console.log(currUser.name);
};
};
Here is the console.log:
23:30:58 // currUser
Object {
"email": "a#b.de",
"is24Hours": true,
"name": "Alex",
}
23:30:58 // currUser.name
undefined
I'm using React Native with Redux and Redux-Thunk and also the realtime-database from firebase as database.
As Max Brodin mentioned I forgot to use the val() method from firebase:
firebase.google.com/docs/database/web/read-and-write
working code:
export const createChat = () => {
return async dispatch => {
dispatch({ type: CREATE_SINGLE_CHAT });
const creator = firebase.auth().currentUser.uid;
const userRef = firebase.database().ref(`/users/${creator}`);
const currUser = await userRef.once('value');
console.log(currUser);
console.log(currUser.val().name); // here is the change
};
};

Categories

Resources