สำหรับคนที่มีปัญหาpopulate
และต้องการทำสิ่งนี้:
- แชทด้วยข้อความธรรมดาและการตอบกลับด่วน (ฟองอากาศ)
- 4
clients
คอลเลกชันฐานข้อมูลสำหรับการสนทนา: users
, rooms
, messasges
,
- โครงสร้างฐานข้อมูลข้อความเดียวกันสำหรับผู้ส่ง 3 ประเภท: บอทผู้ใช้และลูกค้า
refPath
หรือการอ้างอิงแบบไดนามิก
populate
ด้วยpath
และmodel
ตัวเลือก
- ใช้
findOneAndReplace
/ replaceOne
กับ$exists
- สร้างเอกสารใหม่หากไม่มีเอกสารที่ดึงมา
บริบท
เป้าหมาย
- บันทึกข้อความธรรมดาใหม่ลงในฐานข้อมูลและเติมข้อมูลด้วยข้อมูลผู้ใช้หรือไคลเอนต์ (2 รุ่นที่แตกต่างกัน)
- บันทึกข้อความ QuickReplies ใหม่ไปยังฐานข้อมูลและเติมข้อมูลด้วยข้อมูลผู้ใช้หรือไคลเอ็นต์
- บันทึกแต่ละข้อความประเภทผู้ส่ง:
clients
, users
& bot
.
- กรอกเฉพาะข้อความที่มีผู้ส่ง
clients
หรือusers
มีพังพอนโมเดล รุ่นประเภทลูกค้า _sender คือสำหรับผู้ใช้ clients
users
สคีมาข้อความ :
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);
สารละลาย
คำขอ API ฝั่งเซิร์ฟเวอร์ของฉัน
รหัสของฉัน
ฟังก์ชันยูทิลิตี้ (ในchatUtils.js
ไฟล์) เพื่อรับประเภทข้อความที่คุณต้องการบันทึก:
/**
* 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
};
ฝั่งเซิร์ฟเวอร์ของฉัน (โดยใช้ Nodejs) เพื่อรับคำขอบันทึกข้อความ:
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);
}
});
เคล็ดลับ :
สำหรับฐานข้อมูล:
- ทุกข้อความเป็นเอกสารเอง
- แทนการใช้
refPath
เราจะใช้ util ที่ใช้บนgetSenderModel
populate()
นี่เป็นเพราะบอท sender.type
สามารถ: users
กับฐานข้อมูลของเขาclients
กับฐานข้อมูลของเขาและbot
โดยไม่ต้องฐานข้อมูล refPath
อ้างอิงความต้องการที่แท้จริงของรุ่นถ้าไม่ Mongooose โยนความผิดพลาด
sender._id
สามารถเป็นประเภทObjectId
สำหรับผู้ใช้และลูกค้าหรือnull
สำหรับบอท
สำหรับตรรกะการร้องขอ API:
- เราแทนที่
quickReply
ข้อความ (ฐานข้อมูลข้อความต้องมี QuickReply เพียงข้อความเดียว แต่มีข้อความธรรมดามากเท่าที่คุณต้องการ) เราใช้findOneAndUpdate
แทนreplaceOne
หรือfindOneAndReplace
.
- เราดำเนินการค้นหา (the
findOneAndUpdate
) และการpopulate
ดำเนินการกับcallback
แต่ละคำสั่ง นี้เป็นสิ่งสำคัญถ้าคุณไม่ทราบว่าการใช้งานasync/await
, then()
, หรือexec()
callback(err, document)
สำหรับข้อมูลเพิ่มเติมดูเติมหมอ
- เราแทนที่ข้อความตอบกลับด่วนด้วย
overwrite
ตัวเลือกและไม่มี$set
ตัวดำเนินการสืบค้น
- หากเราไม่พบคำตอบด่วนเราจะสร้างคำตอบใหม่ คุณต้องบอกกับพังพอนด้วย
upsert
ตัวเลือกนี้
- เราเติมข้อมูลเพียงครั้งเดียวสำหรับข้อความที่ถูกแทนที่หรือข้อความที่บันทึกใหม่
- เรากลับไปที่การโทรกลับไม่ว่าจะเป็นข้อความใดก็ตามที่เราบันทึกไว้
findOneAndUpdate
และสำหรับไฟล์populate()
.
- ใน
populate
นั้นเราสร้างการอ้างอิงโมเดลไดนามิกที่กำหนดเองด้วยไฟล์getSenderModel
. เราสามารถใช้การอ้างอิงแบบไดนามิกของพังพอนได้เนื่องจากsender.type
for bot
ไม่มี Mongoose Model เราใช้Populating Across Databaseกับmodel
และpath
optins
ฉันใช้เวลาหลายชั่วโมงในการแก้ปัญหาเล็กน้อยที่นี่และที่นั่นและฉันหวังว่านี่จะช่วยใครบางคนได้! 😃