Функции Firebase на круговой задаче обновления
У меня такая ситуация с круговой функцией, возникают проблемы с поиском решения.
Есть коллекция, где у меня есть флаг, который сообщает, изменились ли данные. Также хочу внести изменения.
export async function landWrite(change, context) {
const newDocument = change.after.exists ? change.after.data() : null
const oldDocument = change.before.data()
const log = {
time: FieldValue.serverTimestamp(),
oldDocument: oldDocument,
newDocument: newDocument
}
const landid = change.after.id
const batch = db.batch()
const updated = newDocument && newDocument.updated === oldDocument.updated
if (!updated) {
const landRef = db.collection('land').doc(landid)
batch.update(landRef, {'updated': true })
}
const logRef = db.collection('land').doc(landid).collection('logs').doc()
batch.set(logRef, log)
return batch.commit()
.then(success => {
return true
})
.catch(error => {
return error
})
}
Проблема состоит в том, что это записывает журнал дважды, когда флаг UPDATED имеет значение false. Но также нельзя поместить запись журнала в инструкцию ELSE, поскольку флаг уже может быть ОБНОВЛЕН, и необходимо выполнить обновление нового документа, поэтому необходимо записать новый журнал.
Спусковой крючок:
import * as landFunctions from './lands/index'
export const landWrite = functions.firestore
.document('land/{land}')
.onWrite((change, context) => {
return landFunctions.landWrite(change, context)
})
Ответы
Ответ 1
Основной проблемой здесь является невозможность дифференцировать изменения, которые вносятся этой серверной функцией или клиентом. Всякий раз, когда вы находитесь в этой ситуации, вы должны попытаться провести четкое различие между ними. Вы можете даже подумать о том, чтобы иметь дополнительное поле, например fromServer: true
которое идет с обновлениями сервера и помогает серверу игнорировать связанный триггер. Сказав это, я думаю, что определил проблему и дал четкое решение ниже.
Эта строка вводит в заблуждение:
const updated = newDocument && newDocument.updated === oldDocument.updated
Должен быть назван:
const updateStatusDidNotChange = newDocument && newDocument.updated === oldDocument.updated
Я понимаю, что вы хотите, чтобы обновленный флаг управлялся этой функцией, а не клиентом. Дайте мне знать, если это не так.
Поэтому поле обновления изменяется только в этой функции. Поскольку вы хотите регистрировать только изменения, сделанные вне этой функции, вы хотите регистрировать только, когда обновление не изменилось.
Вот моя попытка исправить ваш код в этом свете:
export async function landWrite(change, context) {
const newDocument = change.after.exists ? change.after.data() : null
const oldDocument = change.before.data()
const updateStatusDidNotChange = newDocument && newDocument.updated === oldDocument.updated
if (!updateStatusDidNotChange) return true; //this was a change made by me, ignore
const batch = db.batch()
if (!oldDocument.updated) {
const landid = change.after.id
const landRef = db.collection('land').doc(landid)
batch.update(landRef, {'updated': true })
}
const log = {
time: FieldValue.serverTimestamp(),
oldDocument: oldDocument,
newDocument: newDocument
}
const logRef = db.collection('land').doc(landid).collection('logs').doc()
batch.set(logRef, log)
return batch.commit()
.then(success => {
return true
})
.catch(error => {
return error
})
}
редактировать
У меня была точная проблема, и я должен был различать изменения по серверу и клиенту и игнорировать те, которые были с сервера. Я надеюсь, что вы попробуете мое предложение.
Ответ 2
Если я правильно понимаю, проблема в том, что updated
флаг не указывает, на какое событие происходит обновление. Другими словами - у вас может быть несколько одновременных записей "первого этапа" в lands
, и вам нужен способ устранения их неоднозначности.
Вот несколько возможных вариантов, которые я бы попробовал - от (ИМХО) худшего к лучшему:
- Первый вариант не очень элегантен в реализации
- Первый и второй варианты приводят к тому, что ваша функция вызывается дважды.
- Третий вариант означает, что ваша функция вызывается только один раз, однако вы должны поддерживать отдельный параллельный документ/коллекцию вместе с
lands
.
Опция 1
Сохраните некоторый уникальный идентификатор в updated
поле (например, хэш строкового события JSON - например, hash(JSON.stringify(oldDocument))
или пользовательский идентификатор события [если он у вас есть]).
Вариант 2
Попробуйте проверить свойство updateMask
входящего события и отменить все события записи, которые влияют только на это свойство.
Вариант 3
Сохраните свой статус обновления в другом пути/коллекции документов (например, в коллекции landUpdates
на том же уровне, что и в вашей коллекции lands
) и настройте свою облачную функцию так, чтобы она не срабатывала по этому пути. (Если вам нужно, вы всегда можете создать вторую landUpdates
функцию, которая landUpdates
пути landUpdates
и добавить к ней либо ту же логику, либо другую логику.)
Надеюсь это поможет!