Firestore 查询错误:错误 9 失败前提条件

问题描述 投票:0回答:2

除了在 Firebase 模拟器中之外,我无法让此 Firebase 功能正常工作。

这是错误消息。它没有提到任何有关索引的内容,但我仍然怀疑错误的索引是罪魁祸首。

"Unhandled error Error: 9 FAILED_PRECONDITION: 
    at callErrorFromStatus (/workspace/node_modules/@grpc/grpc-js/build/src/call.js:31:19)
    at Object.onReceiveStatus (/workspace/node_modules/@grpc/grpc-js/build/src/client.js:357:73)
    at Object.onReceiveStatus (/workspace/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:323:181)
    at /workspace/node_modules/@grpc/grpc-js/build/src/resolving-call.js:94:78
    at process.processTicksAndRejections (node:internal/process/task_queues:77:11)
for call at
    at ServiceClientImpl.makeServerStreamRequest (/workspace/node_modules/@grpc/grpc-js/build/src/client.js:340:32)
    at ServiceClientImpl.<anonymous> (/workspace/node_modules/@grpc/grpc-js/build/src/make-client.js:105:19)
    at /workspace/node_modules/@google-cloud/firestore/build/src/v1/firestore_client.js:227:29
    at /workspace/node_modules/google-gax/build/src/streamingCalls/streamingApiCaller.js:38:28
    at /workspace/node_modules/google-gax/build/src/normalCalls/timeout.js:44:16
    at Object.request (/workspace/node_modules/google-gax/build/src/streamingCalls/streaming.js:130:40)
    at makeRequest (/workspace/node_modules/retry-request/index.js:141:28)
    at retryRequest (/workspace/node_modules/retry-request/index.js:109:5)
    at StreamProxy.setStream (/workspace/node_modules/google-gax/build/src/streamingCalls/streaming.js:121:37)
    at StreamingApiCaller.call (/workspace/node_modules/google-gax/build/src/streamingCalls/streamingApiCaller.js:54:16)
Caused by: Error
    at Query._get (/workspace/node_modules/@google-cloud/firestore/build/src/reference.js:1738:23)
    at Query.get (/workspace/node_modules/@google-cloud/firestore/build/src/reference.js:1726:21)
    at /workspace/timesheet/management.js:219:28
    at /workspace/node_modules/firebase-functions/lib/common/onInit.js:33:16
    at fixedLen (/workspace/node_modules/firebase-functions/lib/v1/providers/https.js:77:47)
    at /workspace/node_modules/firebase-functions/lib/common/providers/https.js:458:32
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  code: 9,
  details: '',
  metadata: Metadata {
    internalRepr: Map(1) { 'x-debug-tracking-id' => [Array] },
    options: {}
  }
}"

这是有问题的函数。我在尝试进行故障排除的部分添加了评论。

const filterTimesheetWorklogs = (worklogs, uid) => {
  return worklogs.filter(worklog => worklog.project.timesheetApprovers.includes(uid))
}

const isSpawnAllApproved = async (virtualTimesheetId, uid) => {
  // All timesheets with the same virtualTimesheetId with uid as one of the approvers are approved
  const collectionGroup = admin.firestore().collectionGroup("timesheets")
  const query = collectionGroup
    .where("virtualTimesheetId", "==", virtualTimesheetId)

  const snap = await query.get()

  const authorizedDocs = snap.docs.filter(async doc => {
    // const projectRef = doc.ref.parent.parent
    // const projectSnap = await projectRef.get()
    // const projectData = projectSnap.data()

    const { projectId } = doc.data()
    const projectCollectionGroup = admin.firestore().collectionGroup("projects")
    const projectQuery = projectCollectionGroup.where("timesheetApprovers", "array-contains", uid)
    const projectSnap = await projectQuery.get()
    const projectData = projectSnap.docs
      .map(doc => ({ id: doc.id, ...doc.data() }))
      .find(project => project.id === projectId)
    
    return projectData.timesheetApprovers.includes(uid)
  })

  return authorizedDocs.every(doc => {
    const data = doc.data()
    return data.status === "approved"
  })
}

const getAuthorizedVirtualTimesheets = functions.https.onCall(async (data, context) => {
  const { filter: { start, end }, companyId } = data;
  const { uid, token: { roles = {} } } = context.auth

  if ([
    Object.keys(roles).includes(companyId),
    roles[companyId].includes("timesheet approver") || roles[companyId].includes("admin"),
  ].some(v => !v)) {
    throw new functions.https.HttpsError("permission-denied", "Unauthorized access.");
  }

  const collectionGroup = admin.firestore().collectionGroup("virtualTimesheets")
  const query = collectionGroup
    // I've tried removing these two `.where`'s, error persists
    .where("createdAt", ">=", dayjs(start).startOf("day").toDate())
    .where("createdAt", "<=", dayjs(end).endOf("day").toDate())
  
  const snap = await query.get()

  // I've tried removing this entire statement and just return
  const docs = await Promise.all(snap.docs
    .map(async doc => ({
      id: doc.id,
      ...doc.data(),
      worklogs: filterTimesheetWorklogs(doc.data().worklogs, uid),
      status: (await isSpawnAllApproved(doc.id, uid)) ? "approved" : null,
    })))
  return docs
})

其他故障排除尝试:

  • 创建了一个复合索引,如下所述(不起作用):
    • 收藏ID:
      virtualTimesheets
    • 领域:
      1. createdAt
        ASC
      2. createdAt
        描述
    • 范围:收藏组
  • 删除where
    子句
    Promise.all
    语句(错误消失)。
javascript node.js firebase google-cloud-firestore google-cloud-functions
2个回答
1
投票
对我来说这是一个缺失的索引。我遵循了这个示例,您可以在 firestore 的查询生成器上构建查询。

第 1 步要了解哪个查询导致问题,请在“原因:错误”之后提到的堆栈周围查找查询,就像上面提出的问题一样。

它似乎位于 --> 或附近 --> 在 /workspace/timesheet/management.js:219:28。

步骤 2 在 firestore 上的查询生成器上运行精确查询。

Step-3 您将收到一条错误消息,提示您需要创建索引。创建该索引即可完成。

https://github.com/googleapis/nodejs-firestore/issues/1866#issuecomment-1793570113


1
投票
简短回答

复制您尝试在 Firestore 客户端 SDK 中执行的查询,部署到 Firebase,执行这些查询并检查日志以了解这些查询缺少哪些索引,构建这些索引,然后重试。

长答案

我的怀疑是正确的:如果函数中存在缺少索引的 Firestore 查询,错误消息将

不会为您提供自动生成的链接。它甚至不会告诉你它与索引相关。

因此,我必须调整一些安全规则,在 Firebase JavaScript SDK 中重新创建查询。观察错误并单击链接。然后清理所有内容并恢复安全规则。

在我的例子中,这是我复制查询的方法:

const collectionGroupRef = collectionGroup(myFirestoreInstance, "virtualTimesheets") const q = query( collectionGroup, where("createdAt", ">=", startDate), where("createdAt", "<=", endDate), ) const querySnapshot = getDocs(q)
    
© www.soinside.com 2019 - 2024. All rights reserved.