Firebase query undefined when using react-firebase-hooks - javascript

I was following Fireship's building chat app video (this part is around 3:30-4 minutes) but I am trying do something different, just using it for oauth and database setup help. I made a simple database on it, then tried to follow the query format where the collection grab and query seem to work fine since I can console.log them. But when I try to use the collection data, it is undefined. I also found another post on here similar that queried it differently and tried to use an grab a specific variable, but doing that just left me with an error saying "query2 is not a function". So below I added 2 versions of what I tested. ListItem is the collection name, and checked is one of my variables in the document.
function GrabData(){
const dataRef = firestore.collection('ListItem')
console.log(dataRef)
const query = dataRef.orderBy('createdAt')
console.log(query)
const [items1] = useCollectionData(firestore.collection("ListItem"))
console.log(items1)
const [items2] = useCollectionData(query, {idField: 'checked'})
console.log("items = " + items2)
UPADTE: From the error message it seems I needed to add permissions. However, instead of undefined it simply returns empty arrays. And I querying it incorrectly or missing something?
function GrabData(){
const dataRef = firestore.collection('ListItem')
console.log(dataRef)
const query = dataRef.orderBy('createdAt')
console.log(query)
const [items] = useCollectionData(query, {idField: 'checked'})
console.log(items)
const [values, loading, error, snapshot] = useCollectionData(query, {idField: 'checked'});
console.log(values)
console.log(snapshot)
UPDATE 2: I changed the if false to if true in the firebase edit rules tab
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}

To fix the problem I had, edit the firestore permissions from "allow read, write: if false;" change the false to true. Also, make sure that you don't define a variable with the name query since you need to import the query function from firebase/firestore. Otherwise it will be overwritten and throw an error

Related

Issues connecting data stream to users responses in Google Voice App

I am currently developing a voice agent to be used in a smart speaker where users will ask about some items that are being stored in a data stream. The ultimate goal is that users ask about items' names in the stream and google actions through voice will tell them the details about those items as presented in another column in the stream.
To do this, I linked a spreadsheet to Axios to stream the content of the spreadsheet as data to be read in a webhook in google actions. The link to the data stream is HERE.
Honestly, I am new to developing apps for google actions and new to javascript overall so I might be doing silly mistakes.
In the graphical interface for google actions, I am setting a type for the items I want the user to ask about.
Then, I set an intent to recognize the item as a data type and be able to send this to the webhook.
The cloud function in the webhook is as follows:
const { conversation } = require('#assistant/conversation');
const functions = require('firebase-functions');
require('firebase-functions/lib/logger/compat'); // console.log compact
const axios = require('axios');
const app = conversation({debug: true});
app.handle('getItem', async conv => {
const data = await getItem();
const itemParam = app.types.Item;
// conv.add("This test to see if we are accessing the webhook for ${itemParam}");
data.map(item => {
if (item.Name === itemParam)
agent.add('These are the datails for ${itemParam}. It is located in zone
${item.Zone}, at level ${item.Level}');
});
});
async function getItem() {
const res = await axios.get('https://sheetdb.io/api/v1/n3ol4hwmfsmqd');
console.log(res.data);
return res.data; // To use in your Action's response
}
exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);
What the webhook is doing is getting the stream with the getItem function and then mapping the data to find the Name in the stream to match the item parameter (ItemParam) as identified by the user.
However, one of the main problems I have is that when trying to access the item from the user, I am using app.types.Item, but this does not work as when testing I get an error saying: "error": "Cannot read property 'Item' of undefined". I think what is happening is that I am not using the correct way to call the Item in the conversation app.
Also, I am not sure exactly how the linking to the database will work. In other works, I am not sure if
data.map(item => {
if (item.Name === itemParam)
agent.add('These are the datails for ${itemParam}. It is located in zone
${item.Zone}, at level ${item.Level}');
will work.
I have tried multiple things to solve but I am really struggling so any help with this would be really appreciated. Also, I know that I rushed to explain things, so please let me know if you need me to explain better or clarify anything.
Thank you
There are three points I am seeing that won't work.
First, app.types.Item is not the way to get this parameter. You should instead use conv.intent.params['Item'].resolved to get the user's spoken name.
Second, you are trying to use agent.add to include text, but there is no agent in your environment. You should instead be using conv.add.
Third, the text you are sending is not properly escaped between backticks ``. It is the backtick that allows you to use template literals.
Altogether your code can be rewritten as:
const { conversation } = require('#assistant/conversation');
const functions = require('firebase-functions');
require('firebase-functions/lib/logger/compat'); // console.log compact
const axios = require('axios');
const app = conversation({debug: true});
app.handle('getItem', async conv => {
const data = await getItem();
const itemParam = conv.intent.params['Item'].resolved;
data.map(item => {
if (item.Name === itemParam)
conv.add(`These are the datails for ${itemParam}. It is located in zone
${item.Zone}, at level ${item.Level}`);
});
});
async function getItem() {
const res = await axios.get('https://sheetdb.io/api/v1/n3ol4hwmfsmqd');
console.log(res.data);
return res.data; // To use in your Action's response
}
exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);

Is it possible if I can get the last key (latest message) added from the realtime database?

I would like to get the last key (the latest message) from my realtime database but not sure how this can be achieved.
I see from this link i need to get Last child of my firebase databse that I can use orderByKey().limitToLast(1) to get this but it looks like I need to specify the complete ref in order to achieve this. Is that correct? Or is it possible if I can orderByKey().limitToLast(1) on the val()? Or is there another way I can achieve this?
Here is my messages structure in the database:
I have a timestamp child under each key as shown above which I thought I could query in order to extract the latest key but I really don't know how to do this. Can someone please help? Below is my code so far:
database().ref(`messages/`).once(`value`, snapshot => {
if(snapshot.exists()) {
snapshot.forEach(function (childSnapshot) {
if(childSnapshot.key.includes(auth().currentUser.uid)) {
console.log("show me the key: "+childSnapshot.key)
//not working
console.log("show last message: "+ JSON.stringify(childSnapshot.val().orderbyKey().limitToLast(1)))
}
})
}
})
console.log(JSON.stringify(messages)) => [{"-MfqYBzbusp1Cljgxpan":{"unreadMessage":true,"user":{"name":"Mike","avatar":"xxxxxx","_id":"tFhmw5oQoPhk8nF2sx5rE5BFqw93"},"timestamp":1627634061437,"senderId":"tFhmw5oQoPhk8nF2sx5rE5BFqw93","notification":{"body":"Hey","title":"Project","imageUrl":"./assets/xxxxx.png"},"text":"Hey"}}]
console.log(JSON.stringify(unreadMsgs)) => []
Firebase Realtime Database queries work on a flat list of nodes. So if you have a specific path /messages/nodeid already, you can find the latest message under that, but you can't find the latest message across all of /messages.
Reading all messages from all chatrooms, just to find the latest message for each chatroom this user is in is really wasteful though. As you add more users to the app, you're driving up the bandwidth cost for them, and for yourself too.
I recommend keeping a separate node where you track the chat rooms for each user, as explained in my answer on Best way to manage Chat channels in Firebase. With such a node you can then easily determine just the chat rooms for the current user, and then load the latest message for each of them with something like:
database().ref(`user_chatrooms/${auth().currentUser.uid}`).once(`value`, indexSnapshot => {
indexSnapshot.forEach((indexSnapshotChild) => {
let chatroomId = indexSnapshotChild.key;
let query = database().ref(`messages/${chatroomId}`).orderByChild("timestamp").limitToLast(1)
query.once(`value`, (msgSnapshot) => {
console.log(`Last message in ${chatroomId} was ${msgSnapshot.val().text}`);
})
}
})
The orderByKey and limitToLast methods exists on a DatabaseReference and not on the value you fetch from the snapshot fetched earlier. It seems the parent key for all messages is of format userId1userId2. If you know this combination then you run your query this way.
const uidsKey = "uid1" + "uid2"
const query = database().ref(`messages/${uidsKey}`).orderByChild("timestamp").limitToLast(1)
query.once("value").then((snapshot) => {
console.log(snapshot.val())
})
But it seems you are trying to get UIDs of others users who have chats with user1 and trying to real all nodes first. I won't recommend doing that as that might have issues with security rules and so on. Instead if you keep list of those UIDs somewhere else, it'll be better. But if you want to keep what you have right now, try this:
const userUID = auth().currentUser.uid
database().ref("messages/").once("value").then(async (msgSnapshot) => {
const keys = Object.keys(msgSnapshot.val() || {})
const userChatKeys = keys.filter(k => k.includes(userUID))
//const otherUserIDs = userChatKeys.map(k => k.replace(userUID, ""))
//userChatKeys will be chat IDs where current user is included
//now follow the same steps mentioned in first code snippet
const queries = userChatKeys.map(chat => database().ref(`messages/${chat}`).orderByChild("timestamp").limitToLast(1).once("value"))
const lastMessagesSnap = await Promise.all(queries)
const messages = lastMessagesSnap.map(m => Object.values(m.val())[0]))
console.log(`messages: ${messages}`)
const unreadMsgs = messages.filter((msg) => msg.unreadMessage === true)
console.log(unreadMsgs.length)
})
This will logs last message from each of user's chat.

Firebase get all usernames & user Id starting with user entered character

I am trying to only fetch username and user IDs that only start with the User entered text.
Below is my firebase database:
As you can see the database contains a list of user Ids which contains the username.
For Example: If the user enters M in the search box, Query should
return Mr Jois and it's the corresponding user ID.
I am trying to do this using javascript. Below is my code:
function* searchUsers(action) {
const database = firebase.database();
const ref = database.ref('users');
try {
console.log('about to fetch filters users');
const query = ref.orderByChild('username').startAt(action.searchText);
const snapshot = yield call([query, query.once], 'value');
console.log('done fetching users');
console.log(snapshot);
}
catch(error){
console.log(error);
}
}
But I am not getting the expected results. Can someone please tell me how to query the result to get the expected result?
Firebase Database queries do a prefix match, not a contains. But since you only specify startAt(...) the query matches all users from the ones whose name starts with the prefix, including all names after it. If you only want results that start with the prefix string, you'll want to also use endAt(...):
const query = ref.orderByChild('username').startAt(action.searchText)endA‌t(action.searchText+‌​"\uf8ff");
const snapshot = yield call([query, query.once], 'value');
snapshot.forEach(function(child) {
console.log(child.key, child.val().username);
});
Initially, I was thinking the equalTo() query along with Firebase .indexOn the username.
However, what we really need is a substring like ECMAScript 6's String.prototype.startsWith() method:
.startsWith(inputValue);
So, The only way I see to do it with realtime DB is to get/fetch/.once it then process client side where you have more robust string matching capability. I guess the next question is how to pull/fetch only the username property of each user key.
To query based on the first character, you should get that character and pass it to the startAt() function:
const query = ref.orderByChild('username').startAt(action.searchText.charAt(0));

Error: Network error: Error writing result to store for query (Apollo Client)

I am using Apollo Client to make an application to query my server using Graphql. I have a python server on which I execute my graphql queries which fetches data from the database and then returns it back to the client.
I have created a custom NetworkInterface for the client that helps me to make make customized server request (by default ApolloClient makes a POST call to the URL we specify). The network interface only has to have a query() method wherein we return the promise for the result of form Promise<ExecutionResult>.
I am able to make the server call and fetch the requested data but still getting the following error.
Error: Network error: Error writing result to store for query
{
query something{
row{
data
}
}
}
Cannot read property 'row' of undefined
at new ApolloError (ApolloError.js:32)
at ObservableQuery.currentResult (ObservableQuery.js:76)
at GraphQL.dataForChild (react-apollo.browser.umd.js:410)
at GraphQL.render (react-apollo.browser.umd.js:448)
at ReactCompositeComponent.js:796
at measureLifeCyclePerf (ReactCompositeComponent.js:75)
at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (ReactCompositeComponent.js:795)
at ReactCompositeComponentWrapper._renderValidatedComponent (ReactCompositeComponent.js:822)
at ReactCompositeComponentWrapper._updateRenderedComponent (ReactCompositeComponent.js:746)
at ReactCompositeComponentWrapper._performComponentUpdate (ReactCompositeComponent.js:724)
at ReactCompositeComponentWrapper.updateComponent (ReactCompositeComponent.js:645)
at ReactCompositeComponentWrapper.performUpdateIfNecessary (ReactCompositeComponent.js:561)
at Object.performUpdateIfNecessary (ReactReconciler.js:157)
at runBatchedUpdates (ReactUpdates.js:150)
at ReactReconcileTransaction.perform (Transaction.js:140)
at ReactUpdatesFlushTransaction.perform (Transaction.js:140)
at ReactUpdatesFlushTransaction.perform (ReactUpdates.js:89)
at Object.flushBatchedUpdates (ReactUpdates.js:172)
at ReactDefaultBatchingStrategyTransaction.closeAll (Transaction.js:206)
at ReactDefaultBatchingStrategyTransaction.perform (Transaction.js:153)
at Object.batchedUpdates (ReactDefaultBatchingStrategy.js:62)
at Object.enqueueUpdate (ReactUpdates.js:200)
I want to know the possible cause of the error and solution if possible.
I had a similar error.
I worked it out by adding id to query.
for example, my current query was
query {
service:me {
productServices {
id
title
}
}
}
my new query was
query {
service:me {
id // <-------
productServices {
id
title
}
}
}
we need to include id,
otherwise it will cause the mentioned error.
{
query something {
id
row {
id
data
}
}
}
I've finally found out what is causing this issue after battling with it in various parts of our app for months. What helped to shed some light on it was switching from apollo-cache-inmemory to apollo-cache-hermes.
I experimented with Hermes hoping to mitigate this ussue, but unfortunately it fails to update the cache the same as apollo-cache-inmemory. What is curious though is that hermes shows a very nice user friendly message, unlike apollo-cache-inmemory. This lead me to a revelation that cache really hits this problem when it's trying to store an object type that is already in the cache with an ID, but the new object type is lacking it. So apollo-cache-inmemory should work fine if you are meticulously consistent when querying your fields. If you omit id field everywhere for a certain object type it will happily work. If you use id field everywhere it will work correctly. Once you mix queries with and without id that's when cache blows up with this horrible error message.
This is not a bug-it's working as intended, it's even documented here: https://www.apollographql.com/docs/react/caching/cache-configuration/#default-identifiers
2020 update: Apollo has since removed this "feature" from the cache, so this error should not be thrown anymore in apollo-client 3 and newer.
I had a similar looking issue.
Perhaps your app was attempting to write (the network response data) to the store with the wrong store address?
Solution for my problem
I was updating the store after adding a player to a team:
// Apollo option object for `mutation AddPlayer`
update: (store, response) => {
const addr = { query: gql(QUERY_TEAM), variables: { _id } };
const data = store.readQuery(addr);
stored.teams.players.push(response.data.player));
store.writeQuery({...addr, data});
}
I started to get a similar error above (I'm on Apollo 2.0.2)
After digging into the store, I realised my QUERY_TEAM request made with one variable meta defaulting to null. The store "address" seems to use the *stringified addr to identify the record. So I changed my above code to mimic include the null:
// Apollo option object for `mutation AddPlayer`
update: (store, response) => {
const addr = { query: gql(QUERY_TEAM), variables: { _id, meta: null } };
const data = store.readQuery(addr);
data.teams.players.push(response.data.player));
store.writeQuery({...addr, data});
}
And this fixed my issue.
* Defaulting to undefined instead of null will probably avoid this nasty bug (unverified)
Further info
My issue may be only tangentially related, so if that doesn't help I have two peices of advice:
First, add these 3 lines to node_modules/apollo-cache-inmemory/lib/writeToStore.js to alert you when the "record" is empty.
And then investigate _a to understand what is going wrong.
exports.writeResultToStore = writeResultToStore;
function writeSelectionSetToStore(_a) {
var result = _a.result, dataId = _a.dataId, selectionSet = _a.selectionSet, context = _a.context;
var variables = context.variables, store = context.store, fragmentMap = context.fragmentMap;
+if (typeof result === 'undefined') {
+ debugger;
+}
Second, ensure all queries, mutations and manual store updates are saving with the variables you expect
For me adding "__typename" into query helped.
Solution for this is 1. it happening when missing id, second one is it is happening when you have same query and hitting them alternately.
Example if you have query like dog and cat.
query dog(){id, name}
query cat(){id, name }
here both query are same just their header are different, during that time, this type of issue is coming. currently i have fetching same query with different status and getting this error and am lost in search of solution.

delete node in firebase

been reading docs and other posts but I am unable to remove a node. I am attempting to select the node by a value.
var eventContactsRef = firebase.database().ref('events-contacts');
eventContactsRef.orderByChild('eventContactId').equalTo(eventContactId);
then call the remove method on the result
eventContactsRef.remove(function (error) {
console.log(error);
});
nothing happens except a null error value. I am using the latest firebase, most examples are for older versions so I am unsure if I need to get the key and then attempt to delete with that as a reference?
This is the first time using firebase so I am not sure if I have saved the data correctly. here is code to save.
var key = firebase.database().ref().child('event-contacts').push().key;
var url = firebase.database().ref('/event-contacts/' + key);
url.set(eventContacts);
and screenshot
You cannot remove a query itself. You can only remove the results that match a query.
var eventContactsRef = firebase.database().ref('events-contacts');
var query = eventContactsRef.orderByChild('eventContactId').equalTo(eventContactId);
query.on('child_added', function(snapshot) {
snapshot.ref.remove();
})

Categories