• 简易搜索引擎SEWeibo


    背景

    有一组微博事件数据,之前做了一些数据分析与挖掘的工作。想着用C++做一个简单的搜索引擎玩玩。

    亮点:

    • 搜索支持关系关键字作为搜索条件,以文本情感极性作为初筛条件,以TF-IDF为搜索排序依据
    • 以Reactor模式为基础,实现C++后台,支持线程池、支持epoll实现I/O多路复用
    • 实现一个简单的前端,搭载在apache上
    • 引入redis实现缓存,引入日志系统

    详细代码请到githubhttps://github.com/li-car-fei/SEWeibo

    求求点个star~~

    搜索关键步骤实现

    预处理阶段:

    • 使用cppjieba对每个微博事件进行分词
    • 分词后计算每个事件的词频,并且根据情感词典计算每个事件的情感倾向分
    • 根据TF-IDF计算每个词在其出现的事件中的影响权重

    实际搜索阶段:

    • 解析出搜索语句中的关键字:ANDORNOT
    • 按照搜索关键字分割搜索语句,并进行搜索,三个关键字分别对应搜索结果求交集、并集、非集
    • 将搜索语句视为一个事件,对其进行分词,计算词频、情感得分
    • 初筛:筛选出拥有搜索语句中所有词语的事件,并选出情感倾向与搜索语句相同的事件
    • 再筛:根据搜索语句的每个词语的权重值构成向量,再计算与备选集的余弦相似度,选取最相似的前k个返回
    • 根据搜索结果的事件ID,生成摘要并返回结果
    • redis 缓存

    结果示例




    Log 日志:

    2023-11-10 17:06:15,951: INFO rootCategory : Log init success
    2023-11-10 17:06:17,226: INFO rootCategory : cppjieba init!
    2023-11-10 17:06:21,692: INFO rootCategory : 停词库,情感词表,网页库,偏移库,倒排索引库,情感得分表 读取数据成功!
    2023-11-10 17:06:28,979: INFO rootCategory : >>client has connected 127.0.0.1:9006 >> 127.0.0.1:34133
    2023-11-10 17:06:28,979: INFO rootCategory : search event: 蔡徐坤
    2023-11-10 17:06:28,980: INFO rootCategory : redis key exists: 蔡徐坤
    2023-11-10 17:06:28,980: INFO rootCategory : >>client has broken up 127.0.0.1:9006 >> 127.0.0.1:34133
    ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    细节

    前端
    • 前端简单写的页面,用apache搭载,详见frontend文件夹
    • 后端返回json数据,前端用jquery进行解析渲染
    单 Reactor 多线程模式
    • net 中实现 Reactor 相关、TCPConnection 相关;由 Acceptor 负责主线程连接相关,监听新的连接请求;回调函数挂载到 TCPConnection 上,由 EventLoop 从 epoll 中取出可操作的 socket 句柄,再调用 TCPConnection 中的回调函数进行处理;
    • threadpool 实现线程池,由回调函数中将业务处理函数添加到线程池的任务队列中,是和 Server 分离开的,不影响 Server 主线程监听连接;
    • using Task = function; 定义了添加到线程池任务队列的数据类型,是一个可调用对象(函数、函数指针、lambda表达式等皆可)
    redis 缓存
    • 单例模式实现,将搜索语句与结果的JSON语句存储到缓存中
    • 在计算搜索匹配结果、词频、情感前先从缓存中查找有没有相应的key(即搜索语句文本),有则直接返回
    • 用于搜索运算消耗较大,使用缓存可以很好提高性能
    log 日志
    • 基于log4cpp实现日志,单例模式
    • 在头文件中定义相应的宏,以实现简单调用日志接口
    搜索细节
    • 关键字分割搜索语句,不同关键字对应不同的搜索结果合并
    • 首先搜索出包含搜索文本每个词的事件,并依据情感得分去除情感倾向不同的结果
    • 根据搜索规则匹配,合并得到搜索结果
    • 根据搜索语句的TF-IDF向量与搜索结果事件的向量计算余弦相似度并排序
    • 最终生成摘要并返回结果
    // 执行查询
        void WordQuery::doQuery(RedisClient& redisClient, const string &s, const TcpConnectionPtr &ptr)
        {
            // 先查询缓存
            auto redisResult = redisClient.get(s);
            if (redisResult.first)
            {
                ptr->sendInEventLoop(redisResult.second);
                return;
            }
    
            // 提取关键字和搜索文本
            auto parseSearchTextResult = parseSearch(s);
    
            /**
             * 构建返回结果所需:
             *  (1)set eventIds:事件id
             *  (2)map> wordsMap : 词频记录(当前搜索语句所构建的event的) 
             *  (3)map>> vec :每个词与其权重(当前搜索语句所构建的event的) 
            */
    
            set<int> eventIds;
            map<int, map<string, int>> wordsMaps;
            map<int, vector<pair<string, double>>> vecs;
    
            // 循环遍历关键字和搜索文本,构建结果
            searchThrough(eventIds, wordsMaps, vecs, parseSearchTextResult.first, parseSearchTextResult.second);
    
            // 根据第一个搜索map和搜索文本的vec 计算余弦排序,形成摘要
            string message = doReturn(eventIds, wordsMaps.begin()->second, vecs.begin()->second, ptr);
    
            // 设置缓存
            redisClient.set(s, message);
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    依赖库
    • cppjieba:常用中文分词库
    • limonp:cppjieba中依赖的
    • jsoncpp:json相关实现
  • 相关阅读:
    Hadoop常见问题
    【LeetCode】2562. 找出数组的串联值
    C认证笔记 - 计算机通识 - HTTPS
    【linux驱动开发】-驱动入门之LED
    在表格数据集上训练变分自编码器 (VAE)示例
    接口自动化测试:mock server之Moco工具
    DS线性表—多项式相加 C++
    JavaScript 通过正则测试页面是否出现连续的重复字符
    双列集合的学习
    Docker 系列之 DockerDesktop 初步安装
  • 原文地址:https://blog.csdn.net/weixin_43783814/article/details/134418677