MongoDB是一个基于分布式文件存储的数据库,使用C++语言编写。它旨在为WEB应用提供可扩展的高性能数据存储解决方案。MongoDB介于关系数据库和非关系数据库之间,是非关系数据库当中功能最丰富、最像关系数据库的。
MongoDB将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB文档类似于JSON对象,字段值可以包含其他文档、数组及文档数组。它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。
MongoDB最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
此外,MongoDB还具有以下特点:
在高负载情况下,添加更多的节点可以保证服务器性能。MongoDB也易于部署和使用,存储数据非常方便。
总的来说,MongoDB是一个高性能、易部署、易使用的数据库系统,具有丰富的功能和特点,适用于各种规模的应用程序和场景。
优点:
缺点:
MongoDB的使用场景非常广泛,包括以下几个方面:
总的来说,MongoDB适用于各种场景,从网站数据到大数据处理,再到社交网络和游戏等领域,它都表现出强大的灵活性和可扩展性。
选择使用MySQL还是MongoDB来存储聊天记录取决于具体需求和场景。以下是两者的一些比较:
MySQL
MongoDB
综上所述,如果聊天记录主要是结构化数据并且需要事务处理和复杂查询,MySQL可能是一个更好的选择。如果聊天记录包含大量非结构化数据并且需要水平扩展和实时处理能力,对事务的完整性要求不高对存取速度要求较高我建议使用新兴的nosql类型数据 MongoDB可能更适合。
需求:我们的需求是实现一个与AI对话的聊天系统,大概分为两个部分,一个是会话,一个是聊天
我给大家放张图帮助理解(左边是会话,右边是聊天)
会话collection:
@Data
@Document(value = "agents_session")
public class AgentsSession implements Serializable {
private static final long serialVersionUID = 198529858452480909L;
private String id;
private String agentId;
/**
* session id
*/
private String sessionId;
/**
* 发送者id
*/
private String senderCode;
/**
* 消息(当前会话组中最早的一次提问(也就是用户想AI提问))
*/
private String message;
/**
* 发送时间
*/
private String sendTime;
/**
* 是否删除
*/
private Boolean isDeleted;
}
聊天记录collection:
@Data
@Document(value = "agents_chat_messages")
public class AgentsChatMessages implements Serializable {
private static final long serialVersionUID = 823228953137629152L;
private String id;
/**
* 会话id
*/
private String sessionId;
/**
* 消息内容
*/
private String message;
/**
* 接收状态
*/
private Integer receiveStatus;
/**
* 发送者id
*/
private String senderCode;
/**
* 接收者id
*/
private String recipientCode;
/**
* 发送时间
*/
private String sendTime;
/**
* 消息类型 文本、图片、文件、语音等
*/
private String messageType;
/**
* 消息内容汉字个数
*/
private Integer tokens;
/**
* 当前支持以下:
* user: 表示用户
* assistant: 表示对话助手
*/
private String role;
/**
* 是否已读
*/
private Boolean isRead;
/**
* 是否删除
*/
private Boolean isDeleted;
/**
* 问答对匹配id
*/
private String questionAnswerId;
}
service实现
public interface ChatMessagesService {
/**
* 分页获取会话列表
* @param dto
* @return
*/
PageModel<AgentsSessionVO> queryAgentSessionPage(AgentsSessionDTO dto, PageRequestDTO page);
/**
* 通过会话id分页获取会话列表
* @param dto
* @return
*/
PageModel<AgentsChatMessagesVO> queryAgentsChatMessagesPage(AgentsChatMessagesDTO dto, PageRequestDTO page);
/**
* 保存会话和聊天
* @param messagesDTO
*/
void saveSessionAndMessages(AgentsChatMessagesDTO messagesDTO);
}
实现类:
@Service
@Slf4j
public class ChatMessagesServiceImpl implements ChatMessagesService {
@Resource
private MongoTemplate mongoTemplate;
/**
* 获取会话列表
*
* @param dto
* @return
*/
@Override
public PageModel<AgentsSessionVO> queryAgentSessionPage(AgentsSessionDTO dto, PageRequestDTO page) {
try {
// 创建分页对象
Pageable pageable = PageRequest.of(page.getPage() - 1, page.getSize(), Sort.Direction.DESC, "sendTime"); // 注意:页码从0开始,所以需要减1
// 创建查询对象
Query query = new Query();
query.addCriteria(Criteria.where("senderCode").is(dto.getUserCode()).and("isDeleted").is(false));
//设置模糊查询
if (StringUtils.isNotEmpty(dto.getMessage())) {
query.addCriteria(Criteria.where("message").regex(dto.getMessage()));
}
if (!CollectionUtils.isEmpty(dto.getAgentsIds())) {
// in 条件查询
Criteria criteria = Criteria.where("agentId").in(dto.getAgentsIds());
query.addCriteria(criteria);
}
// 排序
query.with(Sort.by(Sort.Order.desc("sendTime")));
// 设置分页
query.with(pageable);
List<AgentsSessionVO> list = mongoTemplate.find(query, AgentsSessionVO.class, CommonConstant.AGENTS_SESSION);
list.forEach(s ->{
try {
s.setMessage(AesEncryptionUtil.decrypt(s.getMessage()));
} catch (Exception e) {
throw new HxyAgentsXException("数据加载失败", e);
}
});
long count = mongoTemplate.count(query, AgentsSessionVO.class, CommonConstant.AGENTS_SESSION);
return new PageModel<AgentsSessionVO>(list, count, pageable);
} catch (Exception e) {
log.error("获取会话列表异常");
throw new HxyAgentsXException("获取会话列表异常", e);
}
}
/**
* 通过会话id分页获取聊天记录
*
* @param dto
* @return
*/
@Override
public PageModel<AgentsChatMessagesVO> queryAgentsChatMessagesPage(AgentsChatMessagesDTO dto, PageRequestDTO page) {
try {
// 创建分页对象
Pageable pageable = PageRequest.of(page.getPage() - 1, page.getSize(), Sort.Direction.ASC,"sendTime"); // 注意:页码从0开始,所以需要减1
// 创建查询对象
Query query = new Query();
//设置模糊查询
if (StringUtils.isNotEmpty(dto.getMessage())) {
query.addCriteria(Criteria.where("message").regex(dto.getMessage()));
}
query.addCriteria(Criteria.where("sessionId").is(dto.getSessionId()).and("isDeleted").is(false));
query.addCriteria(new Criteria().orOperator(Criteria.where("senderCode").is(dto.getUserCode()),Criteria.where("recipientCode").is(dto.getUserCode())));
// 排序
query.with(Sort.by(Sort.Order.asc("sendTime")));
// 设置分页
query.with(pageable);
List<AgentsChatMessagesVO> list = mongoTemplate.find(query, AgentsChatMessagesVO.class, CommonConstant.AGENTS_CHAT_MESSAGES);
list.forEach(m ->{
try {
m.setMessage(AesEncryptionUtil.decrypt(m.getMessage()));
} catch (Exception e) {
throw new HxyAgentsXException("数据加载失败", e);
}
});
long count = mongoTemplate.count(query, AgentsChatMessagesVO.class, CommonConstant.AGENTS_CHAT_MESSAGES);
return new PageModel<AgentsChatMessagesVO>(list, count, pageable);
} catch (Exception e) {
log.error("获取聊天记录列表异常");
throw new HxyAgentsXException("获取聊天记录列表异常", e);
}
}
/**
* 存会话聊天
* @param messagesDTO
*/
@Override
public void saveSessionAndMessages(AgentsChatMessagesDTO messagesDTO) {
try {
Criteria.where("sessionId").is(messagesDTO.getSessionId());
AgentsSession agentsSessionOne = mongoTemplate.findOne(new Query(Criteria.where("sessionId").is(messagesDTO.getSessionId()).
and("isDeleted").is(false)), AgentsSession.class);
// 会话
if (agentsSessionOne == null){
AgentsSession agentsSession = new AgentsSession();
agentsSession.setId(UUIDUtils.getUUID());
agentsSession.setSessionId(messagesDTO.getSessionId());
agentsSession.setAgentId(messagesDTO.getAgentId());
agentsSession.setMessage(AesEncryptionUtil.encrypt(messagesDTO.getMessage()));
agentsSession.setSenderCode(messagesDTO.getSenderCode());
agentsSession.setIsDeleted(false);
agentsSession.setSendTime(LocalDateUtil.localDateTimeToString(LocalDateUtil.getLocalDateTime(),"yyyy-MM-dd HH:mm:ss"));
mongoTemplate.insert(agentsSession);
}
// 聊天
AgentsChatMessages agentsChatMessages = new AgentsChatMessages();
agentsChatMessages.setId(UUIDUtils.getUUID());
agentsChatMessages.setSessionId(messagesDTO.getSessionId());
agentsChatMessages.setMessage(AesEncryptionUtil.encrypt(messagesDTO.getMessage()));
agentsChatMessages.setMessageType("text");
agentsChatMessages.setRole(messagesDTO.getRole());
agentsChatMessages.setIsRead(true);
agentsChatMessages.setIsDeleted(false);
agentsChatMessages.setSenderCode(messagesDTO.getSenderCode());
agentsChatMessages.setRecipientCode(messagesDTO.getRecipientCode());
agentsChatMessages.setSendTime(LocalDateUtil.localDateTimeToString(LocalDateUtil.getLocalDateTime(),"yyyy-MM-dd HH:mm:ss"));
agentsChatMessages.setQuestionAnswerId(messagesDTO.getQuestionAnswerId());
mongoTemplate.insert(agentsChatMessages);
} catch (Exception e) {
log.error("保存会话聊天失败",e);
throw new HxyAgentsXException("保存会话聊天失败",e);
}
}
}
聊天内容加密:
public class AesEncryptionUtil {
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION = "AES/ECB/PKCS5Padding";
private static final byte[] keyValue = "yourSecretKey".getBytes(StandardCharsets.UTF_8);
public static String encrypt(String valueToEncrypt) throws Exception {
SecretKeySpec key = new SecretKeySpec(keyValue, ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedByteValue = cipher.doFinal(valueToEncrypt.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptedByteValue);
}
public static String decrypt(String encryptedValue) throws Exception {
SecretKeySpec key = new SecretKeySpec(keyValue, ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] originalValue = cipher.doFinal(Base64.getDecoder().decode(encryptedValue));
return new String(originalValue, StandardCharsets.UTF_8);
}
public static void main(String[] args) throws NoSuchAlgorithmException {
// 创建AES密钥生成器
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
// 设置密钥长度为256位
keyGenerator.init(128);
// 生成密钥
SecretKey secretKey = keyGenerator.generateKey();
// 将密钥转换为字符串
String keyString = Base64.getEncoder().encodeToString(secretKey.getEncoded());
System.out.println("Generated AES key (Base64): " + keyString);
}
}
在模型回答结束的时候保存聊天内容
最后大家可以结合自己的业务来实现聊天记录的存取。
最后送大家一句话白驹过隙,沧海桑田
文章持续更新,可以关注下方公众号或者微信搜一搜「 迷迭香编程 」获取项目源码、干货笔记、面试题集,第一时间阅读,获取更完整的链路资料。