Last active
September 16, 2022 10:52
-
-
Save ydnar/8e4a51f7d1ce42e9bb4ae53ba049de4a to your computer and use it in GitHub Desktop.
Versioned documents with Firestore Cloud Functions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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