跳至主要內容

搜索模块开发

holic-x...大约 8 分钟

搜索模块开发

扩展参考思路:

ES操作:Easy-ES:类似MyBatis-plus轻松操作ES:https://juejin.cn/post/7271896547594682428

聚合搜索

1.系统设计

​ 聚合搜索提供了哪些内容的检索:文章、图片、接口、文件(todo)等信息检索,结合前台功能重点说明数据检索的部分,一些重复的信息管理做关联说明,后台关注基础信息管理、统计部分

​ 核心构建说明:

1)聚合搜索概念:整合不同的搜索源信息并返回(类似baidu的分类搜索概念:文章、图片、数据等),后续扩展考虑是否可以支持更加多样性的搜索数据源

2)数据源的定义:数据爬取、网站解析、本地数据源

3)设计模式的应用:门面模式、适配器模式、注册器模式

4)搜索效率提升:针对不同的数据源处理模式不同

  • 文章数据:例如通过数据爬取的文章进入本地数据库,引入ES同步,进而优化文章检索效率(动静分离概念)
  • 图片数据:
    • 网站解析=》通过解析网站页面获取到图片信息,但这块的查询效率则受限于爬取网站的响应效率
    • 除却网站解析方式,可以考虑从cos存储桶中尝试解析图片信息并响应(此处涉及到云存储的交互)
  • 本地数据源:结合API接口调用模块,提供接口信息检索,帮助用户快速定位接口信息

5)扩展:基于ES的应用可以考虑引入更多检索类型(例如思考公司级别的知识库构建需要检索什么内容等)

模块介绍说明
后台核心:搜索信息管理,关注数据源的获取和处理
1)文章数据抓取:文章抓取信息管理(文章抓取、文章信息同步ES处理&跟踪)
2)图片网站解析:解析网页信息(Bing网站)
3)接口本地数据库:基于API接口开放模块的接口信息基础
前台核心:聚合搜索功能
1)文章检索文章信息:动静分离概念(从ES快速检索文章信息,随后动态关联查找其最新信息)
2)图片检索图片信息:Bing网站(可考虑引入其他网站信息,或者腾讯云的COS存储桶图片信息处理)
3)接口检索接口信息:根据接口筛选条件检索(或者仅提供一个匹配规则,避免繁琐处理,通过like匹配)
4)文件类似知识库概念,检索文件资料

2.系统实现

引入聚合搜索模块接口相关内容,开发聚合搜素前端页面

参考开发网站:Ant Design 5.0open in new windowAnt Design中文open in new window国内镜像open in new window)、Protable(高级表格)open in new window

Ant Design Pro-中文版open in new windowProCompents 组件open in new window

聚合搜索页面

开发思路

【1】在config/routes.ts配置路由

// 聚合搜索模块
  {
    path: '/searchModule',
    icon: 'table',
    name: '聚合搜索',
    routes: [
      {
        path: '/searchModule/search',
        name: '数据检索',
        component: './User/Search',
        routes: [
          {
            path: '/searchModule/search',
            redirect: '/searchModule/search/articles',
          },
          {
            name: '图片检索',
            icon: 'smile',
            path: '/searchModule/search/pictures',
            component: './User/Search/pictures',
          },
          {
            name: '文章检索',
            icon: 'smile',
            path: '/searchModule/search/articles',
            component: './User/Search/articles',
          },
          {
            name: '接口检索',
            icon: 'smile',
            path: '/searchModule/search/interfaces',
            component: './User/Search/interfaces',
          },
        ],
      },
    ],
  },

【2】新建User/Search/index.tsx,初始化(聚合搜索页面主入口)

import { useModel } from '@umijs/max';
import React, { useState } from 'react';

const Search: React.FC = () => {
  const [type, setType] = useState<string>('account');
  const { setInitialState } = useModel('@@initialState');


  return (

    // 页面信息定义(search)
    <div className = "search">
      hello my chart
    </div>

  );
};
export default Search;

【3】页面设计(聚合搜索:一个搜素框,根据不同的搜索类型完成数据检索,并展示不同的信息,初定用户信息、文章信息、图片信息)

​ 此处页面构建有两种思路:

​ 思路1:参考antd pro脚手架的实现思路,在主页面配置PageContainer容器的tabList属性,然后控制tab页面和url联动,如果要将搜索和每个tab联动,则可通过页面跳转到指定的url进行页面刷新。此处每个tab页切换都会通过钩子函数useEffect更新数据(根据url传入参数进行更新),而搜索页和url的联动则是通过数据控制或者搜索按钮点击事件触发页面跳转。

​ 实现效果就是:点击搜索=》填充url并跳转页面;tab页面切换=》触发url跳转,然后每个子组件会校验url传入的参数并进行封装。

​ 分析:每次点击页面都会触发搜索,调用不同的接口完成检索数据并封装,其实现类似于页面嵌套,一个主页面里面嵌套不同子页面的实现,然后子页面根据主页面的检索参数进行数据检索,每个页面都调用接口完成数据封装

​ 【可以参考百度搜索,其也是点击搜索按钮触发页面更新,然后每个模块下有相应的数据】

​ 思路2:类似数据组合的形式,在index.tsx统一调用接口,一次性根据参数获取到所有内容(例如dataList中拆分多个不同的List组合查找所有的内容),其中不同类型的tab组件仅仅作为一个容器,根据生成的数据去渲染不同的生成效果

​ 分析:在主页面管理所有的组件内容,用户点击搜索后只需要调用一次聚合接口获取数据,随后将获取到的所有数据列表封装到对应的tab,减少前后端交互成本

实现参考

1)思路1实现:

​ 基于antd pro脚手架的list=》seach模板构建,通过切换tab页渲染不同的页面,每个页面可以看做是一个单独的组件页面(独立管理自己的组件内容和数据)

​ 此处补充search按钮事件触发和url的联动:

  • 定义全局搜索参数(初始化为从URL中获取的参数searchValue)
  • 修改Input.Search组件的defaultValue={searchValue},让它可以在url刷新的时候初始化搜索组件内容(此处不能是value,会强绑定无法修改,应该是defaultValue)
  • 配置搜索按钮触发事件,点击按钮调用setSearchValue更新值,然后通过url拼接并跳转页面
  • 每个子页面初始化的时候监控url传递的searchText参数并拼接自己的查找参数,调用方法初始化数据并渲染组件
import {PageContainer} from '@ant-design/pro-components';
import {history, Outlet, useLocation, useMatch} from '@umijs/max';
import {Input} from 'antd';
import type {FC} from 'react';
import React, {useState} from 'react';


type SearchProps = {
  children?: React.ReactNode;
};


const tabList = [
  {
    key: 'articles',
    tab: '文章',
  },
  {
    key: 'pictures',
    tab: '图片',
  },
  {
    key: 'interfaces',
    tab: 'API广场',
  },
];

const Search: FC<SearchProps> = () => {

  const location = useLocation();
  const searchParams = new URLSearchParams(location.search);

  // 定义全局搜索参数(初始化为从URL中获取的参数)
  const [searchValue, setSearchValue] = useState(() => {
    return searchParams.get('searchText') || '';
  });

  let match = useMatch(location.pathname);
  const handleTabChange = (key: string) => {
    const url =
      match?.pathname === '/' ? '' : match?.pathname.substring(0, match.pathname.lastIndexOf('/'));
    switch (key) {
      case 'articles':
        history.push(`${url}/articles` + '?searchText=' + searchValue);
        break;
      case 'pictures':
        // history.push(`${url}/pictures`);
        history.push(`${url}/pictures` + '?searchText=' + searchValue);
        break;
      case 'interfaces':
        history.push(`${url}/interfaces` + '?searchText=' + searchValue);
        break;
      default:
        console.log('错误参数');
        break;
    }
  };

  const getTabKey = () => {
    const tabKey = location.pathname.substring(location.pathname.lastIndexOf('/') + 1);
    if (tabKey && tabKey !== '/') {
      return tabKey;
    }
    return 'articles';
  };

  const handleFormSubmit = (value: string) => {
    // console.log(value);
    // 设置当前的搜索值
    setSearchValue(value);
    // handleTabChange('pictures');  按钮点击一次无响应、点击第二次url变化但是没有筛选结果

    // 跳转指定路径
    const url =
      match?.pathname === '/' ? '' : match?.pathname.substring(0, match.pathname.lastIndexOf('/'));
    // history.push(`${url}/${getTabKey()}`+ '?searchText=' + value);// push 只改变了参数 页面不刷新(如果不是切换tab的话url不变)
    window.location.href = `${url}/${getTabKey()}` + '?searchText=' + value;
  };

  return (
    <PageContainer
      content={
        <div style={{textAlign: 'center'}}>
          <Input.Search
            placeholder="请输入"
            enterButton="搜索"
            size="large"
            onSearch={handleFormSubmit}
            style={{maxWidth: 522, width: '100%'}}
            defaultValue={searchValue} // 此处不能用value,会被绑定(初始值设定即可)
          />
          <p>search参数监控:{searchValue}</p>
        </div>
      }
      tabList={tabList}
      tabActiveKey={getTabKey()}
      onTabChange={handleTabChange}
    >
      <Outlet/>
    </PageContainer>
  );
};

export default Search;
2)思路2实现:(符合聚合搜索的概念,减少前后台交互)

后台

1.ES改造(引入ES构建文章检索)

ES同步:全量同步实现构建思路

1)定时器:全量同步(系统启动时同步,全量同步)、增量同步(按照时间维度进行同步)

  • FullSyncFetchPostToEs:全量同步(@Component配置开启任务)
  • IncSyncFetchPostToEs:增量同步(@Component配置开启任务,配置定时定时器@Scheduled(fixedRate = 60 * 1000)
  • FetchPostEsDTO:爬取文章和ES转化包装类(类似ES的实体定义,@Document(indexName = "fetch_post")配置开启ES)
  • ES配置:application-remote.yml
spring:
# Elasticsearch 配置(elasticsearch启用需确保es服务正常启动)
  elasticsearch:
    uris: http://localhost:9200
    username: root
    password: 123456

​ ES数据检索:文章数据从ES中进行检索(由于线上ES环境需要4G内核支持,难顶,本地启动测试)

image-20240501103211958

2)借助其他渠道进行同步(例如Java代码提供入口实现同步),参考其他扩展

2.数据源接入(数据爬取)

​ 例如接入boss的数据源爬取,通过nest操作进行boss的JD信息爬取,随后进行数据入库。可以考虑将爬取的数据源信息组合进行数据分析。

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.1.3