【问题标题】:How to re-order Firestore 'modified' changes using DocumentChange.newIndex?如何使用 DocumentChange.newIndex 重新排序 Firestore 的“修改”更改?
【发布时间】:2017-12-30 13:19:35
【问题描述】:

我正在使用 Firestore 的 Web API 来执行一个简单的查询,该查询对格式化为字符串 ('2017-12-30') 的日期属性进行排序。我使用 onSnapshot() 方法作为侦听器订阅文档更改。结果列表的初始填充按预期工作 - 顺序正确。

当我对数据进行更改时,回调会被调用,更改类型为“已修改”。如果任何更改影响日期属性,那么我无法重新排序结果列表中的项目 - 与旧的实时数据库不同。也就是说,直到我看到 DocumentChange 的 newIndex 和 oldIndex 属性。它们未记录在 Web API (https://firebase.google.com/docs/reference/js/firebase.firestore.DocumentChange) 中,但作为 Node.js API (https://cloud.google.com/nodejs/docs/reference/firestore/0.10.x/DocumentChange) 的一部分记录在案。

所以,我的问题似乎得到了解决 - 除了实际上 newIndex 和 oldIndex 中的值似乎在很大程度上是随机的,并且如果我刷新查询,则与实际顺序无关。我无法找出任何可以解释我返回的索引值的模式。

有人成功使用过 DocumentChange.newIndex 和 DocumentChange.oldIndex 吗?如果不是,您将如何对更改后的订阅者结果进行重新排序?

const query = firestore.collection(`users/${uid}/things`).
  orderBy('sortDate', 'desc').limit(1000)
query.onSnapshot(snapshot => {
  snapshot.docChanges.forEach(change => {
    if (change.type === "added") {
      dispatch(addThing({
        id: change.doc.id, 
        ...change.doc.data()
      }, change.newIndex)
    }
    if (change.type === "modified") {
      dispatch(changeThing({
        id: change.doc.id, 
        ...change.doc.data()
      }, change.oldIndex, change.newIndex))
    }
    if (change.type === "removed") {
      dispatch(removeThing(change.doc.id, change.oldIndex))
    }
  })
})

【问题讨论】:

  • 我使用索引来保持本地数组与服务器上的数据同步并且没有任何问题。不过,这里很难看出“随机”是什么意思。您能否展示一些示例数据、您对该数据所做的更改以及您为该更改获得的事件(包括索引)?
  • 抱歉耽搁了;我花了一些时间在一个测试用例上工作,效果很好。然后我确定在我的原始代码中 (a) 在我的添加逻辑中有一个错误,它颠倒了添加的顺序元素;这掩盖了我的排序顺序错误的错误(b)。实际上,这意味着进来的索引是针对查询的相反排序顺序的。这个混乱的代码中途从 Realtime DB 迁移到 Firestore。无论如何,感谢您的帮助。我会将测试用例作为答案发布,以防它对其他人有所帮助。
  • 很高兴听到您发现问题 Alex。感谢您分享它作为解决方案!

标签: javascript firebase google-cloud-firestore


【解决方案1】:

DocumentChange 索引最初的问题是由于我的代码中其他地方的几个错误。由于我没有在 Node.js Firestore 文档之外找到任何使用此示例的示例,因此这是我用来验证其正确行为的测试代码(ES6)。它假定firebase 已被初始化。

cleanTestData = (firestore, path) => {
  console.log("Cleaning-up old test data")
  var query = firestore.collection(path)
  return query.get().then(snapshot => {

    const deletePromises = []
    if (snapshot.size > 0) {
      snapshot.docs.forEach(function(doc) {
        deletePromises.push(doc.ref.delete().then(() => {
          console.log("Deleted ", doc.id)
        }))
      });
    }
    return Promise.all(deletePromises)
  }).then(() => {
    console.log("Old test data cleaned-up")
  })
}

createTestData = (firestore, path) => {
  console.log("Creating test data")
  const batch = firestore.batch()    
  const data = { 
    a: '2017-09-02',
    b: '2017-12-25',
    c: '2017-10-06',
    d: '2017-08-02',
    e: '2017-09-20',
    f: '2017-11-17' 
  }
  for (const id in data) {
    batch.set(firestore.collection(path).doc(id), { date: data[id] })
  }

  return batch.commit().then(() => {
    console.log("Test data created");
  }).catch(error => {
    console.error("Failed to create test data: ", error);
  })
}

subscribe = (firestore, path) => {
  const datesArray = []

  return firestore.collection(path).orderBy('date', 'asc').onSnapshot(snapshot => {  
    snapshot.docChanges.forEach(change => {
      console.log(change.type, "id:", change.doc.id, 
        "; date:", change.doc.data().date, 
        "; oldIndex:", change.oldIndex, "; newIndex:", change.newIndex,
        "; metadata: ", change.doc.metadata)
      if (change.oldIndex !== -1) {
        datesArray.splice(change.oldIndex, 1);
      }
      if (change.newIndex !== -1) {
        datesArray.splice(change.newIndex, 0, change.doc.data().date);
      }
      console.log("    -->", JSON.stringify(datesArray))
    })
  })
}

update = (firestore, path) => {
  console.log("Updating test data")
  return firestore.collection(path).doc('d').set({date: '2018-01-02'}).then(() => {
    console.log("Test doc 'd' updated from '2017-08-02' to '2018-01-02'")
  })
}

query = (firestore, path) => {
  var query = firestore.collection(path).orderBy('date', 'asc')
  return query.get().then(snapshot => {

    const dates = []
    if (snapshot.size > 0) {
      snapshot.docs.forEach(function(doc) {
        dates.push(doc.data().date)
      });
    }

    console.log("Fresh query of data: \n    -->", JSON.stringify(dates))
  })
}

handleStartTest = e => {
  console.log("Starting test")

  const firestore = firebase.firestore()
  const path = `things`
  let unsubscribeFn = null

  unsubscribeFn = this.subscribe(firestore, path)
  this.cleanTestData(firestore, path).then(() => {
    return this.createTestData(firestore, path)
  }).then(() => {
    return this.update(firestore, path)
  }).then(() => {
    return this.query(firestore, path)
  }).then(() => {
    unsubscribeFn()
    console.log("Test complete")
  }).catch((error) => {
    console.error("Test failed: ", error)
  })
}

【讨论】:

    【解决方案2】:

    这就是它为我工作的方式:

    onSnapshot((ref) => {
      ref.docChanges().forEach((change) => {
        const { newIndex, oldIndex, doc, type } = change;
        if (type === 'added') {
          this.todos.splice(newIndex, 0, doc.data());
          // if we want to handle references we would do it here
        } else if (type === 'modified') {
          // remove the old one first
          this.todos.splice(oldIndex, 1);
          // if we want to handle references we would have to unsubscribe
          // from old references' listeners and subscribe to the new ones
          this.todos.splice(newIndex, 0, doc.data());
        } else if (type === 'removed') {
          this.todos.splice(oldIndex, 1);
          // if we want to handle references we need to unsubscribe
          // from old references
        }
      });
    });
    

    source

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-05-08
      • 2019-02-01
      • 1970-01-01
      • 2019-02-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多