accurateMatching.service.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import { Client } from '@elastic/elasticsearch';
  2. import { Config, Init, Inject, Provide } from '@midwayjs/core';
  3. import { Context } from '@midwayjs/koa';
  4. import { floor, get, head } from 'lodash';
  5. @Provide()
  6. export class AccurateMatchingService {
  7. @Config('elasticsearch')
  8. esConfig: object;
  9. /**es连接实例 */
  10. esClient: Client;
  11. @Inject()
  12. ctx: Context;
  13. @Init()
  14. async initClient() {
  15. const esClient = new Client(this.esConfig);
  16. this.esClient = esClient;
  17. }
  18. async demand(keyword: string, skip: number = 0, limit: number = 10) {
  19. const config = { index: 'demand', fields: ['name', 'tags', 'area', 'brief'] };
  20. return this.search(keyword, config, skip, limit);
  21. }
  22. async supply(keyword: string, skip: number = 0, limit: number = 10) {
  23. const config = { index: 'supply', fields: ['name', 'tags', 'area', 'brief'] };
  24. return this.search(keyword, config, skip, limit);
  25. }
  26. async achievement(keyword: string, skip: number = 0, limit: number = 10) {
  27. const config = { index: 'achievement', fields: ['name', 'tags', 'area', 'brief'] };
  28. return this.search(keyword, config, skip, limit);
  29. }
  30. async forDemandSearch(keyword: string, skip = 0, limit = 10, id) {
  31. const fields = ['name', 'tags', 'brief'];
  32. const config = { index: ['supply', 'achievement'], fields };
  33. const origin_index = 'demand';
  34. return await this.manyIndexsSearch(keyword, config, skip, limit, origin_index, id);
  35. }
  36. async forSupplySearch(keyword: string, skip = 0, limit = 10, id) {
  37. const fields = ['name', 'tags', 'brief'];
  38. const config = { index: ['demand', 'achievement'], fields };
  39. const origin_index = 'supply';
  40. return await this.manyIndexsSearch(keyword, config, skip, limit, origin_index, id);
  41. }
  42. /**精准匹配统一查询,使用简单的form+size分页模式.如果数据量大于1w,此处的分页模式应该修改,不过就会影响资源的占用及数据的实时性 */
  43. async search(keyword: string, config: object, skip: number, limit: number) {
  44. const index = get(config, 'index');
  45. const fields = get(config, 'fields', []);
  46. const result = await this.esClient.search({
  47. index,
  48. query: {
  49. bool: {
  50. must: [
  51. {
  52. multi_match: {
  53. query: keyword,
  54. fields,
  55. },
  56. },
  57. ],
  58. },
  59. },
  60. size: limit,
  61. from: skip,
  62. });
  63. const returnData = this.dealResponses(result);
  64. return returnData;
  65. }
  66. async manyIndexsSearch(keyword: string, config: object, skip: number, limit: number, origin_index: string, id?: number) {
  67. const index = get(config, 'index');
  68. const fields = get(config, 'fields', []);
  69. const user_id = get(this.ctx, 'user.id');
  70. const must: any = [
  71. {
  72. multi_match: {
  73. query: keyword,
  74. fields,
  75. type: 'best_fields',
  76. tie_breaker: 0.5,
  77. },
  78. },
  79. ];
  80. const must_not = [
  81. {
  82. match: {
  83. user: user_id,
  84. },
  85. },
  86. ];
  87. const filter = [];
  88. if (id) {
  89. const res = await this.esClient.search({
  90. index: origin_index,
  91. query: { bool: { must: { match: { id } } } },
  92. });
  93. const originResult = this.dealResponses(res);
  94. const industry = get(head(get(originResult, 'data')), 'industry');
  95. if (industry && industry !== '') {
  96. filter.push({ term: { industry } });
  97. }
  98. }
  99. const result = await this.esClient.search({
  100. index,
  101. query: {
  102. bool: {
  103. must,
  104. must_not,
  105. filter,
  106. },
  107. },
  108. // highlight: {
  109. // fields: {
  110. // name: {
  111. // pre_tags: ['<em>'],
  112. // post_tags: ['</em>'],
  113. // },
  114. // brief: {
  115. // pre_tags: ['<em>'],
  116. // post_tags: ['</em>'],
  117. // },
  118. // },
  119. // },
  120. size: limit,
  121. from: skip,
  122. });
  123. const returnData = this.dealResponses(result);
  124. return returnData;
  125. }
  126. dealResponses(result) {
  127. const total = get(result, 'hits.total.value', 0);
  128. const hits = get(result, 'hits.hits', []);
  129. const list = [];
  130. for (const ol of hits) {
  131. const _score = get(ol, '_score', 0);
  132. const _source = get(ol, '_index');
  133. const data = get(ol, '_source', {});
  134. let recommend = 0;
  135. /**推荐星数计算:超过5,就都是5颗星.未超过5的都向下取整 */
  136. if (_score >= 5) recommend = 5;
  137. else recommend = floor(_score);
  138. data._recommend = recommend;
  139. data._source = _source;
  140. list.push(data);
  141. }
  142. return { total, data: list };
  143. }
  144. }