我有一个名为Posts的消防站集合 我在客户端插入了一个插件,就可以了。
我想使用firebase函数将createdAt和updatedAt字段添加到我的帖子集合Firestore中的每个插入中。
答案 0 :(得分:4)
更新11/24/20 -我实际上将以下函数放入了我的npm包 adv-firestore-functions :
请参阅:https://fireblog.io/blog/post/automatic-firestore-timestamps
我创建了一个通用云功能,可以使用createdAt和updatedAt时间戳更新所需的任何文档:
exports.myFunction = functions.firestore
.document('{colId}/{docId}')
.onWrite(async (change, context) => {
// the collections you want to trigger
const setCols = ['posts', 'reviews','comments'];
// if not one of the set columns
if (setCols.indexOf(context.params.colId) === -1) {
return null;
}
// simplify event types
const createDoc = change.after.exists && !change.before.exists;
const updateDoc = change.before.exists && change.after.exists;
const deleteDoc = change.before.exists && !change.after.exists;
if (deleteDoc) {
return null;
}
// simplify input data
const after: any = change.after.exists ? change.after.data() : null;
const before: any = change.before.exists ? change.before.data() : null;
// prevent update loops from triggers
const canUpdate = () => {
// if update trigger
if (before.updatedAt && after.updatedAt) {
if (after.updatedAt._seconds !== before.updatedAt._seconds) {
return false;
}
}
// if create trigger
if (!before.createdAt && after.createdAt) {
return false;
}
return true;
}
// add createdAt
if (createDoc) {
return change.after.ref.set({
createdAt: admin.firestore.FieldValue.serverTimestamp()
}, { merge: true })
.catch((e: any) => {
console.log(e);
return false;
});
}
// add updatedAt
if (updateDoc && canUpdate()) {
return change.after.ref.set({
updatedAt: admin.firestore.FieldValue.serverTimestamp()
}, { merge: true })
.catch((e: any) => {
console.log(e);
return false;
});
}
return null;
});
答案 1 :(得分:3)
要通过Cloud Function向createdAt
记录添加Post
时间戳,请执行以下操作:
exports.postsCreatedDate = functions.firestore
.document('Posts/{postId}')
.onCreate((snap, context) => {
return snap.ref.set(
{
createdAt: admin.firestore.FieldValue.serverTimestamp()
},
{ merge: true }
);
});
要向现有modifiedAt
添加Post
时间戳,可以使用以下代码。 但是,每次发布文档的某个字段发生更改(包括对createdAt
和updatedAt
字段的更改时,都会触发此Cloud功能,以无限循环 ....
exports.postsUpdatedDate = functions.firestore
.document('Posts/{postId}')
.onUpdate((change, context) => {
return change.after.ref.set(
{
updatedAt: admin.firestore.FieldValue.serverTimestamp()
},
{ merge: true }
);
});
因此,您需要比较文档的两种状态(即change.before.data()
和change.after.data()
,以检测更改是否涉及的字段不是createdAt
或updatedAt
例如,假设您的Post文档仅包含一个字段name
(不考虑两个时间戳字段),则可以执行以下操作:
exports.postsUpdatedDate = functions.firestore
.document('Posts/{postId}')
.onUpdate((change, context) => {
const newValue = change.after.data();
const previousValue = change.before.data();
if (newValue.name !== previousValue.name) {
return change.after.ref.set(
{
updatedAt: admin.firestore.FieldValue.serverTimestamp()
},
{ merge: true }
);
} else {
return false;
}
});
换句话说,恐怕您必须逐字段比较两个文档状态。...
答案 2 :(得分:1)
您不需要云功能来执行此操作。如下所示,在客户端代码中设置服务器时间戳更为简单(且便宜):
self.textbuffer.insert(self.textbuffer.get_end_iter(), '\n' + str(values))
答案 3 :(得分:0)
这就是我用来防止Firebase Firestore无限循环的原因。
与onWrite
触发器相比,我更喜欢将逻辑放在onUpdate
中
我使用npm软件包fast-deep-equal
比较传入数据和先前数据之间的更改。
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
const equal = require('fast-deep-equal/es6');
export const notificationUpdated = functions.firestore
.document('notifications/{notificationId}')
.onWrite((change, context) => {
// Get an object with the current document value.
// If the document does not exist, it has been deleted.
const document = change.after.exists ? change.after.data() : null;
// Get an object with the previous document value (for update or delete)
const oldDocument = change.before.data();
if (document && !change.before.exists) {
// This is a new document
return change.after.ref.set(
{
createdAt: admin.firestore.FieldValue.serverTimestamp(),
updatedAt: admin.firestore.FieldValue.serverTimestamp()
},
{ merge: true }
);
} else if (document && change.before.exists) {
// This is an update
// Let's check if it's only the time that has changed.
// I'll do this by making updatedAt a constant, then use `fast-deep-equal` to compare the rest
const onlyTimeChanged = equal({ ...oldDocument, updatedAt: 0 }, { ...document, updatedAt: 0 });
console.log(`Only time changed? ${onlyTimeChanged}`);
if (onlyTimeChanged) {
// The document has just been updated.
// Prevents an infinite loop
console.log('Only time has changed. Aborting...');
return false;
}
return change.after.ref.set(
{
updatedAt: admin.firestore.FieldValue.serverTimestamp()
},
{ merge: true }
);
} else if (!document && change.before.exists) {
// This is a doc delete
// Log or handle it accordingly
return false;
} else {
return false;
}
});
希望这会有所帮助
答案 4 :(得分:0)
const after = change.after.data();
const before = change.before.data();
const check = Object.keys(after).filter(key => (key !== 'createdAt') && (key !== 'updatedAt')).map(key => after[key] != before[key]);
if (check.includes(true)) {
return change.after.ref.set(
{
updatedAt: admin.firestore.FieldValue.serverTimestamp()
},
{ merge: true }
);
} else {
return false;
}
答案 5 :(得分:0)
此解决方案支持第一级子集合,基于@Jonathan's answer above:
**
* writes fields common to root-level collection records that are generated by the
* admin SDK (backend):
* - createdAt (timestamp)
* - updatedAt (timestamp)
*/
exports.createCommonFields = functions.firestore
.document('{colId}/{docId}')
.onWrite(async (change, context) => {
// the collections you want to trigger
const setCols = ['posts', 'reviews', 'comments', ];
// run the field creator if the document being touched belongs to a registered collection
if (setCols.includes(context.params.colId)) {
console.log(`collection ${context.params.colId} is not registered for this trigger`);
return null;
} else {
console.log(`running createCommonFields() for collection: ${context.params.colId}`);
}
// cause the creation of timestamp fields only
_createCommonFields(change);
});
/**
* createCommonFields' equivalent for sub-collection records
*/
exports.createCommonFieldsSubColl = functions.firestore
.document('{colId}/{colDocId}/{subColId}/{subColDocId}')
.onWrite(async (change, context) => {
console.log(`collection: ${context.params.colId}, subcollection: ${context.params.subColId}`);
// the subcollections of the collections you want to trigger
// triggers for documents like 'posts/postId/versions/versionId, etc
const setCols = {
'posts': ['versions', 'tags', 'links', ],
'reviews': ['authors', 'versions'],
'comments': ['upvotes', 'flags'],
};
// parse the collection and subcollection names of this document
const colId = context.params.colId;
const subColId = context.params.subColId;
// check that the document being triggered belongs to a registered subcollection
// e.g posts/versions; skip the field creation if it's not included
if (setCols[colId] && setCols[colId].includes(subColId)) {
console.log(`running createCommonFieldsSubColl() for this subcollection`);
} else {
console.log(`collection ${context.params.colId}/${context.params.subColId} is not registered for this trigger`);
return null;
}
// cause the creation of timestamp fields
_createCommonFields(change);
});
/**
* performs actual creation of fields that are common to the
* registered collection being written
* @param {QueryDocumentSnapshot} change a snapshot for the collection being written
*/
async function _createCommonFields(change) {
// simplify event types
const createDoc = change.after.exists && !change.before.exists;
const updateDoc = change.before.exists && change.after.exists;
const deleteDoc = change.before.exists && !change.after.exists;
if (deleteDoc) {
return null;
}
// simplify input data
const after = change.after.exists ? change.after.data() : null;
const before = change.before.exists ? change.before.data() : null;
// prevent update loops from triggers
const canUpdate = () => {
// if update trigger
if (before.updatedAt && after.updatedAt) {
if (after.updatedAt._seconds !== before.updatedAt._seconds) {
return false;
}
}
// if create trigger
if (!before.createdAt && after.createdAt) {
return false;
}
return true;
}
const currentTime = admin.firestore.FieldValue.serverTimestamp();
// add createdAt
if (createDoc) {
return change.after.ref.set({
createdAt: currentTime,
updatedAt: currentTime,
}, { merge: true })
.catch((e) => {
console.log(e);
return false;
});
}
// add updatedAt
if (updateDoc && canUpdate()) {
return change.after.ref.set({
updatedAt: currentTime,
}, { merge: true })
.catch((e) => {
console.log(e);
return false;
});
}
return null;
}