문제가 populate
있고이 작업을 수행하려는 사람 :
- 간단한 텍스트 및 빠른 답장 (거품)으로 채팅
- 채팅의 4 개 데이터베이스 컬렉션 :
clients
, users
, rooms
,messasges
.
- 봇, 사용자 및 클라이언트의 3 가지 유형의 발신자에 대해 동일한 메시지 DB 구조
refPath
또는 동적 참조
populate
로 path
와model
옵션
- 사용
findOneAndReplace
/ replaceOne
함께$exists
- 가져온 문서가없는 경우 새 문서를 만듭니다.
문맥
골
- 새로운 단순 텍스트 메시지를 데이터베이스에 저장하고 사용자 또는 클라이언트 데이터 (2 가지 다른 모델)로 채 웁니다.
- 새 quickReplies 메시지를 데이터베이스에 저장하고 사용자 또는 클라이언트 데이터로 채 웁니다.
- 각 메시지의 발신자 유형 저장 :
clients
, users
&bot
.
- 보낸 사람
clients
또는 users
Mongoose 모델 이있는 메시지 만 채 웁니다 . _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
사용되는 유틸리티 getSenderModel
를 사용합니다 populate()
. 이것은 봇 때문입니다. 이 sender.type
될 수 있습니다 users
자신의 데이터베이스와, clients
그의 데이터베이스 및 bot
데이터베이스없이. 은 refPath
하지 않을 경우, Mongooose이 오류가 발생, 진정한 모델의 참조를 필요로한다.
sender._id
ObjectId
사용자 및 클라이언트 또는 null
봇에 대한 유형일 수 있습니다 .
API 요청 로직의 경우 :
quickReply
메시지를 대체합니다 (메시지 DB에는 빠른 응답이 하나만 있어야하지만 원하는만큼의 간단한 문자 메시지가 있음). 우리는 findOneAndUpdate
대신 replaceOne
또는findOneAndReplace
.
- 쿼리 작업 (
findOneAndUpdate
)과 각각 의 populate
작업을 실행합니다 callback
. 당신이 사용하는 경우 모르는 경우에 중요하다 async/await
, then()
, exec()
또는 callback(err, document)
. 자세한 내용은 문서 채우기 를 참조하십시오. .
- 빠른 답장 메시지를
overwrite
옵션과 함께 대체합니다.$set
쿼리 연산자 바꿉니다.
- 빠른 답장을 찾지 못하면 새 답장을 만듭니다. 몽구스에게
upsert
옵션 .
- 교체 된 메시지 또는 새로 저장된 메시지에 대해 한 번만 채 웁니다.
- 우리는 우리가 저장 한 메시지가 무엇이든, 콜백으로 되돌아
findOneAndUpdate
와 대한 populate()
.
- 에서
populate
, 우리는있는 사용자 정의 동적 모델의 참조를 생성 getSenderModel
. sender.type
for bot
에는 Mongoose 모델이 없기 때문에 Mongoose 동적 참조를 사용할 수 있습니다 . 우리는 및 옵틴 과 함께 데이터베이스 전체 채우기를 사용합니다 .model
path
나는 여기저기서 작은 문제를 해결하는 데 많은 시간을 보냈고 이것이 누군가를 도울 수 있기를 바랍니다! 😃