搜索模块开发
搜索模块开发
扩展参考思路:
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.0、Ant Design中文(国内镜像)、Protable(高级表格)、
Ant Design Pro-中文版、ProCompents 组件
聚合搜索页面
开发思路
【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内核支持,难顶,本地启动测试)
2)借助其他渠道进行同步(例如Java代码提供入口实现同步),参考其他扩展
2.数据源接入(数据爬取)
例如接入boss的数据源爬取,通过nest操作进行boss的JD信息爬取,随后进行数据入库。可以考虑将爬取的数据源信息组合进行数据分析。