Đối với một người có vấn đề với populate
và cũng muốn làm điều này:
- trò chuyện bằng văn bản đơn giản và trả lời nhanh (bong bóng)
- 4 bộ sưu tập cơ sở dữ liệu cho chat:
clients
, users
, rooms
, messasges
.
- cùng một cấu trúc DB thông báo cho 3 loại người gửi: bot, người dùng & máy khách
refPath
hoặc tham chiếu động
populate
với path
và model
các tùy chọn
- sử dụng
findOneAndReplace
/ replaceOne
với$exists
- tạo một tài liệu mới nếu tài liệu tìm nạp không tồn tại
BỐI CẢNH
Mục tiêu
- Lưu một tin nhắn văn bản đơn giản mới vào cơ sở dữ liệu và điền nó với dữ liệu người dùng hoặc khách hàng (2 kiểu máy khác nhau).
- Lưu một tin nhắn quickReplies mới vào cơ sở dữ liệu và đưa nó vào dữ liệu người dùng hoặc khách hàng.
- Lưu mỗi tin nhắn gửi loại của nó:
clients
, users
& bot
.
- Chỉ điền các tin nhắn có người gửi
clients
hoặc users
bằng các Mô hình Mongoose của nó. _sender type client model is clients
, for user is users
.
Lược đồ thông báo :
const messageSchema = new Schema({
room: {
type: Schema.Types.ObjectId,
ref: 'rooms',
required: [true, `Room's id`]
},
sender: {
_id: { type: Schema.Types.Mixed },
type: {
type: String,
enum: ['clients', 'users', 'bot'],
required: [true, 'Only 3 options: clients, users or bot.']
}
},
timetoken: {
type: String,
required: [true, 'It has to be a Nanosecond-precision UTC string']
},
data: {
lang: String,
// Format samples on https://docs.chatfuel.com/api/json-api/json-api
type: {
text: String,
quickReplies: [
{
text: String,
// Blocks' ids.
goToBlocks: [String]
}
]
}
}
mongoose.model('messages', messageSchema);
GIẢI PHÁP
Yêu cầu API phía máy chủ của tôi
Mã của tôi
Chức năng tiện ích (trên chatUtils.js
tệp) để nhận loại thông báo bạn muốn lưu:
/**
* We filter what type of message is.
*
* @param {Object} message
* @returns {string} The type of message.
*/
const getMessageType = message => {
const { type } = message.data;
const text = 'text',
quickReplies = 'quickReplies';
if (type.hasOwnProperty(text)) return text;
else if (type.hasOwnProperty(quickReplies)) return quickReplies;
};
/**
* Get the Mongoose's Model of the message's sender. We use
* the sender type to find the Model.
*
* @param {Object} message - The message contains the sender type.
*/
const getSenderModel = message => {
switch (message.sender.type) {
case 'clients':
return 'clients';
case 'users':
return 'users';
default:
return null;
}
};
module.exports = {
getMessageType,
getSenderModel
};
Phía máy chủ của tôi (sử dụng Nodejs) để nhận yêu cầu lưu thông báo:
app.post('/api/rooms/:roomId/messages/new', async (req, res) => {
const { roomId } = req.params;
const { sender, timetoken, data } = req.body;
const { uuid, state } = sender;
const { type } = state;
const { lang } = data;
// For more info about message structure, look up Message Schema.
let message = {
room: new ObjectId(roomId),
sender: {
_id: type === 'bot' ? null : new ObjectId(uuid),
type
},
timetoken,
data: {
lang,
type: {}
}
};
// ==========================================
// CONVERT THE MESSAGE
// ==========================================
// Convert the request to be able to save on the database.
switch (getMessageType(req.body)) {
case 'text':
message.data.type.text = data.type.text;
break;
case 'quickReplies':
// Save every quick reply from quickReplies[].
message.data.type.quickReplies = _.map(
data.type.quickReplies,
quickReply => {
const { text, goToBlocks } = quickReply;
return {
text,
goToBlocks
};
}
);
break;
default:
break;
}
// ==========================================
// SAVE THE MESSAGE
// ==========================================
/**
* We save the message on 2 ways:
* - we replace the message type `quickReplies` (if it already exists on database) with the new one.
* - else, we save the new message.
*/
try {
const options = {
// If the quickRepy message is found, we replace the whole document.
overwrite: true,
// If the quickRepy message isn't found, we create it.
upsert: true,
// Update validators validate the update operation against the model's schema.
runValidators: true,
// Return the document already updated.
new: true
};
Message.findOneAndUpdate(
{ room: roomId, 'data.type.quickReplies': { $exists: true } },
message,
options,
async (err, newMessage) => {
if (err) {
throw Error(err);
}
// Populate the new message already saved on the database.
Message.populate(
newMessage,
{
path: 'sender._id',
model: getSenderModel(newMessage)
},
(err, populatedMessage) => {
if (err) {
throw Error(err);
}
res.send(populatedMessage);
}
);
}
);
} catch (err) {
logger.error(
`#API Error on saving a new message on the database of roomId=${roomId}. ${err}`,
{ message: req.body }
);
// Bad Request
res.status(400).send(false);
}
});
MẸO :
Đối với cơ sở dữ liệu:
- Mỗi tin nhắn là một tài liệu chính nó.
- Thay vì sử dụng
refPath
, chúng tôi sử dụng tiện ích getSenderModel
được sử dụng trên populate()
. Điều này là do bot. Có sender.type
thể là: users
với cơ sở dữ liệu của anh ấy, clients
với cơ sở dữ liệu của anh ấy và bot
không có cơ sở dữ liệu. Các refPath
nhu cầu tham khảo mẫu thật, nếu không, Mongooose ném ra một lỗi.
sender._id
có thể là loại ObjectId
cho người dùng và khách hàng, hoặc null
cho bot.
Đối với logic yêu cầu API:
- Chúng tôi thay thế
quickReply
thông báo (Message DB chỉ có một quickReply, nhưng có nhiều tin nhắn văn bản đơn giản như bạn muốn). Chúng tôi sử dụng findOneAndUpdate
thay thế cho replaceOne
hoặc findOneAndReplace
.
- Chúng tôi thực hiện thao tác truy vấn (sự
findOneAndUpdate
) và populate
thao tác với callback
mỗi thao tác. Điều này rất quan trọng nếu bạn không biết nếu sử dụng async/await
,then()
, exec()
hoặc callback(err, document)
. Để biết thêm thông tin, hãy xem Tài liệu phổ biến .
- Chúng tôi thay thế tin nhắn trả lời nhanh bằng
overwrite
tùy chọn và không có $set
toán tử truy vấn.
- Nếu không tìm thấy câu trả lời nhanh, chúng tôi sẽ tạo một câu trả lời mới. Bạn phải nói với Mongoose điều này với
upsert
tùy chọn.
- Chúng tôi chỉ điền một lần cho tin nhắn được thay thế hoặc tin nhắn mới được lưu.
- Chúng tôi quay lại các cuộc gọi lại, bất kể thông điệp nào chúng tôi đã lưu
findOneAndUpdate
và chopopulate()
.
- Trong
populate
, chúng tôi tạo một tham chiếu Mô hình động tùy chỉnh với getSenderModel
. Chúng ta có thể sử dụng tài liệu tham khảo động Mongoose vì sender.type
cho bot
có không bất kỳ mẫu Mongoose. Chúng tôi sử dụng Cơ sở dữ liệu phổ biến trên toàn bộ với model
và path
tùy chọn.
Tôi đã dành rất nhiều giờ để giải quyết các vấn đề nhỏ ở đây và ở đó và tôi hy vọng điều này sẽ giúp ích cho ai đó! 😃