Отредактированный ответ (после пояснения в комментариях):
Структура данных:
users/{userId}/posts/
◙ post18sfge89s
- title: My first post!
- t: 1572967518
◙ post2789sdjnf
- title: I like posting
- t: 1572967560
posts/
◙ post18sfge89s
- title: My first post!
- uid: someUid1
- comments/
◙ comment237492384
...
◙ comment234234235
...
◙ post2789sdjnf
- title: I like posting
- uid: someUid1
Код функции облака:
С вышеуказанной структуройдля управления вам понадобятся две облачные функции - одна для обработки каждого нового сообщения (для копирования информации в список публикаций автора) и одна для проверки того, является ли это первое сообщение автора.
// Function: New post handler
exports.newPost = functions.firestore
.document('posts/{postId}')
.onCreate((postDocSnap, context) => {
// get relevant information
const postId = postDocSnap.id; // provided automatically
const postTitle = postDocSnap.get('title');
const userId = postDocSnap.get('uid');
const postedAt = postDocSnap.createTime; // provided automatically
// add to user's post list/index
return firestore.doc(`users/${userId}/posts/${postId}`)
.set({
title: postTitle,
t: postedAt
});
});
// Function: New post by user handler
exports.newPostByUser = functions.firestore
.document('users/{userId}/posts/{postId}')
.onCreate((postDocSnap, context) => {
// get references
const userPostsColRef = postDocSnap.ref.parent;
const userDocRef = userPostsCol.parent;
// get snapshot of user data
return userDocRef.get()
.then((userDocSnap) => {
// check if "First Post Event" has already taken place
if (userDocSnap.get('madeFirstPostEvent') != true) {
return getCollectionSize(userPostsColRef).then((length) => {
if (length == 1) {
return handleFirstPostByUser(userDocSnap, postDocSnap);
} else {
return; // could return "false" here
}
});
}
});
});
// Pseudo-function: First post by user handler
function handleFirstPostByUser(userDocSnap, postDocSnap) {
return new Promise(() => {
const postId = postDocSnap.id;
const postTitle = postDocSnap.get('title');
const userId = userDocSnap.id;
// do something
// mark event handled
return userDocSnap.ref.update({madeFirstPostEvent: true});
});
}
// returns a promise containing the length of the given collection
// note: doesn't filter out missing (deleted) documents
function getCollectionSize(colRef) {
return colRef.listDocuments()
.then(docRefArray => {
return docRefArray.length;
});
}
Исходный ответ (для сообщений, которые являются частными для каждой команды): Допущения:
- Проверка первого сообщения пользователя в конкретной команде, а не на уровне платформы.
- Неизвестная структура данных - я использовал то, что, как мне кажется, будет хорошо работать с вашей существующей структурой.
Структура данных:
Структура teamContent/
структурированатак что он может содержать вложенные коллекции для различных элементов, таких как сообщения, вложения, рисунки и т. д.
teamProfile/{teamId}/teamMemberList/{userId}/posts/
◙ post18sfge89s
- title: My first post!
- t: 1572967518
◙ post2789sdjnf
- title: I like posting
- t: 1572967560
teamContent/{teamId}/posts/
◙ post18sfge89s
- title: My first post!
- author: someUid1
- comments/
◙ comment237492384
...
◙ comment234234235
...
◙ post2789sdjnf
- title: I like posting
- author: someUid1
Код функции облака:
С приведенной выше структурой вам понадобятся две функции облака дляУправляй этим - один для обработки каждого нового сообщения (для копирования информации в список сообщений автора) и один для проверки, является ли это первое сообщение автора в этой конкретной команде.
// Function: New team post handler
exports.newPostInTeam = functions.firestore
.document('teamContent/{teamId}/posts/{postId}')
.onCreate((postDocSnap, context) => {
// get relevant information
const postId = postDocSnap.id; // provided automatically
const postTitle = postDocSnap.get('title');
const authorId = postDocSnap.get('author');
const postedAt = postDocSnap.createTime; // provided automatically
const teamId = context.params.teamId;
// add to user's post list/index
return firestore.doc(`teamProfile/${teamId}/teamMemberList/${authorId}/posts/${postId}`)
.set({
title: postTitle,
t: postedAt
});
});
// Function: New post by team member handler
exports.newPostByTeamMember = functions.firestore
.document('teamProfile/{teamId}/teamMemberList/{userId}/posts/{postId}')
.onCreate((postDocSnap, context) => {
// get references
const userPostsColRef = postDocSnap.ref.parent;
const userDocRef = userPostsCol.parent;
// get snapshot of user data
return userDocRef.get()
.then((userDocSnap) => {
// check if "First Post Event" has already taken place
if (userDocSnap.get('madeFirstPostEvent') != true) {
return getCollectionSize(userPostsColRef).then((length) => {
if (length == 1) {
return handleFirstPostInTeamEvent(userDocSnap, postDocSnap);
} else {
return; // could return "false" here
}
});
}
});
});
// Pseudo-function: First post by team member handler
function handleFirstPostInTeamEvent(userDocSnap, postDocSnap) {
return new Promise(() => {
const postId = postDocSnap.id;
const postTitle = postDocSnap.get('title');
const userId = userDocSnap.id;
// do something
// mark event handled
return userDocSnap.update({madeFirstPostEvent: true});
});
}
// returns a promise containing the length of the given collection
// note: doesn't filter out missing (deleted) documents
function getCollectionSize(colRef) {
return colRef.listDocuments()
.then(docRefArray => {
return docRefArray.length;
});
}
Примечания:
- Приведенный выше код не является полностью идемпотентным . Если несколько сообщений от одного и того же пользователя загружены одновременно, существует вероятность, что функции
handleFirstPost*
будут вызываться несколько раз. - Выше код не учитывает пропущенных документов, возвращенных из
listDocuments()
в функции getCollectionSize()
. Это не относится к приведенному выше образцу, поскольку коллекция {userId}/posts
не имеет вложенных коллекций. Будьте осторожны, если вы вызываете его в другом месте. - Не включена обработка ошибок
- Использование синтаксиса
async
/ await
может сделать его чище - Выше был написан кодбез его развертывания могут присутствовать ошибки / опечатки