历史记录
更新时间: 2024/05/27 14:12:39
本地记录
- 获取聊天对象的本地消息记录
通过传入指定的消息历史记录的参数(会话id,会话类型,指定消息的时间戳)作为锚点,向前或向后查询本地消息记录。
C++void nim_msglog_query_msg_async(const char *account_id //会话id,对方的account id或者群组tid
, NIMSessionType to_type //会话类型
, int limit_count //一次查询数量,建议20
, __int64 anchor_msg_time //指定消息的时间戳
, const char *json_extension
, nim_msglog_query_cb_func cb
, const void *user_data);
例:
C++
C++void OnQueryMsgCallback(nim::NIMResCode code, const std::string& query_id, nim::NIMSessionType query_type, const nim::QueryMsglogResult& result)
{
std::vector<nim::IMMessage> vec;
for each (auto msg in result.msglogs_)
{
vec.push_back(msg);
}
}
void foo()
{
nim::MsgLog::QueryMsgAsync("session_id", nim::kNIMSessionTypeTeam, 20, 0, &OnQueryMsgCallback);
}
C#
C#NIM.Messagelog.MessagelogAPI.QueryMsglogLocally("session_id", NIM.Session.NIMSessionType.kNIMSessionTypeP2P, 20, 0, (code, accountId, sType, result) =>
{
foreach(var i in result.MsglogCollection)
{
···
}
});
C
C//按时间逆序查询,逆序排列
void CallbackQueryMsgCb(int res_code, const char* id, const char* type, const char* str, const char *json_exten, const void *user_data)
{
Json::Value values;
Json::Reader reader;
if (reader.parse(str, values) && values.isObject())
{
int count = values[kNIMMsglogQueryKeyCount].asInt();
if (count > 0)
...
}
}
typedef void(*nim_msglog_query_msg_async)(const char* account_id, nim::NIMSessionType to_type, int limit_count, __int64 last_time, const char *json_extension, nim_msglog_query_cb_func cb, const void* user_data);
void foo()
{
//设置搜索方向为向前
Json::Value json_extension_value;
json_extension_value[kNIMMsglogQueryJsonExtensionKeyDirection] = kForward;
Json::FastWriter fw;
nim_msglog_query_msg_async func = (nim_msglog_query_msg_async) GetProcAddress(hInst, "nim_msglog_query_msg_async");
func("acount_id", is_team ? kNIMSessionTypeTeam : kNIMSessionTypeP2P, 20, 0, fw.write(json_extension_value).c_str(), &CallbackQueryMsgCb, nullptr);
}
- 删除消息记录:
C++
C++void foo()
{
//删除单条消息
nim::MsgLog::DeleteAsync("session_id", nim::kNIMSessionTypeTeam, client_msg_id_, nim::MsgLog::DeleteCallback());
//删除与某个聊天对象的全部消息记录
nim::MsgLog::BatchStatusDeleteAsync("session_id", nim::kNIMSessionTypeTeam, nim::MsgLog::BatchStatusDeleteCallback());
//删除指定会话类型的所有消息
nim::MsgLog::DeleteBySessionTypeAsync(true, nim::kNIMSessionTypeTeam, nim::MsgLog::DeleteBySessionTypeCallback());
//删除所有消息记录
nim::MsgLog::DeleteAllAsync(true, nim::MsgLog::DeleteAllCallback());
}
C#
C#void foo()
{
//删除单条消息
NIM.Messagelog.MessagelogAPI.DeleteSpecifiedMsglog("session_id", NIM.Session.NIMSessionType.kNIMSessionTypeP2P, msg_id, (code, msgId) =>
{
});
//删除与某个聊天对象的全部消息记录
NIM.Messagelog.MessagelogAPI.BatchDeleteMeglog("session_id", NIM.Session.NIMSessionType.kNIMSessionTypeP2P, (code, uid, sType) =>
{
});
//删除指定会话类型的所有消息
NIM.Messagelog.MessagelogAPI.DeleteMsglogsBySessionType(NIM.Session.NIMSessionType.kNIMSessionTypeP2P, true, (code, uid, sType) =>
{
});
//删除所有消息记录
NIM.Messagelog.MessagelogAPI.ClearAll(true, (code) =>
{
});
}
C
Cvoid CallbackModifyMsglog(int res_code, const char *msg_id, const char *json_extension, const void user_data)
{
if (user_data)
{
···
}
}
typedef void(*nim_msglog_delete_async)(const char *account_id, NIMSessionType to_type, const char *msg_id, const char *json_extension, nim_msglog_res_cb_func cb, const void *user_data);
typedef void(*nim_msglog_batch_status_delete_async)(const char* account_id, nim::NIMSessionType to_type, const char *json_extension, nim_msglog_res_ex_cb_func cb, const void* user_data);
typedef void(*nim_msglog_delete_by_session_type_async)(bool delete_sessions, NIMSessionType to_type, const char *json_extension, nim_msglog_res_ex_cb_func cb, const void *user_data);
typedef void(*nim_msglog_delete_all_async)(bool delete_sessions, const char *json_extension, nim_msglog_modify_res_cb_func cb, const void *user_data);
void foo()
{
nim_msglog_delete_async func = (nim_msglog_delete_async) GetProcAddress(hInst, "nim_msglog_delete_async");
nim_msglog_batch_status_delete_async func_batch_statuc = (nim_msglog_batch_status_delete_async) GetProcAddress(hInst, "nim_msglog_batch_status_delete_async");
nim_msglog_delete_by_session_type_async func_session = (nim_msglog_delete_by_session_type_async) GetProcAddress(hInst, "nim_msglog_delete_by_session_type_async");
nim_msglog_delete_all_async func_all = (nim_msglog_delete_all_async) GetProcAddress(hInst, "nim_msglog_delete_all_async");
//删除单条消息
func("acount_id", is_team ? kNIMSessionTypeTeam : kNIMSessionTypeP2P, msg_id, nullptr, &CallbackModifyMsglog, nullptr);
//删除与某个聊天对象的全部消息记录
func_batch_statuc("acount_id", is_team ? kNIMSessionTypeTeam : kNIMSessionTypeP2P, nullptr, &CallbackModifyMsglog, nullptr);
//删除指定会话类型的所有消息
func_session(true, is_team ? kNIMSessionTypeTeam : kNIMSessionTypeP2P, nullptr, &CallbackModifyMsglog, nullptr);
//删除所有消息记录
func_all(delete_sessions, nullptr, &CallbackModifyMsglog, nullptr);
}
- 插入聊天记录
C++
接口:
static bool WriteMsglogToLocalAsync(const std::string& talk_id , const IMMessage& msg , bool need_update_session , const WriteMsglogCallback& cb , const std::string& json_extension = "");
示例:
C++void foo()
{
std::wstring notify_text = GetRecallNotifyTextEx(talk_id, notify.session_type_, notify.from_id_, notify.operator_id_,notify.from_nick_);
nim::IMMessage msg;
msg.timetag_ = notify.msglog_timetag_;
msg.client_msg_id_ = QString::GetGUID();
msg.receiver_accid_ = talk_id;
msg.session_type_ = notify.session_type_;
msg.sender_accid_ = notify.from_id_;
msg.status_ = nim::kNIMMsgLogStatusSent;
msg.type_ = nim::kNIMMessageTypeText;
Json::Value values;
values["comment"] = "is_recall_notification";
values["notify_from"] = notify.from_id_;
values["operator_id"] = notify.operator_id_;
values["from_nick"] = notify.from_nick_;
msg.attach_ = values.toStyledString();
msg.content_ = nbase::UTF16ToUTF8(notify_text);
msg.msg_setting_.push_need_badge_ = nim::BS_FALSE; //设置会话列表不需要计入未读数
nim::MsgLog::WriteMsglogToLocalAsync(talk_id, msg, true, nim::MsgLog::WriteMsglogCallback());
}
nim_msglog.h 文件中的接口提供了比较完善的消息记录管理功能,开发者可本地查阅、在线查阅、删除和清空聊天记录,还可以调用nim_msglog_write_db_only_async 接口只往本地消息历史数据库里写入一条消息(通常是APP 的本地自定义消息,并不会发给服务器)。详见API 文档。此外,还要注意:本地消息历史操作(如删除)及状态变化(如发送失败或成功)会与会话列表里的消息状态自动同步,App 上层需要根据nim_session_reg_change_cb 注册好的会话列表通知回调进行相应通知事件的处理。
导入导出
- 导入消息历史记录(不包括系统通知,且不允许拿别人的消息历史记录文件来导入):
void nim_msglog_import_db_async(const char *src_path, const char *json_extension, nim_msglog_modify_res_cb_func res_cb, const void *res_user_data, nim_msglog_import_prg_cb_func prg_cb, const void *prg_user_data);
例:
C++
C++void OnImportProgressCallback(int64_t imported_count, int64_t total_count)
{
···
}
void OnImportCompeleteCallback(nim::NIMResCode res_code)
{
if (res_code == nim::kNIMResSuccess)
{
//导入完成
}
else
{
//导入失败
}
}
void foo()
{
nim::MsgLog::ImportDbAsync(path, &OnImportCompeleteCallback, &OnImportProgressCallback);
}
C#
C#NIM.Messagelog.MessagelogAPI.ImportDatabase(path, (code) =>
{
}
, (importedCount, totalCount) =>
{
});
C
Cvoid CallbackMsglogRes(int res, const char* msg_id, const char *json_exten, const void *user_data)
{
...
}
void CallbackMsgImportPrg(int res_code, const char* id, const char* type, const char* str, const char *json_exten, const void *user_data)
{
...
}
typedef void(*nim_msglog_import_db_async)(const char *src_path, const char *json_extension, nim_msglog_modify_res_cb_func res_cb, const void *res_user_data, nim_msglog_import_prg_cb_func prg_cb, const void *prg_user_data);
void foo()
{
nim_msglog_import_db_async func = (nim_msglog_import_db_async) GetProcAddress(hInst, "nim_msglog_import_db_async");
func("src_path", "", &CallbackMsglogRes, nullptr, &CallbackMsgImportPrg, nullptr);
}
- 导出整个消息历史DB文件(不包括系统通知):
C++
C++void OnExportCompeleteCallback(nim::NIMResCode res_code)
{
if (res_code == nim::kNIMResSuccess)
{
//导出完成
}
else
{
//导出失败
}
}
void foo()
{
nim::MsgLog::ExportDbAsync(path, &OnExportCompeleteCallback);
}
C#
C#NIM.Messagelog.MessagelogAPI.ExportDatabaseFile(path, (code) =>
{
});
C
Cvoid CallbackMsglogRes(int res, const char* msg_id, const char *json_exten, const void *user_data)
{
...
}
typedef void(*nim_msglog_export_db_async)(const char *dst_path, const char *json_extension, nim_msglog_modify_res_cb_func cb, const void *user_data);
void foo()
{
nim_msglog_export_db_async func = (nim_msglog_export_db_async) GetProcAddress(hInst, "nim_msglog_export_db_async");
func("src_path", "", &CallbackMsglogRes, nullptr);
}
云端记录
- 在线查询聊天对象的消息历史记录:
void nim_msglog_query_msg_online_async(const char *id //会话id,对方的account id或者群组tid
, NIMSessionType to_type //会话类型
, int limit_count //本次查询的消息条数上限(最多100条)
, __int64 from_time //起始时间点,单位:毫秒
, __int64 end_time //结束时间点,单位:毫秒
, __int64 end_msg_id //结束查询的最后一条消息的server_msg_id(不包含在查询结果中)
, bool reverse //true:反向查询(按时间正序起查,正序排列),false:按时间逆序起查,逆序排列(建议默认为false)
, bool need_save_to_local //true: 将在线查询结果保存到本地,false: 不保存
, const char *json_extension
, nim_msglog_query_cb_func cb
, const void *user_data);
例:
C++
C++void QueryMsgOnlineCb(nim::NIMResCode code, const std::string& id, nim::NIMSessionType type, const nim::QueryMsglogResult& result)
{
if (code == nim::kNIMResSuccess)
{
std::vector<nim::IMMessage> vec;
for (auto& msg : result.msglogs_)
{
···
}
}
}
void foo()
{
nim::MsgLog::QueryMsgOnlineAsync("session_id", nim::kNIMSessionTypeP2P, 20, 0, farst_msg_time_, last_server_id_, false, true, &QueryMsgOnlineCb);
}
C#
C#NIM.Messagelog.MessagelogAPI.QueryMsglogOnline("session_id", NIM.Session.NIMSessionType.kNIMSessionTypeTeam, 20, 0, 0, 0, false, false,
(ResponseCode code, string accountId, NIM.Session.NIMSessionType sType, MsglogQueryResult result) =>
{
···
});
C
Cvoid CallbackQueryMsgCb(int res_code, const char* id, const char* type, const char* str, const char *json_exten, const void *user_data)
{
...
}
typedef void(*nim_msglog_query_msg_online_async)(const char *id, nim::NIMSessionType to_type, int limit_count, __int64 from_time, __int64 end_time, __int64 end_msg_id, bool reverse, bool need_save_to_local, const char *json_extension, nim_msglog_query_cb_func cb, const void *user_data);
void foo()
{
nim_msglog_query_msg_online_async func = (nim_msglog_query_msg_online_async) GetProcAddress(hInst, "nim_msglog_query_msg_online_async");
func("acount_id", is_team ? kNIMSessionTypeTeam : kNIMSessionTypeP2P, 20, 0, msg_time, first_msg_server_msg_id, false, false, nullptr, &CallbackQueryMsgCb, nullptr);
}
动态查询历史消息
为了获取完整的历史消息记录,您可以使用全云端方案,即用户每次进入时都通过查询云端历史记录并将查到的消息同步至本地。但是在会话多、消息多的场景下,使用全云端方案,会导致耗时长,耗能大等问题。
为解决上述问题,云信 IM 提供本地+云端智能调度方案,通过调用完整的内部逻辑保证历史消息的连续性和完整性。您仅需要选择开始或结束时间戳以及对应的消息条数即可按需获取历史消息。
开启动态查询历史消息功能
请先在云信控制台开通动态查询历史消息功能,否则该接口只会从本地数据库获取消息。
-
在控制台首页应用管理中选择应用,然后单击 IM 即时通讯下的功能配置按钮进入功能配置页。
-
在顶部选择全局功能页签,开启动态查询历史消息功能。
-
阅读并确认信息后,单击确认开启动态查询历史消息功能。
接口说明
该接口为完整独立的历史消息查询接口,与其他历史消息查询接口互不影响。
通过调用 GetMessagesDynamically
方法动态查询连续的历史消息。
参数说明:
参数 | 说明 |
---|---|
session_id | 会话 ID |
to_type | 会话类型,具体请及参见NIMSessionType |
from_time | 开始时间 |
to_time | 结束时间,大于开始时间,0 表示当前时间 |
limit_count | 查询的历史消息数量,最大 100 |
anchor_client_msg_id | 查询起始的客户端消息 ID,查询结果不包括这条消息 |
anchor_server_msg_id | 查询起始的服务端消息 ID,查询结果不包括这条消息 |
direction | 查询方向,具体请参见NIMMsglogSearchDirection ,默认kForward ,即从新往旧查询 |
cb | 查询历史消息的回调函数 |
示例代码:
c++std::string session_id = "session_id";
uint64_t from_time = 0;
uint64_t to_time = 0;
uint32_t limit = 100;
std::string anchor_client_msg_id = "";
uint64_t anchor_server_msg_id = 0;.
MsgLog::GetMessagesDynamically(session_id, kNIMSessionTypeP2P, from_time, to_time, limit, anchor_client_msg_id, anchor_server_msg_id, kForward, [](const GetMessagesResult& result) {
// process result
// ......
});
用户可通过 nim_msglog_get_messages_dynamically
方法动态查询连续的历史消息。
示例代码:
static void CallbackGetMessages(int res_code,
const char* id,
nim::NIMSessionType to_type,
const char* result,
const char* json_extension,
const void* callback) {
// process messages
// ......
}
nim_msglog_get_messages_dynamically("session_id", kNIMSessionTypeP2P, 0, 0, 100, "", 0, direction,json_extension.c_str(), &CallbackGetMessages, cb_pointer);
消息全文检索
自 V9.16.3 版本起,云信 IM SDK 引入 SQLite3 FTS5 全文检索能力,并大幅提高搜索性能。
全文搜索能力支持常规检索、拼音检索以及分词逻辑检索。
常规检索
如果您是首次安装全新应用并启动,那么您可以直接使用全文搜索功能(开箱即用)。可直接调用 QueryMessagesByKeywordAsync
接口根据关键字在本地检索所有历史消息。
如果您已安装应用,通过升级操作至最新版本,那么您需要先调用 BuildMsglogIndexes
接口对全文检索的索引进行构建。
- 如果您有历史数据,请先调用
IsMessageIndexEstablished
接口判断是否已经同步完成所有旧消息索引。 - 如果尚未同步完成,可使用
BuildMsglogIndexes
接口来构建历史消息索引,以提供全文检索接口快速查询内容。 - 如果构建时间较久,可调用
CancelMsglogIndexesBuilding
接口停止历史消息的索引构建。停止构建时会记录已构建过的消息唯一 ID,后续再调用构建消息历史索引的 API 时,将执行增量构建索引,不会从头再构建。
示例代码如下:
MsgLog::QueryMsgByKeywordParam param;
param.keyword_ = "search keyword";
MsgLog::QueryMessagesByKeywordAsync(
param, [this](NIMResCode res_code, const std::string& id, nim::NIMSessionType to_type, const QueryMsglogResult& result) {
// handle result
});
本接口默认根据关键字(keyword)进行搜索,若需要指定其他筛选条件,修改传入的 MsgLog::QueryMsgByKeywordParam
参数指定要过滤条件,示例代码如下:
/** @class QueryMsgByKeywordParam
* 根据关键字本地查询消息参数
*/
class NIM_SDK_CPPWRAPPER_DLL_API QueryMsgByKeywordParam {
public:
QueryMsgByKeywordParam() = default;
/// 要查询的关键字
std::string keyword_;
/// 查询 ID,对方的 account ID 或者群组 team ID,可留空将不作为查询条件
std::string account_id_;
/// enum 会话类型,双人0,群组1 (nim_msglog_def.h),当 id_ 为空时,to_type_ 不作为查询条件
nim::NIMSessionType to_type_{kNIMSessionTypeP2P};
/// std::list 要获取的消息类型,默认只有文本类消息,可自行扩充其他类型消息,除通知类消息不受支持外,其他类型消息(包括自定义消息)均可检索
std::vector<nim::NIMMessageType> type_;
/// uint32_t 本次查询的消息条数上限,默认 100 条
uint32_t limit_count_{100};
/// time_t 起始时间点,单位:毫秒
time_t from_time_{0};
/// time_t 结束时间点,单位:毫秒
time_t end_time_{0};
/// 查询方向,@see NIMMsglogSearchDirection
NIMMsglogSearchDirection direction_{kForward};
/// enum 全文检索对搜索词分词逻辑,默认为 kSimple @see NIMMsglogSearchSegmentEngine
NIMMsglogSearchSegmentEngine segment_engine_{kSegmentEngineSimple};
/// bool 是否使用拼音检索,默认为 true
bool enable_pinyin_{true};
};
拼音检索
全文检索支持使用拼音检索中文内容,例如消息库中有消息内容包含“网易云信 C++ SDK”,在将 enable_pinyin_
参数设置为 true 的前提下,搜索的 keyword 包含 wangyiyunxin
或拼音首字母 wyyx
均可以检索到该消息,示例代码如下:
MsgLog::QueryMsgByKeywordParam param;
param.keyword_ = "wangyiyunxin; // or wyyx
MsgLog::QueryMessagesByKeywordAsync(
param, [this](NIMResCode res_code, const std::string& id, nim::NIMSessionType to_type, const QueryMsglogResult& result) {
// handle result
});
应用层可在搜索输入框内容变更事件中判断内容是否完全由字母组成,以决定是否开启拼音检索开关。
分词逻辑检索
IM SDK 提供了 segment_engine_
参数,允许您在检索消息内容时控制对检索词的分词逻辑,默认情况下使用 kSegmentEngineSimple
,会将搜索词拆分为单个字符并到 FTS 表中查询对应索引,比如有以下检索字符串:
网易云信 C++ SDK
那么在使用默认分词逻辑后将转换为以下条件进行检索:
"\"网\" AND \"易\" AND \"云\" AND \"信\" AND c* AND \"+\" AND \"+\" AND ( s+d+k* OR sdk* )"
使用默认分词逻辑情况下,如果搜索词过短可能会搜索出一些与预期不太符合需求的内容,对于这样的问题,云信通过引入结巴分词词库,切换不同的 segment_engine
参数,搜索字串的分词逻辑将出现变化,比如将分词逻辑修改为 kSegmentEngineJiebaCutWithHMM
时,再次执行搜索该搜索内容将被拆分为如下搜索条件:
"\"网易\" AND \"云信\" AND ( c++* OR c++++* ) AND ( s+d+k* OR sdk* )"
使用不同的搜索字串分词逻辑搜索结果也将出现不同的效果,这些需要您根据业务具体情况适当调整,如果您没有特殊要求,使用默认的 kSegmentEngineSimple
分词模式即可。
- 当 segment_engine >= 2 时会从本地加载结巴分词的词库文件,您需要将结巴分词的词库文件与 SDK 动态库文件保存到相同目录下的 dict 文件夹中,否则会在搜索过程中出现无法加载词库异常退出或搜索结果不符合预期等问题。
- 单击可下载词典。各系统的目录结构不同,Windows 下将词典的 dict 文件夹与 nim.dll 存放到同级目录,macOS 与 libnim.dylib 存放到同级目录,Linux 与 libnim.so 存放到同级目录。