node-red-contrib-cloud-firestore
v3.2.0
Published
Node-RED nodes to handle google cloud firestore read and write operations.
Downloads
1,205
Maintainers
Readme
node-red-contrib-cloud-firestore
Node-RED nodes to handle google cloud firestore read and write operations.
For any assistance, contributions or stars, visit the repo.
Install
Install from the palette manager
node-red-contrib-cloud-firestore
Install from npm
npm install node-red-contrib-cloud-firestore
Usage
Admin configuration
A configuration property under either Read or Write nodes that
initializes a firebase app taking in a name and the json contents of
your apps service account credentials which can be generated under
Project settings > service accounts > Firebase Admin SDK
.
Firestore Read
Node fetches data from a referenced collection, subcollection or document.
Configurations can be made within the node or on the msg.firestore
property:
collection
: [string] The collection or subCollection in referencedocument
: [string] The document reference under the defined collectionrealtime
: [boolean] telling the node to listen for live updates or not (false by default)group
: [boolean] fetch all documents under collections with the above supplied collection name (false by default)query
: [array<object>] an array of objects defining query methods to apply to the readdisableHandler
: [boolean] disables the default snapshot handler, returning a built query reference as the payload (false by default)
Response data from the operation is output through the msg.payload
property
Upstream input queries
To perform dynamic queries with the read node through input, you need to supply an array of objects on the msg.firestore.query
property in the order they will be chained
with the query method as the only property and it's value being an array of arguments, or a single string value as show below.
{
query : [
{where: ["state", "==", "CA"]},
{where: ["population", "<", 1000000]}
]
}
// reference.where("state", "==", "CA").where("population", "<", 1000000)
{
query : [
{orderBy: "name"},
{limit: 2}
]
}
// reference.orderBy("name").limit(2)
{
query : [
{where: ["population", ">", 100000]},
{orderBy: ["population", "asc"]},
{limit: 2}
]
}
// reference.where("population", ">", 100000).orderBy("population", "asc").limit(2)
{
query : [
{orderBy: "population"},
{startAt: 100000},
{endAt: 1000000}
]
}
// reference.orderBy("population").startAt(100000).endAt(1000000)
Custom snapshot handler
You can also write your own snapshot handler under the other settings accordion. The editor is similar to the core function node & but supports
the following global objects: config
(the nodes settings), snap
(query snapshot), util
(nodejs), msg
, context
, RED.util
& console
.
The Promise
, Buffer
and Date
objects are also supported.
The
snap
object contains the resulting query snapshot.
Do remember that what you return
will then be sent as the output payload.
The following example returns an array of objects, while logging to the cmd console
const docs = [];
let added = context.flow.get('added');
snap.docChanges().forEach(change => {
docs.push(change.doc.data());
if (change.type === 'added') {
added++;
console.log('Added: ', change.doc.data());
}
if (change.type === 'modified') {
console.log('Modified: ', change.doc.data());
}
if (change.type === 'removed') {
console.log('Removed: ', change.doc.data());
}
});
context.flow.set('added', added);
return docs;
Additionally, you can also save your snippets into the snippet library by giving it a file name and clicking the Save to Library
button
Realtime edge cases
If you intend on passing in dynamic configurations from an upstream node while still having realtime enabled, the node will not have your upstream values recorded during the next restart. This could result in some unexpected outcomes.
A way around this would be for the node to store your most recent settings from the interface / upstream nodes into node-red's provided storage mechanism.
To enable this workaround in the node, you'll have to change your instances default storage module from memory
to localfilesystem
in your settings.js
file. Read more on this here
Firestore Write
Node performs write operations to the referenced collection, subCollection or document.
Configurations made from within the node or on the msg.firestore
property:
operation
: [string] Write operation to perform, eitheradd
,set
,update
ordelete
collection
: [string] collection or subCollection reference to write to.document
: [string] document reference to write to (optional foradd
operations)options
: [object] additional options passed to firebase (currently specific toset
operations)
Handling Firestore classes & sentinels
Due to the nature of Cloud firestore's implementation, some actions need special handling.
Arrays
To perform array updates, you'll
need to wrap your elements in an object with the _arrayUnion
or _arrayRemove
property to add or remove elements respectively within an array
msg.payload = {
animals: {
_arrayUnion: 'goats'
},
farmers: {
_arrayRemove: {name: "John Doe"}
}
}
becomes:
msg.payload = {
animals: firestore.FieldValue.arrayUnion("goats"),
farmers: firestore.FieldValue.arrayRemove({name: "John Doe"})
}
GeoPoints
Objects within the payload received by the Write Node containing a _lat
and _lng
property will be replaced with the appropriate GeoPoint class
msg.payload = {
farm:{
location: {
_lat: -1.232134,
_lng: 36.123131
},
fence: [
{_lat: -1.433434, _lng: 35.123324},
{_lat: -1.673214, _lng: 36.126541},
{_lat: -1.334124, _lng: 34.342131}
]
}
}
becomes:
msg.payload = {
farm: {
location: new firestore.GeoPoint(-1.232134, 36.123131),
fence: [
new firestore.GeoPoint(-1.433434, 35.123324),
new firestore.GeoPoint(-1.673214, 36.126541),
new firestore.GeoPoint(-1.334124, 34.342131)
]
}
}
Server Timestamp
Properties with the _serverTimestamp
string value will be replace with the appropriate serverTimestamp sentinel
msg.payload = {
time: '_serverTimestamp'
}
becomes:
msg.payload = {
time: firestore.FieldValue.serverTimestamp()
}
Increment
Properties with the _increment
string value will be replaced with the appropriate increment sentinel
msg.payload = {
itemCount: {
'_increment': 30
}
}
becomes:
msg.payload = {
itemCount: firestore.FieldValue.increment(30)
}
Delete
Properties with the _delete
string value will be replaced with the appropriate delete sentinel
msg.payload = {
unwantedField: '_delete'
}
becomes:
msg.payload = {
unwantedField: firestore.FieldValue.delete()
}
Extensibility & additional use cases
Mustache templating
Both the read & write nodes support mustache templating on the collection
& document
properties, which allows you to
cherry pick your collection / document properties directly from the msg
object.
For example, setting the collection field to {{col}}
with a message object like
msg = {
col: "users"
}
will have the corresponding node run operations against the users
collection.
Firebase instances
Both the read & write nodes can expose a firebase
object that contains the current app instance and a reference
to the firebase admin sdk, which allows you to extend the node to your liking. Simply enable the eject property in the ui / via
an upstream input.
// upstream input / via the ui
msg.firestore = {
eject: true
};
// output
msg.firebase = {
"app": "...", // current firebase instance
"admin": "...", // firebase admin sdk
};
TODO
- Handle transactions and batch writes