APP搜索设计案例demo
use Elasticsearch\ClientBuilder;
public static function singleton()
{
if (empty(self::$client)) {
$hosts = self::getHost();
$client = ClientBuilder::create()->setHosts($hosts)->build();
self::$client = $client;
}
return self::$client;
}
[数据同步核心代码]
public static function addAppV2($appInfo)
{
$isIos = intval(stripos($appInfo['platform'], 'IOS') !== false);
$isAz = intval(stripos($appInfo['platform'], '安卓') !== false);
$isH5 = intval(stripos($appInfo['platform'], 'H5') !== false);
$isYgj = intval(stripos($appInfo['platform'], 'YGJ') !== false);
$isYyx = intval(stripos($appInfo['platform'], 'PCYYX') !== false);
$isAzH5 = intval($isAz || $isH5);
$isIosH5 = intval($isIos || $isH5);
$param = [
'index' => self::APP_INDEX_V2,
'id' => $appInfo['id'],
'body' => [
'id' => $appInfo['id'],
'state' => $appInfo['state'],
'is_ios' => $isIos,
'is_az' => $isAz,
'is_ios_h5' => $isIosH5,
'is_az_h5' => $isAzH5,
'is_ygj' => $isYgj,
'is_h5' => $isH5,
'is_pc_yyx' => $isYyx,
'classid' => $appInfo['classid'],
'app_id' => $appInfo['app_id'],
'is_az_h5_pc_yyx' => intval($isYyx || $isAzH5),
'game_name' => $appInfo['main_title'],
'subtitle' => $appInfo['subtitle'] ?? '',
'title' => $appInfo['new_title'],
'mix_type' => $appInfo['mix_type'] ?? 100,
]
];
$addIndex = self::singleton()->index($param);
return $addIndex;
}
[搜索核心代码]
同步游戏数据到es数据库,通过function_score 设置权重
游戏名中含有 关键字 完全匹配权重设置10000
搜索词包含在标题中,且比例 ≥ 60% → 高权重
标题包含在搜索词中,且比例 ≤ 200% → 高权重
下载加权 5
日活加权 15
支付加权80
private function searchGameIdsV21()
{
$keyword = $this->keyword;
$must = $mustNot = $scoreFun = [];
$should[] = [
'match' => [
'game_name.keyword' => [
'query' => $keyword,
'fuzziness' => floor(mb_strlen($keyword) / 4), // 表示容错字符
'boost' => 1
]
]
];
$should[] = [
'match' => [
'title.keyword' => [
'query' => $keyword,
'fuzziness' => floor(mb_strlen($keyword) / 3),
'boost' => 1
]
]
];
$should[] = [
'match' => [
'cates.keyword' => [
'query' => $keyword,
'fuzziness' => 1,
'boost' => 1
]
]
];
$should[] = [
'multi_match' => [
'query' => $keyword,
'fields' => [
'game_name', 'subtitle'
],
'boost' => 1,
]
];
// 增加包含全量游戏名的权重
$scoreFun[] = [
'filter' => [
'term' => [
'game_name.keyword' => $keyword,
]
],
'weight' => 10000
];
$scoreFun[] = [
'filter' => [
'term' => [
'title.keyword' => $keyword,
]
],
'weight' => 10000
];
$scoreFun[] = [
'filter' => [
'script' => [
'script' => [
'source' => "String title = doc['title.keyword'].value;title.contains(params.query) && (params.query.length() / (double) Math.round(doc['title_length'].value)) >= 0.6 || params.query.contains(title) && (params.query.length() / (double) Math.round(doc['title_length'].value)) <= 2",
'params' => [
'query' => $keyword
]
]
]
],
'weight' => 10000
];
$scoreFun[] = [
'filter' => [
'script' => [
'script' => [
'source' => "String title = doc['game_name.keyword'].value; title.contains(params.query) && (params.query.length() / (double) Math.round(doc['name_length'].value)) >= 0.6 || params.query.contains(title) && (params.query.length() / (double) Math.round(doc['name_length'].value)) <= 4",
'params' => [
'query' => $keyword
]
]
]
],
'weight' => 10000
];
// 近1日的累计实付*80%+昨日日活*15%+下载量*5%
$scoreFun[] = [
'script_score' => [
'script' => [
'source' => "1 + Math.log1p(doc['true_down'].value)"
]
],
'weight' => 5,
];
$scoreFun[] = [
'script_score' => [
'script' => [
'source' => "1 + Math.log1p(doc['yesterday_hy'].value)"
]
],
'weight' => 15,
];
$scoreFun[] = [
'script_score' => [
'script' => [
'source' => "1 + Math.log1p(Math.ceil(doc['real_pay'].value))"
]
],
'weight' => 80,
];
if (EcloudByService::isZkyCps() || AFrom::isADevice()) {
$filter[] = [
'match' => [
'is_az_h5_pc_yyx' => 1
]
];
} else {
$filter[] = [
'match' => [
'is_ios_h5' => 1
]
];
// iOS过滤模拟器游戏
$mustNot[] = [
'term' => [
'classid' => APP_CLASS_SIMULATOR,
]
];
}
$mustNot[] = [
'term' => [
'is_pc_yyx' => 1,
]
];
// 只展示预约、运营中
$mustNot[] = [
'terms' => [
'state' => [
XYApp::STATE_CLOSED,
XYApp::STATE_DISABLED,
XYApp::STATE_CLOSING,
XYApp::STATE_HX1,
XYApp::STATE_HX2,
]
]
];
$param = [
'index' => ElasticSearch::APP_INDEX_V3,
'body' => [
'query' => [
'function_score' => [
'query' => [
'bool' => []
],
'functions' => $scoreFun,
"score_mode" => "sum",
"boost_mode" => "sum"
],
],
'from' => ($this->page - 1) * $this->listRows,
'size' => $this->listRows,
'sort' => [
[
'_score' => ['order' => 'desc']
]
],
],
];
if ($must) {
$param['body']['query']['function_score']['query']['bool']['must'] = $must;
}
if ($mustNot) {
$param['body']['query']['function_score']['query']['bool']['must_not'] = $mustNot;
}
if ($filter) {
$param['body']['query']['function_score']['query']['bool']['filter'] = $filter;
}
$param['body']['query']['function_score']['query']['bool']['should'] = $should;
$param['body']['query']['function_score']['query']['bool']['minimum_should_match'] = 1;
$esData =self::singleton()->search($param);