I am following the Firebase tutorial on how to implement Algolia with Firebase: https://firebase.google.com/docs/firestore/solutions/search
I am currently stuck on the indexing part of the tutorial as I have errors coming from the firebase cloud-functions logs.
This is the output of the cloud-functions log
and this is the code I wrote
const functions = require('firebase-functions');
const algoliasearch = require("algoliasearch");
const ALGOLIA_ID = functions.config().algolia.app;
const ALGOLIA_ADMIN_KEY = functions.config().algolia.key;
const ALGOLIA_SEARCH_KEY = functions.config().algolia.search_key;
const ALGOLIA_INDEX_NAME = 'users';
const client = algoliasearch(ALGOLIA_ID, ALGOLIA_ADMIN_KEY);
// Update the search index every time a blog post is written.
exports.onUserCreated = functions.firestore.document('organisations/40R0LMA6ALZgF7KjHJMc/employees/{userId}').onCreate((snap, context) => {
// Get the note document
const user = snap.data();
// Add an 'objectID' field which Algolia requires
user.objectID = snap.id;
console.log(user.objectID)
// Write to the algolia index
const index = client.initIndex(ALGOLIA_INDEX_NAME);
return index.saveObject(user);
});
It seems that you are not correctly setting the different environment variables used in this example.
As explained in the doc, to get the value of the algolia.app environment variable when you do const ALGOLIA_ID = functions.config().algolia.app; you need to previously set its value as follows:
firebase functions:config:set algolia.app="THE_ALGOLIA_ID"
Since you need to set several variables, you can set them in one instruction, as follows:
firebase functions:config:set algolia.app="THE_ALGOLIA_ID" algolia.key="THE_ALGOLIA_ADMIN_KEY" ...
As explained in the doc, "to inspect what's currently stored in environment config for your project, you can use firebase functions:config:get" in the CLI.
Related
I have some code that looks like the following:
export const createTable = async (data) => {
const doc = db.collection("tables").doc();
const ref = db
.collection("tables")
.where("userId", "==", data.userId)
.orderBy("number", "desc").limit(1);
db.runTransaction(async transaction => {
const query = await transaction.get(ref);
let number = 1;
if (!query.empty) {
const snapshot = query.docs[0];
const data = snapshot.data();
const id = snapshot.id;
number = data.number + 1;
}
data = {number, ...data};
transaction.set(doc, data);
});
Basically I have a tables collection and each table has an auto generated number like #1, #2, #3
When creating new tables, I want to fetch the latest table number and create the new table with that number incremented by 1.
I wanted to wrap it in a transaction so that if a table created while running the transaction, it will restart so that I don't end up with duplicate numbers.
However, this errors out on the .get(), and from googling I've read that Firestore can't monitor a whole collection within transactions, but instead it requires a specific doc passed to it. Which I obviously can't do because I need to monitor for new docs created in that collection, not changes in a particular doc.
If so, what's the correct way to implement this?
The Firestore transaction API for client apps requires that you get() each individual document that you want to participate in the transaction. So, if you have a query whose results you want to transact with, you will need to:
Perform the query (outside of the transaction)
Collect document references for each document in the result set
In the transaction, get() them all individually.
You will be limited to 500 documents per transaction.
If you want to dynamically look for new documents to modify, you will probably much better off implementing that on the backend using a Firestore trigger in Cloud Functions to automatically handle each new document as they are created, without requiring any code on the client.
Because you're updating just one document, you probably don't need to use transactions for incrementing values.
You can use Firestore Increment to achieve this.
Here is an example taken from here:
const db = firebase.firestore();
const increment = firebase.firestore.FieldValue.increment(1);
// Document reference
const storyRef = db.collection('stories').doc('hello-world');
// Update read count
storyRef.update({ reads: increment });
This is the easiest way to increment values in Firestore.
I am trying to set a region to deploy my functions. According to the documentation I have to do:
var functions = firebase.app().functions('us-west2');
But when I do this and then try to deploy I get an error:
Error: Error occurred while parsing your function triggers.
TypeError: Cannot read property 'onCall' of undefined
If I change functions definitions back to default:
const functions = require("firebase-functions");
It works, any ideas why I get this error?
Sample Code:
const firebase = require("firebase");
const admin = require("firebase-admin");
require("firebase-functions");
firebase.initializeApp({...})
admin.initializeApp()
let functions = firebase.app().functions('us-east1')
exports.findUserInAuth = functions.https.onCall((data, context) => {..}
I think you're reading the documentation incorrectly.
If this is a most basic definition of a callable function, as suggested by the documentation:
const functions = require("firebase-functions");
exports.findUserInAuth = functions.https.onCall((data, context) => {
// ...
});
Then, to change the region, you need to insert some more method calls in the builder for that function:
const functions = require("firebase-functions");
exports.findUserInAuth = functions.https.region('us-west2').onCall((data, context) => {
// ...
});
The code on the frontend client will not use firebase-functions. You have to use the instructions for setting up the client later on that page. Setting the region on the client works differently.
Every hour, I want my firebase function to look through my database, read a value, calculate a new value from this old value, and then update it in the database. I am having trouble accessing a snapshot of the data. Specificically,
exports.scheduledFunction = functions.pubsub.schedule('every 1 hour').onRun((context) => {
const ref = functions.database.ref('/users/test_user/commutes');
ref.once('value',function(snapshot) {
// do new calculation here
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
return null;
});
I am getting a : functions: TypeError: ref.once is not a function error.
How do I access a value from my firebase real time database and update it from a Firebase function?
You're trying to use the firebase-functions SDK to query the database. It can't do that. You will have to use the Firebase Admin SDK to make the query.
You will need to get started like this (not complete, but you should be able to see what you need to do). Import and initialize at the global scope:
const admin = require('firebase-admin')
admin.initializeApp()
Then in your function, use it. Be sure to work with promises correctly.
const ref = admin.database().ref('...')
return ref.once('value').then(snapshot => {
// work with the snapshot here, and return another promise
// that resolves after all your updates are complete
})
The firebase-functions is different from the client side. The ref() function according to the docs:
ref: function
ref(path: string): RefBuilder
Select Firebase Realtime Database Reference to listen to.
Path of the database to listen to.
Returns RefBuilder
The RefBuilder will contain the database triggers that you can call, onCreate(), onWrite(). To be able to use your database, then you need to use the admin sdk.
https://firebase.google.com/docs/reference/functions/providers_database_.refbuilder
This question already has answers here:
Firebase DB - How to update particular value of child in Firebase Database
(3 answers)
Closed 4 years ago.
I am trying to keep some data consistent with firebase cloud functions. When data changes in the main list, I want all the data to change in the user's favourite list.
Currently, I am able to call the function and get a log of the correct data which is changing, but my query doesn't work. I am getting the following error:
TypeError: ref.update is not a function
at exports.itemUpdate.functions.database.ref.onUpdate
Here is my code:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.itemUpdate = functions.database
.ref('/items/{itemId}')
.onUpdate((change, context) => {
const before = change.before.val(); // DataSnapshot after the change
const after = change.after.val(); // DataSnapshot after the change
console.log(after);
if (before.effects === after.effects) {
console.log('effects didnt change')
return null;
}
const ref = admin.database().ref('users')
.orderByChild('likedItems')
.equalTo(before.title);
console.log(ref);
return ref.update(after);
});
I'm not to sure where I am going wrong, I appreciate all the help and guidance to resolve this!
Cheers.
equalTo() returns a Query object. You're then trying to call update() on that object. Note that in the linked API docs, Query doesn't have an update() method. You can't simply "update" a Query that hasn't been performed. You're going to have to actually perform the query using once(), iterate the results form the snapshot in the returned promise, and perform further updates using the data you find.
I tried both and all working. What are the difference?
import firebase from 'react-native-firebase';
const defaultApp = firebase.app();
defaultApp.database().ref('foobar').once('value', (snapshot) => {
// snapshot from default app
});
vs
import firebase from 'react-native-firebase';
firebase.database().ref('foobar').once('value', (snapshot) => {
// snapshot from default app
});
The two approaches are equivalent. The second one just relies on some hard-coded defaults, while the first is more explicit. This becomes especially apparent if you want to (for example) access to databases in a single app.
Our documentation explains this rather well, so I'll quote from there:
In most cases, you will only have to initialize a single, default app. You can access services off of that app in two equivalent ways:
// Initialize the default app
var defaultApp = firebase.initializeApp(defaultAppConfig);
console.log(defaultApp.name); // "[DEFAULT]"
// You can retrieve services via the defaultApp variable...
var defaultStorage = defaultApp.storage();
var defaultDatabase = defaultApp.database();
// ... or you can use the equivalent shorthand notation
defaultStorage = firebase.storage();
defaultDatabase = firebase.database();
Some use cases require you to create multiple apps at the same time. For example, you might want to read data from the Realtime Database of one Firebase project and store files in another project. Or you might want to authenticate one app while have another app be unauthenticated. The Firebase SDK allows you create multiple apps at the same time, each with their own configuration information.
// Initialize the default app
firebase.initializeApp(defaultAppConfig);
// Initialize another app with a different config
var otherApp = firebase.initializeApp(otherAppConfig, "other");
console.log(firebase.app().name); // "[DEFAULT]"
console.log(otherApp.name); // "other"
// Use the shorthand notation to retrieve the default app's services
var defaultStorage = firebase.storage();
var defaultDatabase = firebase.database();
// Use the otherApp variable to retrieve the other app's services
var otherStorage = otherApp.storage();
var otherDatabase = otherApp.database();
Note: Each app instance has its own configuration options and authentication state.
you do not need to call that method, unless you are using more than one firebase app instance in your application