# #javascript #node.js #firebase #google-cloud-firestore
Вопрос:
Окружающая среда: nodejs, firebase-admin, firestore.
Структура базы данных (пространство):
Структура базы данных (пользователь):
Создание нового пространства (пример):
// init data
const userId = "someUserId";
// Create new space
const spaceRef = await db.collection("spaces").add({ name: "SomeName" });
// Get spaceId
spaceId = spaceRef.id;
// Get user Doc for upate their spaces
const userRef = await db.collection("users").doc(userId);
// Add "spaceId" to user spaces list
userRef.collection("spaces").doc(spaceId).set({ some: "data" });
// Create collection "members" in new space with "userId"
spaceRef.collection("members").doc(userId).set({role: "OWNER"})
Вопрос: Я хочу выполнить этот код внутри одной runTransaction, но, поскольку я вижу, что транзакции поддерживают только однократное чтение и многократное обновление, это меня не устраивает, поскольку я получаю spaceId
то, что мне нужно, во время выполнения кода.
Почему я хочу использовать транзакцию: В моей структуре данных требуется связь между созданным пространством и наличием идентификатора этого пространства у пользователя. Если мы предположим, что во время выполнения этого кода произошла ошибка, например, пространство было создано, но это пространство не было добавлено внутри профиля пользователя, то это будет фатальной проблемой в структуре моей базы данных.
Как и в других базах данных, транзакции решают эту проблему, но я не могу понять, как это сделать с firestore.
Может быть, вы знаете лучший способ защитить себя от непротиворечивых данных в этом случае?
Ответ №1:
На самом деле, вам не нужна транзакция для этого, поскольку вы не читаете документы.
При db.collection("users").doc(userId);
этом вы фактически не читаете документ, а просто вызываете «локально» doc()
метод для создания DocumentReference
. Этот метод не является асинхронным, поэтому вам не нужно использовать await
. Чтобы прочитать документ, вы должны использовать асинхронный get()
метод.
Итак, использование пакетной записи, которая атомарно фиксирует все ожидающие операции записи в базу данных, сделает свое дело:
const userId = 'someUserId';
const userRef = db.collection('users').doc(userId);
const spaceRef = firestore.collection('spaces').doc();
const spaceId = spaceRef.id;
const writeBatch = firestore.batch();
writeBatch.set(spaceRef, { name: "SomeName" });
writeBatch.set(userRef.collection("spaces").doc(spaceId), { some: "data" });
writeBatch.set(spaceRef.collection("members").doc(userId), {role: "OWNER"});
await writeBatch.commit();
Вы должны включить этот код в try/catch
блок, и если пакетная фиксация завершится неудачно, вы сможете справиться с этой ситуацией в catch
блоке, зная, что ни одна запись не была зафиксирована.
Комментарии:
1. хм, я ошибочно полагал, что только после операции
.add ({name:" Some Name "})
с ответом сервера я могу получить идентификатор вновь созданного пространства. Но получается, судя по вашему примеру, что.doc ()
функция подготавливает рандомid
для ранее, вообще без запроса к серверу, и уже использует его при его создании..doc ()
Функция работает не так, как я ожидал 🙂 Спасибо.2. »
doc()
функция подготавливает случайный идентификатор для более раннего, без запроса к серверу» => это именно так!