perf: 优化消息搜索速度

This commit is contained in:
kuaifan 2024-10-24 00:37:47 +08:00
parent b976f294f9
commit 287b6b396d
9 changed files with 194 additions and 32 deletions

View File

@ -147,18 +147,26 @@ class DialogController extends AbstractController
}
// 搜索消息会话
if (count($list) < 20) {
$msgs = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.last_at', 'u.mark_unread', 'u.silence', 'u.hide', 'u.color', 'u.updated_at as user_at', 'm.id as search_msg_id'])
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
->join('web_socket_dialog_msgs as m', 'web_socket_dialogs.id', '=', 'm.dialog_id')
->where('u.userid', $user->userid)
->where('m.key', 'LIKE', "%{$key}%")
->orderByDesc('m.id')
$msgs = WebSocketDialogMsg::select(['web_socket_dialog_msgs.*'])
->join('web_socket_dialog_msg_reads as r', 'r.msg_id', '=', 'web_socket_dialog_msgs.id')
->where('r.userid', $user->userid)
->where('r.live', 1)
->where('web_socket_dialog_msgs.key', 'LIKE', "%{$key}%")
->orderByDesc('r.msg_id')
->take(20 - count($list))
->get();
$msgs->transform(function (WebSocketDialog $item) use ($user) {
return $item->formatData($user->userid);
});
$list = array_merge($list, $msgs->toArray());
foreach ($msgs as $msg) {
$item = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.last_at', 'u.mark_unread', 'u.silence', 'u.hide', 'u.color', 'u.updated_at as user_at'])
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
->where('web_socket_dialogs.id', $msg->dialog_id)
->first();
if (empty($item)) {
continue;
}
$item->search_msg_id = $msg->id;
$item->last_msg = $msg;
$list[] = $item->formatData($user->userid);
}
}
//
return Base::retSuccess('success', $list);

View File

@ -200,7 +200,7 @@ class WebSocketDialog extends AbstractModel
//
if (isset($this->search_msg_id)) {
// 最后消息 (搜索预览消息)
$this->last_msg = WebSocketDialogMsg::whereDialogId($this->id)->find($this->search_msg_id);
$this->last_msg = $this->last_msg ?? WebSocketDialogMsg::whereDialogId($this->id)->find($this->search_msg_id);
$this->last_at = $this->last_msg ? Carbon::parse($this->last_msg->created_at)->toDateTimeString() : null;
} else {
// 未读信息
@ -214,11 +214,11 @@ class WebSocketDialog extends AbstractModel
// 是否免打扰
$this->silence = $this->silence ?? $dialogUserFun('silence');
// 对话人数
$this->people = WebSocketDialogUser::whereDialogId($this->id)->count();
$this->people = $this->people ?? WebSocketDialogUser::whereDialogId($this->id)->count();
// 有待办
$this->todo_num = WebSocketDialogMsgTodo::whereDialogId($this->id)->whereUserid($userid)->whereDoneAt(null)->count();
$this->todo_num = $this->todo_num ?? WebSocketDialogMsgTodo::whereDialogId($this->id)->whereUserid($userid)->whereDoneAt(null)->count();
// 最后消息
$this->last_msg = WebSocketDialogMsg::whereDialogId($this->id)->orderByDesc('id')->first();
$this->last_msg = $this->last_msg ?? WebSocketDialogMsg::whereDialogId($this->id)->orderByDesc('id')->first();
}
// 对方信息
$this->pinyin = Base::cn2pinyin($this->name);

View File

@ -200,6 +200,7 @@ class WebSocketDialogMsg extends AbstractModel
'msg_id' => $this->id,
'userid' => $userid,
'after' => 1,
'live' => 1,
]);
if ($msgRead->saveOrIgnore()) {
$this->send = WebSocketDialogMsgRead::whereMsgId($this->id)->count();
@ -638,16 +639,35 @@ class WebSocketDialogMsg extends AbstractModel
}
/**
* 生成关键词
* @return string
* 生成关键词并保存
* @return void
*/
public function generateMsgKey()
public function generateKeyAndSave(): void
{
return match ($this->type) {
'text' => str_replace("&nbsp;", " ", strip_tags($this->msg['text'])),
'meeting', 'file' => $this->msg['name'],
default => '',
};
$key = '';
switch ($this->type) {
case 'text':
case 'vote':
case 'word-chain':
$key = strip_tags($this->msg['text']);
break;
case 'file':
$key = $this->msg['name'];
$key = preg_replace("/^(image|\d+)\.(png|jpg|jpeg|webp|gif)$/i", "", $key);
$key = preg_replace("/^LongText-(.*?)/i", "", $key);
break;
case 'meeting':
$key = $this->msg['name'];
break;
}
$key = str_replace(["&quot;", "&amp;", "&lt;", "&gt;"], "", $key);
$key = str_replace(["\r", "\n", "\t", "&nbsp;"], " ", $key);
$key = preg_replace("/^\/[A-Za-z]+/", " ", $key);
$key = preg_replace("/\s+/", " ", $key);
$this->key = trim($key);
$this->save();
}
/**
@ -997,8 +1017,7 @@ class WebSocketDialogMsg extends AbstractModel
'modify' => $modify,
];
$dialogMsg->updateInstance($updateData);
$dialogMsg->key = $dialogMsg->generateMsgKey();
$dialogMsg->save();
$dialogMsg->generateKeyAndSave();
//
WebSocketDialogUser::whereDialogId($dialog->id)->whereUserid($sender)->whereHide(1)->change([
'hide' => 0, // 修改消息时,显示会话(仅自己)
@ -1045,8 +1064,7 @@ class WebSocketDialogMsg extends AbstractModel
]);
AbstractModel::transaction(function () use ($dialogMsg) {
$dialogMsg->send = 1;
$dialogMsg->key = $dialogMsg->generateMsgKey();
$dialogMsg->save();
$dialogMsg->generateKeyAndSave();
//
if ($dialogMsg->type === 'meeting') {
MeetingMsg::createInstance([

View File

@ -16,6 +16,7 @@ use Carbon\Carbon;
* @property int|null $email 是否发了邮件
* @property int|null $after 在阅读之后才添加的记录
* @property int|null $dot 红点标记
* @property int|null $live 是否在会话里
* @property \Illuminate\Support\Carbon|null $read_at 阅读时间
* @property-read \App\Models\WebSocketDialogMsg|null $webSocketDialogMsg
* @method static \Illuminate\Database\Eloquent\Builder|AbstractModel cancelAppend()
@ -32,6 +33,7 @@ use Carbon\Carbon;
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereDot($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereEmail($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereLive($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereMention($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereMsgId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereReadAt($value)

View File

@ -4,6 +4,7 @@ namespace App\Observers;
use App\Models\Deleted;
use App\Models\WebSocketDialog;
use App\Models\WebSocketDialogMsgRead;
use App\Models\WebSocketDialogUser;
class WebSocketDialogObserver
@ -39,6 +40,7 @@ class WebSocketDialogObserver
public function deleted(WebSocketDialog $webSocketDialog)
{
Deleted::record('dialog', $webSocketDialog->id, $this->userids($webSocketDialog));
WebSocketDialogMsgRead::whereDialogId($webSocketDialog->id)->update(['live' => 0]);
}
/**
@ -49,7 +51,9 @@ class WebSocketDialogObserver
*/
public function restored(WebSocketDialog $webSocketDialog)
{
Deleted::forget('dialog', $webSocketDialog->id, $this->userids($webSocketDialog));
$userids = $this->userids($webSocketDialog);
Deleted::forget('dialog', $webSocketDialog->id, $userids);
WebSocketDialogMsgRead::whereDialogId($webSocketDialog->id)->whereIn('userid', $userids)->update(['live' => 1]);
}
/**

View File

@ -3,6 +3,7 @@
namespace App\Observers;
use App\Models\Deleted;
use App\Models\WebSocketDialogMsgRead;
use App\Models\WebSocketDialogUser;
use Carbon\Carbon;
@ -29,6 +30,7 @@ class WebSocketDialogUserObserver
}
}
Deleted::forget('dialog', $webSocketDialogUser->dialog_id, $webSocketDialogUser->userid);
WebSocketDialogMsgRead::whereDialogId($webSocketDialogUser->dialog_id)->whereUserid($webSocketDialogUser->userid)->update(['live' => 1]);
}
/**
@ -51,6 +53,7 @@ class WebSocketDialogUserObserver
public function deleted(WebSocketDialogUser $webSocketDialogUser)
{
Deleted::record('dialog', $webSocketDialogUser->dialog_id, $webSocketDialogUser->userid);
WebSocketDialogMsgRead::whereDialogId($webSocketDialogUser->dialog_id)->whereUserid($webSocketDialogUser->userid)->update(['live' => 0]);
}
/**

View File

@ -128,6 +128,7 @@ class WebSocketDialogMsgTask extends AbstractTask
'mention' => $mention,
'silence' => $silence,
'dot' => $dot,
'live' => 1,
])->saveOrIgnore();
$array[$userid] = [
'userid' => $userid,

View File

@ -26,11 +26,7 @@ class AddWebSocketDialogMsgsKey extends Migration
\App\Models\WebSocketDialogMsg::chunkById(100, function ($lists) {
/** @var \App\Models\WebSocketDialogMsg $item */
foreach ($lists as $item) {
$key = $item->generateMsgKey();
if ($key) {
$item->key = $key;
$item->save();
}
$item->generateKeyAndSave();
}
});
}

View File

@ -0,0 +1,130 @@
<?php
use App\Models\WebSocketDialog;
use App\Models\WebSocketDialogMsg;
use App\Models\WebSocketDialogMsgRead;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddWebSocketDialogMsgReadsLive extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
info("update web_socket_dialog_msg_reads live field");
$isAdd = false;
Schema::table('web_socket_dialog_msg_reads', function (Blueprint $table) use (&$isAdd) {
if (!Schema::hasColumn('web_socket_dialog_msg_reads', 'live')) {
$isAdd = true;
$table->integer('live')->nullable()->default(0)->index()->after('dot')->comment('是否在会话里');
$table->index(['userid', 'live', 'msg_id']);
}
});
info("update web_socket_dialog_msgs deleted_at");
Schema::table('web_socket_dialog_msgs', function (Blueprint $table) {
$table->index('deleted_at');
});
if ($isAdd) {
// 关键词包含
$contains = [
'您可以通过发送以下命令来控制我',
'我的机器人。',
'机器人名称:',
'已加入的会话:',
'你可以通过执行以下命令来请求我:',
'假期类型:',
'评论了此审批',
'我是你的机器人助理',
'打卡时间: ',
'匿名消息使用说明',
'匿名消息将通过',
'导出任务统计已完成',
'我不是你的机器人',
'机器人名称由',
'您没有创建机器人',
'缺卡提醒:',
'打卡提醒:',
'文件下载打包已完成',
'您有一个新任务 #',
'您协助的任务 #',
'您负责的任务 #',
];
info("update web_socket_dialog_msgs key contains");
foreach ($contains as $key) {
WebSocketDialogMsg::whereType('text')->where('key', 'like', "%{$key}%")->update(['key' => '']);
}
// 关键词开始以
$starts = [
'/hello',
'/help',
'/list',
'/info',
'/newbot',
'/setname',
'/deletebot',
'/token',
'/revoke',
'/webhook',
'/clearday',
'/dialog',
'/api',
];
info("update web_socket_dialog_msgs key starts");
foreach ($starts as $key) {
WebSocketDialogMsg::whereType('text')->where('key', 'like', "{$key}%")->update(['key' => '']);
}
// 关键词等于
$equals = [
'我要打卡',
'IT资讯',
'36氪',
'60s读世界',
'开心笑话',
'心灵鸡汤',
'使用说明',
'隐私说明',
'帮助指令',
'API接口文档',
'我的机器人',
'清空上下文',
'操作频繁!',
'暂未开启签到功能。',
'暂未开放手动签到。',
'设置成功',
'机器人不存在。',
];
info("update web_socket_dialog_msgs key equals");
foreach ($equals as $key) {
WebSocketDialogMsg::whereType('text')->whereKey($key)->update(['key' => '']);
}
// 更新是否在会话里面
info("update web_socket_dialog_msg_reads live value");
WebSocketDialog::chunk(100, function ($dialogs) {
/** @var WebSocketDialog $dialog */
foreach ($dialogs as $dialog) {
WebSocketDialogMsgRead::whereDialogId($dialog->id)->whereIn('userid', function ($query) use ($dialog) {
$query->select('userid')->from('web_socket_dialog_users')->whereDialogId($dialog->id);
})->update(['live' => 1]);
}
});
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}