Für jemanden, der das Problem hat populate
und dies auch tun möchte:
- chatte mit einfachem Text & schnellen Antworten (Blasen)
- 4 Datenbank Sammlungen Chat:
clients
, users
, rooms
, messasges
.
- Gleiche Nachrichten-DB-Struktur für 3 Arten von Absendern: Bot, Benutzer und Clients
refPath
oder dynamische Referenz
populate
mit path
und model
Optionen
- benutze
findOneAndReplace
/ replaceOne
mit$exists
- Erstellen Sie ein neues Dokument, wenn das abgerufene Dokument nicht vorhanden ist
KONTEXT
Tor
- Speichern Sie eine neue einfache Textnachricht in der Datenbank und füllen Sie sie mit den Benutzer- oder Clientdaten (2 verschiedene Modelle).
- Speichern Sie eine neue quickReplies-Nachricht in der Datenbank und füllen Sie sie mit den Benutzer- oder Clientdaten.
- Speichern Sie jede Nachricht mit ihrem Absendertyp :
clients
, users
& bot
.
- Füllen Sie nur die Nachrichten aus, die den Absender
clients
oder users
seine Mungomodelle haben. _sender Typ Client-Modelle ist clients
, für Benutzer ist users
.
Nachrichtenschema :
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);
LÖSUNG
Meine serverseitige API-Anfrage
Mein Code
Dienstprogrammfunktion (in chatUtils.js
Datei), um den Nachrichtentyp abzurufen, den Sie speichern möchten:
/**
* 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
};
Meine Serverseite (mit Nodejs), um die Anforderung zum Speichern der Nachricht zu erhalten:
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);
}
});
TIPPS :
Für die Datenbank:
- Jede Nachricht ist ein Dokument.
- Anstatt zu verwenden
refPath
, verwenden wir das util getSenderModel
, das für verwendet wird populate()
. Das liegt am Bot. Das sender.type
kann sein: users
mit seiner Datenbank, clients
mit seiner Datenbank und bot
ohne Datenbank. Die refPath
braucht echte Modellreferenz, wenn nicht, werfen Mongooose einen Fehler.
sender._id
kann ObjectId
für Benutzer und Clients oder null
für den Bot eingegeben werden.
Für die API-Anforderungslogik:
- Wir ersetzen die
quickReply
Nachricht (Nachrichten-DB muss nur eine schnelle Antwort haben, aber so viele einfache Textnachrichten, wie Sie möchten). Wir verwenden das findOneAndUpdate
anstelle von replaceOne
oder findOneAndReplace
.
- Wir führen die Abfrageoperation (die
findOneAndUpdate
) und die populate
Operation mit der callback
von jedem aus. Dies ist wichtig , wenn Sie nicht wissen , ob die Verwendung async/await
, then()
, exec()
oder callback(err, document)
. Weitere Informationen finden Sie im Populate Doc .
- Wir ersetzen die Schnellantwortnachricht durch die
overwrite
Option und ohne $set
Abfrageoperator.
- Wenn wir die schnelle Antwort nicht finden, erstellen wir eine neue. Sie müssen Mongoose dies mit
upsert
Option mitteilen .
- Wir füllen nur einmal für die ersetzte Nachricht oder die neu gespeicherte Nachricht.
- Wir kehren zu Rückrufen zurück, unabhängig von der Nachricht, die wir mit
findOneAndUpdate
und für die gespeichert haben populate()
.
- In
populate
erstellen wir eine benutzerdefinierte dynamische Modellreferenz mit dem getSenderModel
. Wir können die dynamische Mungo-Referenz verwenden, da das sender.type
for bot
kein Mungo-Modell hat. Wir verwenden eine bevölkerungsübergreifende Datenbank mit model
und path
optins.
Ich habe viele Stunden damit verbracht, hier und da kleine Probleme zu lösen, und ich hoffe, das wird jemandem helfen! 😃