Skip to content

Commit 1d3b1b8

Browse files
author
vikasrohit
authored
Merge pull request #103 from topcoder-platform/feature/projectSearchUpdate
Feature/project search update
2 parents 993a19d + 1db1342 commit 1d3b1b8

File tree

1 file changed

+94
-27
lines changed

1 file changed

+94
-27
lines changed

src/routes/projects/list.js

Lines changed: 94 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ import util from '../../util';
1010

1111
const ES_PROJECT_INDEX = config.get('elasticsearchConfig.indexName');
1212
const ES_PROJECT_TYPE = config.get('elasticsearchConfig.docType');
13+
14+
const MATCH_TYPE_EXACT_PHRASE = 1;
15+
const MATCH_TYPE_WILDCARD = 2;
16+
const MATCH_TYPE_SINGLE_FIELD = 3;
17+
1318
/**
1419
* API to handle retrieving projects
1520
*
@@ -40,6 +45,59 @@ const PROJECT_PHASE_PRODUCTS_ATTRIBUTES = _.without(
4045

4146

4247
const escapeEsKeyword = keyword => keyword.replace(/[+-=><!|(){}[&\]^"~*?:\\/]/g, '\\\\$&');
48+
49+
const buildEsFullTextQuery = (keyword, matchType, singleFieldName) => {
50+
let should = [
51+
{
52+
query_string: {
53+
query: (matchType === MATCH_TYPE_EXACT_PHRASE) ? keyword : `*${keyword}*`,
54+
analyze_wildcard: (matchType === MATCH_TYPE_WILDCARD),
55+
fields: ['name^5', 'description^3', 'type^2'],
56+
},
57+
},
58+
{
59+
nested: {
60+
path: 'details',
61+
query: {
62+
nested: {
63+
path: 'details.utm',
64+
query: {
65+
query_string: {
66+
query: (matchType === MATCH_TYPE_EXACT_PHRASE) ? keyword : `*${keyword}*`,
67+
analyze_wildcard: (matchType === MATCH_TYPE_WILDCARD),
68+
fields: ['details.utm.code^4'],
69+
},
70+
},
71+
},
72+
},
73+
},
74+
},
75+
{
76+
nested: {
77+
path: 'members',
78+
query: {
79+
query_string: {
80+
query: (matchType === MATCH_TYPE_EXACT_PHRASE) ? keyword : `*${keyword}*`,
81+
analyze_wildcard: (matchType === MATCH_TYPE_WILDCARD),
82+
fields: ['members.email', 'members.handle', 'members.firstName', 'members.lastName'],
83+
},
84+
},
85+
},
86+
},
87+
];
88+
89+
if (matchType === MATCH_TYPE_SINGLE_FIELD && singleFieldName === 'ref') {
90+
// only need to match the second item in the should array
91+
should = should.slice(1, 2);
92+
}
93+
94+
return {
95+
bool: {
96+
should,
97+
},
98+
};
99+
};
100+
43101
/**
44102
* Parse the ES search criteria and prepare search request body
45103
*
@@ -126,41 +184,50 @@ const parseElasticSearchCriteria = (criteria, fields, order) => {
126184
if (_.has(criteria, 'filters.keyword')) {
127185
// keyword is a full text search
128186
// escape special fields from keyword search
129-
const keyword = escapeEsKeyword(criteria.filters.keyword);
130-
fullTextQuery = {
131-
bool: {
132-
should: [
133-
{
134-
query_string: {
135-
query: `*${keyword}*`,
136-
analyze_wildcard: true,
137-
fields: ['name^3', 'description', 'type'], // boost name field
138-
},
139-
},
140-
{
141-
nested: {
142-
path: 'members',
143-
query: {
144-
query_string: {
145-
query: `*${keyword}*`,
146-
analyze_wildcard: true,
147-
fields: ['members.email', 'members.handle', 'members.firstName', 'members.lastName'],
148-
},
149-
},
150-
},
151-
},
152-
],
153-
},
154-
};
187+
const keywordCriterion = criteria.filters.keyword;
188+
let keyword;
189+
let matchType;
190+
let singleFieldName;
191+
// check exact phrase match first (starts and ends with double quotes)
192+
if (keywordCriterion.startsWith('"') && keywordCriterion.endsWith('"')) {
193+
keyword = keywordCriterion;
194+
matchType = MATCH_TYPE_EXACT_PHRASE;
195+
}
196+
197+
if (keywordCriterion.indexOf(' ') > -1 || keywordCriterion.indexOf(':') > -1) {
198+
const parts = keywordCriterion.split(' ');
199+
if (parts.length > 0) {
200+
for (let i = 0; i < parts.length; i += 1) {
201+
const part = parts[i].trim();
202+
if (part.length > 4 && part.startsWith('ref:')) {
203+
keyword = escapeEsKeyword(part.substring(4));
204+
matchType = MATCH_TYPE_SINGLE_FIELD;
205+
singleFieldName = part.substring(0, 3);
206+
break;
207+
}
208+
}
209+
}
210+
}
211+
212+
if (!keyword) {
213+
// Not a specific field search nor an exact phrase search, do a wildcard match
214+
keyword = escapeEsKeyword(criteria.filters.keyword);
215+
matchType = MATCH_TYPE_WILDCARD;
216+
}
217+
218+
fullTextQuery = buildEsFullTextQuery(keyword, matchType, singleFieldName);
155219
}
156220
const body = { query: { } };
157221
if (boolQuery.length > 0) {
158222
body.query.bool = {
159-
must: boolQuery,
223+
filter: boolQuery,
160224
};
161225
}
162226
if (fullTextQuery) {
163227
body.query = _.merge(body.query, fullTextQuery);
228+
if (body.query.bool) {
229+
body.query.bool.minimum_should_match = 1;
230+
}
164231
}
165232

166233
if (fullTextQuery || boolQuery.length > 0) {

0 commit comments

Comments
 (0)