Ошибка запроса Firestore: ошибка 9. Не выполнено предварительное условие

У меня проблемы с работой этой функции 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 АСЦ
      2. createdAt ДЕСК
    • Область применения: группа сбора
  • Удалим как предложения where, так и оператор Promise.all (ошибка исчезнет).

Я предлагаю сначала полностью исключить облачные функции и просто запустить запрос в локальной программе nodejs. Это поможет вам лучше отладить и наблюдать за проблемой. Стек вызовов ошибок предполагает, что это проблема Firestore, а не проблема облачных функций, но вы определенно усложняете ситуацию, тестируя среду облачных функций.

Doug Stevenson 28.05.2024 20:54

Я также отмечу, что здесь потенциально задействовано много разных запросов Firestore, но, похоже, вы не сделали ничего, чтобы сузить круг конкретных запросов, вызывающих проблему. Пожалуйста, сделайте это в первую очередь и покажите [минимальный полный воспроизводимый пример](stackoverflow.com/help/minimal-reproducible-example‌​), который четко иллюстрирует, какой конкретный запрос не выполнен.

Doug Stevenson 28.05.2024 20:58
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
2
107
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Короткий ответ

Реплицируйте запросы, которые вы пытаетесь выполнить, в клиентском SDK Firestore, разверните их в Firebase, выполните эти запросы и проверьте журналы на наличие индексов, которых нет в этих запросах, создайте эти индексы и повторите попытку.

Длинный ответ

Мое подозрение верно: если в функции есть запрос Firestore, в котором отсутствует индекс, сообщение об ошибке не предоставит вам ссылку для автоматического создания. Он даже не скажет вам, что это связано с индексом.

Итак, мне нужно настроить некоторые правила безопасности, воссоздать запрос в Firebase JavaScript SDK. Обратите внимание на ошибки и перейдите по ссылкам. А потом все почистить и вернуть правила безопасности.

В моем случае вот как я копирую запрос:

const collectionGroupRef = collectionGroup(myFirestoreInstance, "virtualTimesheets")
const q = query(
  collectionGroup,
  where("createdAt", "> = ", startDate),
  where("createdAt", "< = ", endDate),
)
const querySnapshot = getDocs(q)

О боже, это единственное решение. Как это единственное решение? что изменилось в SDK администратора, чтобы эту ошибку можно было увидеть, но теперь URL-адрес для создания индекса отсутствует в ошибке. Я потратил так много времени на отладку этого. Решение утомительно, особенно при наличии нескольких сред. Команда Firebase должна это исправить.

Taio 09.06.2024 21:09

У меня буквально был похожий вопрос здесь stackoverflow.com/questions/77680486/…. где SDK администратора не предоставляет URL-адрес для создания индекса, как это имеет клиентский SDK.

Taio 09.06.2024 21:11
Ответ принят как подходящий

Для меня это был отсутствующий индекс. Я последовал этому примеру, где вы можете построить свой запрос с помощью построителя запросов Firestore.

Шаг 1. Чтобы узнать, какой запрос вызывает проблему, найдите запросы вокруг стека, упомянутые после «Вызвано: ошибка», как в заданном выше вопросе.

Кажется, это где-то около --> /workspace/timesheet/management.js:219:28.

Шаг 2. Запустите точный запрос в Query Builder в Firestore.

Шаг 3. Вы получите сообщение об ошибке с сообщением о том, что вам необходимо создать индекс. Создайте этот индекс, и все готово.

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

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

starleaf1 18.07.2024 14:50

Другие вопросы по теме