Функции Firebase на круговой задаче обновления - PullRequest
14 голосов
/ 29 мая 2019

У меня такая ситуация с круговой функцией, возникают проблемы с поиском решения.

У меня есть коллекция, где у меня есть флаг, который сообщает, изменились ли данные. Также хочу внести изменения.

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
  })

}

Проблема заключается в том, что при записи флага ОБНОВЛЕНО дважды записывается журнал. Но также нельзя поместить запись журнала в инструкцию ELSE, поскольку флаг уже может быть ОБНОВЛЕН, и необходимо выполнить обновление нового документа, поэтому необходимо записать новый журнал.

Trigger:

import * as landFunctions from './lands/index'
export const landWrite = functions.firestore
.document('land/{land}')
.onWrite((change, context) => {
  return landFunctions.landWrite(change, context)
})

Ответы [ 2 ]

4 голосов
/ 02 июля 2019

Если я правильно понимаю, проблема в том, что флаг updated не указывает , на какое событие приходит обновление (поскольку вы не можете сделать это с boolean),Другими словами - у вас может быть несколько одновременных записей «первого этапа» в lands, и вам нужен способ устранить их неоднозначность.

Вот несколько возможных вариантов, которые я бы попробовал - из (ИМХО) худшегок лучшему:

  • Первый вариант не очень элегантен для реализации
  • Первые и вторые параметры оба приводят к тому, что ваша функция становитсявызывается дважды.
  • Третий вариант означает, что ваша функция вызывается только один раз, однако вы должны поддерживать отдельный параллельный документ / коллекцию вместе с lands.

Вариант 1

Сохраните какой-то уникальный идентификатор в поле updated (например, хэш строкового события JSON - например, hash(JSON.stringify(oldDocument)) или пользовательский идентификатор события [если он у вас есть]).

Опция 2

Попробуйте проверить свойство updateMask входящего события и отбросьте все события записи, которые только влияет на это свойство.

Вариант 3

Сохраните вашиукажите состояние в другом пути / коллекции документов (например, в коллекции landUpdates на том же уровне, что и в вашей коллекции lands), и настройте облачную функцию так, чтобы она не срабатывала по этому пути.(Если вам нужно, вы всегда можете создать секундную облачную функцию, которая вызывает срабатывание по пути landUpdates и добавить либо ту же логику , либо другую логикук нему.)

Надеюсь, это поможет!

2 голосов
/ 29 июня 2019

Основная проблема здесь заключается в невозможности дифференцировать изменения, сделанные этой серверной функцией или клиентом.Всякий раз, когда вы находитесь в этой ситуации, вы должны попытаться провести четкое различие между ними.Вы можете даже подумать о наличии дополнительного поля, например 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
  })

}

Редактировать

У меня была точная проблема, и мне пришлось различать изменения по серверу и клиентуи игнорировать те, которые были с сервера.Я надеюсь, что вы попробуете мое предложение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...