Skip to content

Instantly share code, notes, and snippets.

@ydnar
Last active September 16, 2022 10:52
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save ydnar/8e4a51f7d1ce42e9bb4ae53ba049de4a to your computer and use it in GitHub Desktop.
Save ydnar/8e4a51f7d1ce42e9bb4ae53ba049de4a to your computer and use it in GitHub Desktop.
Versioned documents with Firestore Cloud Functions
import * as admin from 'firebase-admin'
import * as functions from 'firebase-functions'
admin.initializeApp(functions.config().firebase)
const db = admin.firestore()
async function writeIntegerVersion(event) {
const ref = event.data.ref
if (ref.path.indexOf('/_versions/') >= 0) {
return false
}
const snapshot = event.data
const previous = event.data.previous
// Return early if a previous snapshot existed AND its version differs than the current version.
// FIXME: if a client modifies _version along with other fields, we won’t create a versioned snapshot.
if (previous && previous.get('_version') != snapshot.get('_version')) {
return false
}
let latest = 0
const query = db.collection(`${ref.path}/_versions`).orderBy('_version', 'desc').limit(1)
const querySnapshot = await query.get()
if (!querySnapshot.empty) {
latest = querySnapshot.docs[0].get('_version')
console.log(`Highest version found: ${latest} @ ${querySnapshot.docs[0].ref.path}`)
latest = +latest || 0
}
const version = latest + 1
const data = event.data.data()
data._version = version
return Promise.all([
ref.set({ _version: version }, { merge: true }),
db.doc(`${ref.path}/_versions/${version}`).create(data)
])
}
async function writeTimestampVersion(event) {
if (event.data.ref.path.indexOf('/_versions/') >= 0) {
return false
}
return db.doc(`${event.data.ref.path}/_versions/${event.data.updateTime}`).create(event.data.data())
}
const ffd = functions.firestore.document
// Set fn to writeIntegerVersion for linear version numbers (more expensive)
// Set fn to writeTimestampVersion for timestamp version IDs (cheaper; no reads or updates)
const fn = writeTimestampVersion
// Root-level collection
export const createVersion = ffd('{collection}/{id}').onCreate(fn)
export const updateVersion = ffd('{collection}/{id}').onUpdate(fn)
// Subcollection nested 1-deep
export const createVersionN1 = ffd('{parent}/{pid}/{collection}/{id}').onCreate(fn)
export const updateVersionN1 = ffd('{parent}/{pid}/{collection}/{id}').onUpdate(fn)
// Subcollection nested 2-deep
export const createVersionN2 = ffd('{grandparent}/{gid}/{parent}/{pid}/{collection}/{id}').onCreate(fn)
export const updateVersionN2 = ffd('{grandparent}/{gid}/{parent}/{pid}/{collection}/{id}').onUpdate(fn)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment