Untuk seseorang yang memiliki masalah dengan populate
dan juga ingin melakukan ini:
- mengobrol dengan teks sederhana & balasan cepat (gelembung)
- 4 koleksi database untuk chatting:
clients
, users
, rooms
, messasges
.
- struktur DB pesan yang sama untuk 3 jenis pengirim: bot, pengguna & klien
refPath
atau referensi dinamis
populate
dengan path
dan model
opsi
- gunakan
findOneAndReplace
/ replaceOne
dengan$exists
- buat dokumen baru jika dokumen yang diambil tidak ada
KONTEKS
Tujuan
- Simpan pesan teks sederhana baru ke database & isi dengan data pengguna atau klien (2 model berbeda).
- Simpan pesan quickReplies baru ke database dan isi dengan data pengguna atau klien.
- Simpan setiap pesan jenis pengirimnya:
clients
, users
& bot
.
- Isi hanya pesan yang memiliki pengirim
clients
atau users
dengan Model Mongoose-nya. _sender type client models is clients
, for user is users
.
Skema pesan :
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);
LARUTAN
Permintaan API sisi server saya
Kode saya
Fungsi utilitas (dalam chatUtils.js
file) untuk mendapatkan jenis pesan yang ingin Anda simpan:
/**
* 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
};
Sisi server saya (menggunakan Nodejs) untuk mendapatkan permintaan menyimpan pesan:
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);
}
});
TIPS :
Untuk database:
- Setiap pesan adalah dokumen itu sendiri.
- Alih-alih menggunakan
refPath
, kami menggunakan util getSenderModel
yang digunakan di populate()
. Ini karena bot. The sender.type
dapat: users
dengan database-nya, clients
dengan database dan bot
tanpa database. The refPath
perlu referensi Model yang benar, jika tidak, Mongooose melempar kesalahan.
sender._id
dapat berupa tipe ObjectId
untuk pengguna dan klien, atau null
untuk bot.
Untuk logika permintaan API:
- Kami mengganti
quickReply
pesan (Message DB harus memiliki hanya satu quickReply, tetapi sebanyak yang Anda inginkan). Kami menggunakan findOneAndUpdate
alih - alih replaceOne
atau findOneAndReplace
.
- Kami menjalankan operasi kueri (the
findOneAndUpdate
) dan populate
operasi dengan callback
masing-masing. Hal ini penting jika Anda tidak tahu apakah penggunaan async/await
, then()
, exec()
atau callback(err, document)
. Untuk info lebih lanjut lihat Populate Doc .
- Kami mengganti pesan balasan cepat dengan
overwrite
opsi dan tanpa $set
operator kueri.
- Jika kami tidak menemukan balasan cepat, kami membuat yang baru. Anda harus memberitahu Mongoose ini dengan
upsert
opsi.
- Kami hanya mengisi satu kali, untuk pesan yang diganti atau pesan baru yang disimpan.
- Kami kembali ke callback, apa pun pesan yang telah kami simpan dengan
findOneAndUpdate
dan untuk populate()
.
- Di
populate
, kami membuat referensi Model dinamis kustom dengan getSenderModel
. Kita dapat menggunakan referensi dinamis Mongoose karena sender.type
for bot
tidak memiliki Model Mongoose. Kami menggunakan Populating Across Database dengan model
dan path
optins.
Saya telah menghabiskan banyak waktu untuk memecahkan masalah kecil di sana-sini dan saya harap ini akan membantu seseorang! 😃