内容中心-搜索设计

343 阅读5分钟

内容中心-搜索设计

项目背景:

收到一个P0级产品需求

简单说下产品需求:

将系统重已有的一些独立内容模块(文章、视频、图片、海报、商品、话术.....)整合到一起。包含收藏、常用、筛选、搜索、历史搜索记录、搜索结果高亮等配套功能。

模型设计:

内容实体模型:

// SearchContent 搜索内容结构
type SearchContent struct {
	ID                 string    `json:"id"`
	Type               string    `json:"contentType,omitempty"`          // 内容类型
	SubType            string    `json:"subContentType,omitempty"`       // 内容子类型
	Content            []Content `json:"content,omitempty"`              // 文本内容对象数组 (标题、副标题、分享标题、文本内容、摘要等需要进行搜索的文案)
	CategoryId         string    `json:"categoryId,omitempty"`           // 文件夹ID
	LastSendTime       int64     `json:"lastSendTime,omitempty"`         // 最近一次发送时间
	CreateUser         string    `json:"createUser,omitempty"`           // 创建人ID
	UpdateTime         int64     `json:"updateTime,omitempty"`           // 最近更新时间
	CreateTime         int64     `json:"createTime,omitempty"`           // 创建时间
	OnlineTime         int64     `json:"onlineTime"`                     // 上架时间 - 起点
	OfflineTime        int64     `json:"offlineTime"`                    // 上架时间 - 截止
	VisibleUserIDs     []string  `json:"visibleUserIDs"`                 // 可见权限-员工
	VisibleDepartments []int64   `json:"visibleDepartments"`             // 可见权限-部门
	SendCount          uint32     `json:"sendCount,omitempty"`            // 总发送次数
	Rank1             uint32     `json:"rank1"`                         // 排序字段
	Rank2            uint32     `json:"rank2"`                        // 排序字段2
	Rank3            uint32     `json:"rank3"`                        // 排序字段3
}

// Content 内容详情
type Content struct {
	ID            string `json:"id,omitempty"`                   
	Title         string `json:"title,omitempty"`                // 标题
	Text          string `json:"text,omitempty"`                 // 内容详情
	Summary       string `json:"summary,omitempty"`              // 摘要
	Type          string `json:"contentType,omitempty"`          // 内容类型
	Tags          []Tag  `json:"tags,omitempty"`
}

// Tag 内容标签
type Tag struct {
	ID   string `json:"id,omitempty"` // 标签ID
	Name string `json:"name"`         // 标签名称
}

模型设计思路:

之所以会设计成用content结构嵌套,一是由于话术的特性(一个话术可以包含N个子话术,而子话术可以是除话术外的所有类型内容实体),二是层级的加入使得模型整体结构不至于那么臃肿。

数据同步

那么这么多内容,如何同步到es中呢?要知道这些内容都存在于不同的表甚至存在于不同的数据库中,甚至可能来自于不同的三方平台。

需要考虑的主要问题

1、  数据最终一致性

2、  实时性

3、  数据竞争

结合以上3点主要问题,我们采用了以下设计方案

为避免数据竞争,这里采用了pulsar/kafka的shared模式,key使用binlog的主键ID,这样保证了同一条数据只会被传输到同一个patition中,实现了数据的有序性,当然这里也可以用有序队列进行替换。

而到了server这里层, 不信任binlog的值,而是采用主键id点查的方式查询最新数据进行更新,也就规避了数据最终一致性的问题。当然这样做也是有代价的,数据库的io也会有一定增长。

采用了这一套同步机制,实时性根据量级会有一定的牺牲。

设计模式的选择

策略模式

上述说到,内容中心es中涵盖了所有类别的内容实体,那么这个时候运用策略模式,根据不同的SearchContent.Type走各个实体自己的逻辑无疑是很好的选择之一。

注册策略

var (
	searchCenterStrategy = make(map[entity.ContentType]func(ctx context.Context) IntelligentSearchTranslator)
)

// Register 注册策略模式
func Register(t []entity.ContentType, f func(ctx context.Context) IntelligentSearchTranslator) {
	for _, tp := range t {
		searchCenterStrategy[tp] = f
	}
}

好的,策略已注入,IntelligentSearchTranslator这个下面会提到,不急不急

简易工厂模式

interface设计

// IntelligentSearchTranslator 数据转换器
type IntelligentSearchTranslator interface {
	// 转换为更新插入的数据结构
	TranslateForUpdate(contentId, contentType string) (data entity.SearchContent, exist bool, err error)
	// 查询详情并转换为统一结构
	SearchAndTranslate(contentIds []string, dataList []entity.SearchContent) ([]vo.SearchResponse, error)
}

这里,IntelligentSearchTranslator作为转换器,只实现2种功能

1、  TranslateForUpdate将源数据转换为SearchContent格式,也就是es中的数据格式

2、  SearchAndTranslate在查询时使用,反查数据详情

有同学可能在想,SearchAndTranslate这个方法是不是有点冗余了,把数据全部存到es中,直接反查不就好了吗?

针对这个问题,需要看业务场景,像一些简单的场景下,只需要反显一些简单的text,那么也就无需这个操作。但如果涉及到补充的数据过多,有的甚至有关联的数据,那么关联的数据是不是也要监听并同步到es中呢?这样设计的话反而更复杂了。 例如:商品item需要展示店铺详情,店铺信息本身不参与搜索,但是要反显到列表中,这时候SearchAndTranslate反查一下信息就好,但如果存到es中,还要多监听店铺信息的表信息,设计上会很麻烦。再例如:若果反查的信息需要调用三方接口,那是否得要求三方接口提供回调功能,否则无法监听数据变更。

分页设计

es的深度分页是避不开的问题,那么整合下es的分页方式,有如下比较。结合实际情况选取适合的分页方式。

分页方式优势劣势
from + size实时性强1、  可能出现数据index变更2、  无法深度分页
scroll深度分页不受数据增、删影响1、  查询期间无法感知数据的增和删2、  内存消耗大3、  无法连续分页(跳页
scroll scanscroll升级版,性能有提升1、2、3与scroll相同4、不支持排序
search after性能最佳,无深度分页问题无法连续分页(跳页)但最低需求7.1.0版本

小结

内容中心的搜索设计大体上就如上所述。本文旨在分享下使用es做搜索引擎以及数据同步的设计,如果各位有做过类似功能或者有更好建议欢迎在评论区讨论。下期分享下es的一些语法,以及如何用这些语法实现复杂的业务功能。