123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- import { Client } from '@elastic/elasticsearch';
- import { Config, Init, Inject, Provide } from '@midwayjs/core';
- import { Context } from '@midwayjs/koa';
- import { floor, get, head } from 'lodash';
- @Provide()
- export class AccurateMatchingService {
- @Config('elasticsearch')
- esConfig: object;
- /**es连接实例 */
- esClient: Client;
- @Inject()
- ctx: Context;
- @Init()
- async initClient() {
- const esClient = new Client(this.esConfig);
- this.esClient = esClient;
- }
- async demand(keyword: string, skip: number = 0, limit: number = 10) {
- const config = { index: 'demand', fields: ['name', 'tags', 'area', 'brief'] };
- return this.search(keyword, config, skip, limit);
- }
- async supply(keyword: string, skip: number = 0, limit: number = 10) {
- const config = { index: 'supply', fields: ['name', 'tags', 'area', 'brief'] };
- return this.search(keyword, config, skip, limit);
- }
- async achievement(keyword: string, skip: number = 0, limit: number = 10) {
- const config = { index: 'achievement', fields: ['name', 'tags', 'area', 'brief'] };
- return this.search(keyword, config, skip, limit);
- }
- //由供给信息查询
- async forDemandSearch(keyword: string, skip = 0, limit = 10, id) {
- const fields = ['name', 'tags', 'brief'];
- const config = { index: ['demand', 'achievement'], fields };
- const origin_index = 'supply';
- return await this.manyIndexsSearch(keyword, config, skip, limit, origin_index, id);
- }
- // 由需求信息查询
- async forSupplySearch(keyword: string, skip = 0, limit = 10, id) {
- const fields = ['name', 'tags', 'brief'];
- const config = { index: ['supply', 'achievement'], fields };
- const origin_index = 'demand';
- return await this.manyIndexsSearch(keyword, config, skip, limit, origin_index, id);
- }
- /**精准匹配统一查询,使用简单的form+size分页模式.如果数据量大于1w,此处的分页模式应该修改,不过就会影响资源的占用及数据的实时性 */
- async search(keyword: string, config: object, skip: number, limit: number) {
- const index = get(config, 'index');
- const fields = get(config, 'fields', []);
- const result = await this.esClient.search({
- index,
- query: {
- bool: {
- must: [
- {
- multi_match: {
- query: keyword,
- fields,
- },
- },
- ],
- },
- },
- size: limit,
- from: skip,
- });
- const returnData = this.dealResponses(result);
- return returnData;
- }
- async manyIndexsSearch(keyword: string, config: object, skip: number, limit: number, origin_index: string, id?: number) {
- const index = get(config, 'index');
- // const fields = get(config, 'fields', []);
- const user_id = get(this.ctx, 'user.id');
- // const must: any = [
- // {
- // match: {
- // name: {
- // query: keyword,
- // },
- // },
- // },
- // ];
- const must_not = [
- {
- match: {
- user: user_id,
- },
- },
- ];
- const should: any = [
- {
- constant_score: {
- filter: {
- bool: {
- must: {
- match: { name: keyword },
- },
- },
- },
- boost: 3,
- },
- },
- ];
- const queries = [];
- queries.push({ bool: { must_not, should: { match: { name: keyword } } } });
- let fieldValue, industryValue;
- if (id) {
- const res = await this.esClient.search({
- index: origin_index,
- query: { bool: { must: { match: { id } } } },
- });
- const originResult = this.dealResponses(res);
- const industry = get(head(get(originResult, 'data')), 'industry');
- // 产业
- if (industry && industry !== '') {
- industryValue = industry;
- should.push({
- constant_score: {
- filter: {
- bool: {
- must: {
- term: { industry },
- },
- },
- },
- boost: 1,
- },
- });
- }
- // 技术领域
- const field = get(head(get(originResult, 'data')), 'field');
- if (field && field !== '') {
- fieldValue = field;
- should.push({
- constant_score: {
- filter: {
- bool: {
- must: {
- match: { field },
- },
- },
- },
- boost: 1,
- },
- });
- }
- }
- if (fieldValue) {
- queries.push({ match: { field: fieldValue } });
- }
- if (industryValue) {
- queries.push({ term: { industry: industryValue } });
- }
- const result = await this.esClient.search({
- index,
- // constant_score方式
- // query: {
- // bool: {
- // must_not,
- // should,
- // minimum_should_match: 1,
- // },
- // },
- // dis_max方式
- query: {
- dis_max: {
- queries,
- tie_breaker: 0.3,
- boost: 0.7,
- },
- },
- // min_score: 3,
- size: limit,
- from: skip,
- explain: true,
- });
- const returnData = this.dealResponses(result);
- return returnData;
- }
- dealResponses(result) {
- const total = get(result, 'hits.total.value', 0);
- const hits = get(result, 'hits.hits', []);
- const list = [];
- for (const ol of hits) {
- const _score = get(ol, '_score', 0);
- const _source = get(ol, '_index');
- const data = get(ol, '_source', {});
- let recommend = 0;
- /**推荐星数计算:超过5,就都是5颗星.未超过5的都向下取整 */
- if (_score >= 5) recommend = 5;
- else recommend = floor(_score);
- data._recommend = recommend;
- data._source = _source;
- list.push(data);
- }
- return { total, data: list };
- }
- }
|