|
@@ -0,0 +1,79 @@
|
|
|
+import { Client } from '@elastic/elasticsearch';
|
|
|
+import { Config, Init, Provide } from '@midwayjs/core';
|
|
|
+import { floor, get, head } from 'lodash';
|
|
|
+
|
|
|
+@Provide()
|
|
|
+export class AccurateMatchingService {
|
|
|
+ @Config('elasticsearch')
|
|
|
+ esConfig: object;
|
|
|
+ /**es连接实例 */
|
|
|
+ esClient: Client;
|
|
|
+ @Init()
|
|
|
+ async initClient() {
|
|
|
+ const esClient = new Client(this.esConfig);
|
|
|
+ this.esClient = esClient;
|
|
|
+ }
|
|
|
+
|
|
|
+ async demand(keyword: string, skip: number = 0, limit: number = 10) {
|
|
|
+ const configs = [
|
|
|
+ { index: 'supply', fields: ['name', 'tags', 'area', 'brief'] },
|
|
|
+ { index: 'achievement', fields: ['name', 'tags', 'area', 'brief'] },
|
|
|
+ ];
|
|
|
+ return this.search(keyword, configs, skip, limit);
|
|
|
+ }
|
|
|
+
|
|
|
+ async supply(keyword: string, skip: number = 0, limit: number = 10) {
|
|
|
+ const configs = [
|
|
|
+ { index: 'demand', fields: ['name', 'tags', 'area', 'brief'] },
|
|
|
+ { index: 'achievement', fields: ['name', 'tags', 'area', 'brief'] },
|
|
|
+ ];
|
|
|
+ return this.search(keyword, configs, skip, limit);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**精准匹配统一查询,使用简单的form+size分页模式.如果数据量大于1w,此处的分页模式应该修改,不过就会影响资源的占用及数据的实时性 */
|
|
|
+ async search(keyword: string, configs: Array<object>, skip: number, limit: number) {
|
|
|
+ const searches = [];
|
|
|
+ for (const c of configs) {
|
|
|
+ searches.push({ index: get(c, 'index') });
|
|
|
+ const obj = {
|
|
|
+ query: { bool: { must: [{ multi_match: { query: keyword, fields: get(c, 'fields', []) } }] } },
|
|
|
+ sort: [{ _score: { order: 'desc' } }],
|
|
|
+ size: limit,
|
|
|
+ from: skip,
|
|
|
+ };
|
|
|
+ searches.push(obj);
|
|
|
+ }
|
|
|
+ const result = await this.esClient.msearch({
|
|
|
+ searches,
|
|
|
+ });
|
|
|
+ const returnData = this.dealResponses(result);
|
|
|
+ return returnData;
|
|
|
+ }
|
|
|
+
|
|
|
+ dealResponses(result) {
|
|
|
+ const returnData = [];
|
|
|
+ const responses = get(result, 'responses');
|
|
|
+ for (const res of responses) {
|
|
|
+ const status = get(res, 'status');
|
|
|
+ if (status !== 200) continue;
|
|
|
+ const hits = get(res, 'hits', []);
|
|
|
+ if (!hits) continue;
|
|
|
+ const total = get(hits, 'total.value', 0);
|
|
|
+ const originList = get(hits, 'hits', []);
|
|
|
+ const list = [];
|
|
|
+ for (const ol of originList) {
|
|
|
+ const _score = get(ol, '_score', 0);
|
|
|
+ const data = get(ol, '_source', {});
|
|
|
+ let recommend = 0;
|
|
|
+ /**推荐星数计算:超过5,就都是5颗星.未超过5的都向下取整 */
|
|
|
+ if (_score >= 5) recommend = 5;
|
|
|
+ else recommend = floor(_score);
|
|
|
+ data._recommend = recommend;
|
|
|
+ list.push(data);
|
|
|
+ }
|
|
|
+ const table = get(head(originList), '_index');
|
|
|
+ returnData.push({ data: list, total, table });
|
|
|
+ }
|
|
|
+ return returnData;
|
|
|
+ }
|
|
|
+}
|