跳转到内容

DS-Search概述

DS提供了搜索模型功能,通过查询搜索引擎来提高查询性能。对于搜索模型,DS支持将该搜索模型的业务数据从DB同步到ES建索引,并支持查询ES获取数据,从而提高业务模型数据模糊搜索性能。

搜索模型与业务模型的关系

搜索模型是从一个或者多个业务模型中抽取字段组成的便于查询的新模型。因此在业务模型发生数据改变时,需要同步修改搜索模型的值,可以将修改业务模型和修改搜索模型的代码放在同一个事务里,保证其一致性。

1 搜索模型定义

搜索模型的定义方式: 定义的时候添加两个注解@Model和@SearchModel。该模型添加后DS会根据模型定义生成索引到搜索引擎上, 当模型发生变更时,需要用户手动执行全量同步来同步变更的模型信息。 当模型数据发生变更时,DS会自动将变化同步至搜索引擎,但是搜索内容会有所延迟,但大概率优化极限是接近1秒。 搜索模型的字段类型只支持Trantor中的普通类型,比如:Integer、Long、Json、String、BigDecimal等。关联字段Link和Relation都不支持。

下面介绍搜索模型的创建,TradeOrderSO是基于TradeOrderBO和TradeOrderLineBO中需要被查询的字段组建的新模型。

TradeOrderBO模型:

@Data
@Model(name = "Trade Order")
public class TradeOrderBO extends BaseModel<Long> {
/**
* 支付状态
*/
@DictionaryMeta(PayStatusDict.class)
@Field(name = "Pay status", desc = "支付状态", defaultValue = PayStatusDict.UNPAID)
private String payStatus;
/**
* 地理位置类型,此字段表示一个坐标点
*/
@Field(name = "GeoLocation")
private GeoLocation geoLocation;
/**
*地理位置类型,此字段表示一个区域(区域下有三个子类: 矩形,圆形,多边形)
*/
@Field(name = "GeoArea")
private GeoArea geoArea;
/**
* 关联的交易订单行
*/
@LookupMeta
@Field(name = "Trade order line list")
private List<TradeOrderLineBO> tradeOrderLineList;
/**
* 订单标题
*/
@TextMeta(length = 64)
@Field(name = "Trade order title")
private String tradeOrderTitle;
}

TradeOrderLineBO模型:

@Data
@Model(name = "Trade order line")
public class TradeOrderLineBO extends BaseModel<Long> {
/**
* 关联的交易订单
*/
@LinkMeta
@Field(name = "Trade order")
private TradeOrderBO tradeOrder;
/**
* sku
*/
@Field(name = "Sku", type = FieldType.Json, desc = "Sku")
private SkuBO sku;
}

搜索模型TradeOrderSO:

@Data
@SearchModel(name = "TradeOrder search model")
@Model(name = "Trade Order SO")
public class TradeOrderSO extends BaseModel<Long> {
@Field(name = "TradeOrder id")
private Long tradeOrderId;
@Field(name = "TradeOrderLineBO模型的skuId列表", type = FieldType.Json)
private List<Long> skuIds;
@Field(name = "TradeOrderLineBO模型的skuName列表", type = FieldType.Json)
private List<String> skuNames;
@DictionaryMeta(PayStatusDict.class)
@Field(name = "Pay status", desc = "支付状态", defaultValue = PayStatusDict.UNPAID)
private String payStatus;
@TextMeta(length = 64)
@Field(name = "Trade order title")
private String tradeOrderTitle;
@Field(name = "GeoLocation")
private GeoLocation geoLocation;
@Field(name = "GeoArea")
private GeoArea feoArea;
}

当TradeOrderBO数据发生变化时,要同时修改TradeOrderSO中的数据,并放在同一个事务里面,保证其一致性。 下面以订单支付成功后,更新订单支付状态为例。

@DSTransaction
public void execute() {
TradeOrderBO tradeOrderBO=DS.findById(TradeOrderBO.class ,1L);
tradeOrderBO.setPayStatus(PayStatusDict.PAID);
DS.update(tradeOrderBO);
//查询tradeOrderBO对应的所有订单搜索模型列表
List<TradeOrderSO> existedTradeOrderSOList = DS.findAll(TradeOrderSO.class,
"*",
"tradeOrderId = ?",
tradeOrderBO.getId()
);
//更新搜索模型的支付状态
updateTradeOrderSOPayStatusFunc.execute(tradeOrderBO, existedTradeOrderSOList);
}

2 数据同步

模型定义结束后,DS会根据模型源信息将其同步到搜索引擎上创建索引。当业务端需要进行数据变更时,DS提供了两种数据同步到ES上的方式。

  • 需要手动操作的全量同步
  • DS实现的增量同步

2.1 全量同步

搜索模型在执行模型变更的时候,需要执行一次全量同步将持久化模型修改后的模型定义同步到ES上。同时当ES上数据出现问题,或者有旧数据未同步到ES上,可通过全量同步按钮将数据库中的数据全部同步到ES上。 操作方法:可在交付控制台上通过按钮操作 全量同步执行路径.png

2.2 增量同步

增量同步是由DS内部实现,不需要业务端进行额外的操作。当业务进行数据增删改时,DS会自动将变更的数据同步到ES上。具体原理是DS先将变更的数据保存到数据库,然后发消息给另一个服务Search,通过Search服务将变更的数据同步到ES上。

3 数据搜索

3.1 搜索API

Trantor通过Search API来实现所有搜索的操作。Search API提供有两种搜索方式,一是TSQL方式,一种是SearchQuery方式。

  • TSQL方式可实现常见的搜索,不支持特殊场景比如涉及权重,Geo运算,特殊场景可通过SearchQuery方式实现。
  • SearchQuery方式支持所有搜索。

3.1.1 TSQL方式

搜索包含’订单标题’的tradeOrderTitle字段并且按照payStatus倒序的前二十条TradeOrderSO。

String select = "*";
String where = "tradeOrderTitle like '%订单标题%' order by payStatus desc";
Paging<TradeOrderSO> tradeOrderSOList=Search.paging(TradeOrderSO.class,select,where,new Page(0,20));

使用TSQL方式搜索时不支持以别名搜索

使用TSQL方式时,可以使用别名的方式来搜索,但是搜索模型不支持以这种方式来搜索ES。 例如生成这样的代码:

//TSQL方式 是不支持 from TradeOrderSO as c 这样的语法的。
String sql = "select c.id as cid from TradeOrderSO as c where c.`tradeOrderTitle` = '订单标题' group by payStatus";
List<Map<String, Object>> map=Search.find(sql);

另外需要注意点: 目前Search.find()方法只用来需要查询分组的数据。如果不是分组的SQL,请使用paging()方法。

详细的操作方法可查看TSQL文档。

3.1.2 SearchQuery方式

下面是一个包含权重,Geo运算等高级功能的搜索:查看id等于1或者2或者3(查询权重为2)和覆盖地理位置类型coordinates(默认权重为1)的geoArea列表。

SearchQuery searchQuery=new SearchQuery();
//根据俄下面的type类型设置,coordinates表示一个矩形
List<Location> coordinates=new LinkedList<>();
coordinates.add(new Location(100.3, 1.0));//左上角坐标点
coordinates.add(new Location(101.0, 0.0));//右上角坐标点
searchQuery.where(a->a.bool()
.must(a.termsQuery(TradeOrderSO.id_field,1,23).boosts(2.00f))
.filter(a.geoShapeSearch(TradeOrderSO.geoArea_field)
.relation(ShapeRelation.contains) //表示搜索出覆盖coordinates的geoArea
.locations(coordinates)
.type(ShapeType.envelope)));//表示coordinates的形状是一个矩形
Paging<TradeOrderSO> tradeOrderSOList=Search.searchDSL(TradeOrderSO.class ,new Page(0,20),searchQuery);

根据JSON字段查询TradeOrderSO列表

SearchQuery searchQuery = new SearchQuery();
//搜索出categoryIds列表中有一个元素在(1,2,3)中的TradeOrderSO
searchQuery.where(a -> a.bool()
.must(a.termsQuery(TradeOrderSO.skuIds_field, "1", "2", "3").boosts(2.00f)));
Paging<TradeOrderSO> tradeOrderSOList=Search.searchDSL(TradeOrderSO.class ,new Page(0,20),searchQuery);

根据String字段查询TradeOrderSO列表

SearchQuery searchQuery = new SearchQuery();
searchQuery.where(f -> f.bool()
.must(f.matchQuery(TradeOrderSO.tradeOrderTitle_field,"姓名")))
.sort(TradeOrderSO.payStatus_field,Order.DESC));
Paging<TradeOrderSO> tradeOrderSOList=Search.searchDSL(TradeOrderSO.class,new Page(0,20),searchQuery);

详细的查询API可查看SearchApi文档。

3.2 查询时不支持的操作

不支持关联模型

搜索模型的设计是一个索引的文档数据全部来自同一张数据库表,为了保证对数据库的查询压力可控,数据同步和数据查询性能都有保障。 不过可以使用JSON字段来实现关联模型的功能,比如TradeOrderSO中的tradeOrderLineIds和tradeOrderLineCodes分别代表TradeOrderBO模型关联的TradeOrderLineBo模型中id和bundledCode字段,修改和查询方式可参考上述文档描述。