@@ -102,6 +102,87 @@ const buildEsFullTextQuery = (keyword, matchType, singleFieldName) => {
102102 } ;
103103} ;
104104
105+ /**
106+ * Build ES query search request body based on value, keyword, matchType and fieldName
107+ *
108+ * @param {String } value the value to build request body for
109+ * @param {String } keyword the keyword to query
110+ * @param {String } matchType wildcard match or exact match
111+ * @param {Array } fieldName the fieldName
112+ * @return {Object } search request body that can be passed to .search api call
113+ */
114+ const buildEsQueryWithFilter = ( value , keyword , matchType , fieldName ) => {
115+ let should = [ ] ;
116+ if ( value !== 'details' && value !== 'customer' && value !== 'manager' ) {
117+ should = _ . concat ( should , {
118+ query_string : {
119+ query : keyword ,
120+ analyze_wildcard : ( matchType === MATCH_TYPE_WILDCARD ) ,
121+ fields : fieldName ,
122+ } ,
123+ } ) ;
124+ }
125+
126+ if ( value === 'details' ) {
127+ should = _ . concat ( should , {
128+ nested : {
129+ path : 'details' ,
130+ query : {
131+ nested : {
132+ path : 'details.utm' ,
133+ query : {
134+ query_string : {
135+ query : keyword ,
136+ analyze_wildcard : ( matchType === MATCH_TYPE_WILDCARD ) ,
137+ fields : fieldName ,
138+ } ,
139+ } ,
140+ } ,
141+ } ,
142+ } ,
143+ } ) ;
144+ }
145+
146+ if ( value === 'customer' || value === 'manager' ) {
147+ should = _ . concat ( should , {
148+ nested : {
149+ path : 'members' ,
150+ query : {
151+ bool : {
152+ must : [
153+ { match : { 'members.role' : value } } ,
154+ {
155+ query_string : {
156+ query : keyword ,
157+ analyze_wildcard : ( matchType === MATCH_TYPE_WILDCARD ) ,
158+ fields : fieldName ,
159+ } ,
160+ } ,
161+ ] ,
162+ } ,
163+ } ,
164+ } ,
165+ } ) ;
166+ }
167+
168+ return should ;
169+ } ;
170+
171+ /**
172+ * Prepare search request body based on wildcard query
173+ *
174+ * @param {String } value the value to build request body for
175+ * @param {String } keyword the keyword to query
176+ * @param {Array } fieldName the fieldName
177+ * @return {Object } search request body that can be passed to .search api call
178+ */
179+ const setFilter = ( value , keyword , fieldName ) => {
180+ if ( keyword . indexOf ( '*' ) > - 1 ) {
181+ return buildEsQueryWithFilter ( value , keyword , MATCH_TYPE_WILDCARD , fieldName ) ;
182+ }
183+ return buildEsQueryWithFilter ( value , keyword , MATCH_TYPE_EXACT_PHRASE , fieldName ) ;
184+ } ;
185+
105186/**
106187 * Parse the ES search criteria and prepare search request body
107188 *
@@ -152,13 +233,42 @@ const parseElasticSearchCriteria = (criteria, fields, order) => {
152233 }
153234 // prepare the elasticsearch filter criteria
154235 const boolQuery = [ ] ;
236+ let mustQuery = [ ] ;
155237 let fullTextQuery ;
156238 if ( _ . has ( criteria , 'filters.id.$in' ) ) {
157239 boolQuery . push ( {
158240 ids : {
159241 values : criteria . filters . id . $in ,
160242 } ,
161243 } ) ;
244+ } else if ( _ . has ( criteria , 'filters.id' ) && criteria . filters . id . indexOf ( '*' ) > - 1 ) {
245+ mustQuery = _ . concat ( mustQuery , buildEsQueryWithFilter ( 'id' , criteria . filters . id , MATCH_TYPE_WILDCARD , [ 'id' ] ) ) ;
246+ } else if ( _ . has ( criteria , 'filters.id' ) ) {
247+ boolQuery . push ( {
248+ term : {
249+ id : criteria . filters . id ,
250+ } ,
251+ } ) ;
252+ }
253+
254+ if ( _ . has ( criteria , 'filters.name' ) ) {
255+ mustQuery = _ . concat ( mustQuery , setFilter ( 'name' , criteria . filters . name , [ 'name' ] ) ) ;
256+ }
257+
258+ if ( _ . has ( criteria , 'filters.code' ) ) {
259+ mustQuery = _ . concat ( mustQuery , setFilter ( 'details' , criteria . filters . code , [ 'details.utm.code' ] ) ) ;
260+ }
261+
262+ if ( _ . has ( criteria , 'filters.customer' ) ) {
263+ mustQuery = _ . concat ( mustQuery , setFilter ( 'customer' ,
264+ criteria . filters . customer ,
265+ [ 'members.firstName' , 'members.lastName' ] ) ) ;
266+ }
267+
268+ if ( _ . has ( criteria , 'filters.manager' ) ) {
269+ mustQuery = _ . concat ( mustQuery , setFilter ( 'manager' ,
270+ criteria . filters . manager ,
271+ [ 'members.firstName' , 'members.lastName' ] ) ) ;
162272 }
163273
164274 if ( _ . has ( criteria , 'filters.status.$in' ) ) {
@@ -177,21 +287,6 @@ const parseElasticSearchCriteria = (criteria, fields, order) => {
177287 } ) ;
178288 }
179289
180- if ( _ . has ( criteria , 'filters.type.$in' ) ) {
181- // type is an array
182- boolQuery . push ( {
183- terms : {
184- type : criteria . filters . type . $in ,
185- } ,
186- } ) ;
187- } else if ( _ . has ( criteria , 'filters.type' ) ) {
188- // type is simple string
189- boolQuery . push ( {
190- term : {
191- type : criteria . filters . type ,
192- } ,
193- } ) ;
194- }
195290 if ( _ . has ( criteria , 'filters.keyword' ) ) {
196291 // keyword is a full text search
197292 // escape special fields from keyword search
@@ -222,7 +317,7 @@ const parseElasticSearchCriteria = (criteria, fields, order) => {
222317
223318 if ( ! keyword ) {
224319 // Not a specific field search nor an exact phrase search, do a wildcard match
225- keyword = escapeEsKeyword ( criteria . filters . keyword ) ;
320+ keyword = criteria . filters . keyword ;
226321 matchType = MATCH_TYPE_WILDCARD ;
227322 }
228323
@@ -234,17 +329,22 @@ const parseElasticSearchCriteria = (criteria, fields, order) => {
234329 filter : boolQuery ,
235330 } ;
236331 }
332+
333+ if ( mustQuery . length > 0 ) {
334+ body . query . bool = _ . merge ( body . query . bool , {
335+ must : mustQuery ,
336+ } ) ;
337+ }
237338 if ( fullTextQuery ) {
238339 body . query = _ . merge ( body . query , fullTextQuery ) ;
239340 if ( body . query . bool ) {
240341 body . query . bool . minimum_should_match = 1 ;
241342 }
242343 }
243344
244- if ( fullTextQuery || boolQuery . length > 0 ) {
345+ if ( fullTextQuery || boolQuery . length > 0 || mustQuery . length > 0 ) {
245346 searchCriteria . body = body ;
246347 }
247-
248348 return searchCriteria ;
249349} ;
250350
@@ -267,8 +367,7 @@ const retrieveProjects = (req, criteria, sort, ffields) => {
267367 fields . projects . push ( 'id' ) ;
268368 }
269369
270- const searchCriteria = parseElasticSearchCriteria ( criteria , fields , order ) ;
271-
370+ const searchCriteria = parseElasticSearchCriteria ( criteria , fields , order ) || { } ;
272371 return new Promise ( ( accept , reject ) => {
273372 const es = util . getElasticSearchClient ( ) ;
274373 es . search ( searchCriteria ) . then ( ( docs ) => {
@@ -300,7 +399,8 @@ module.exports = [
300399 'name' , 'name asc' , 'name desc' ,
301400 'type' , 'type asc' , 'type desc' ,
302401 ] ;
303- if ( ! util . isValidFilter ( filters , [ 'id' , 'status' , 'type' , 'memberOnly' , 'keyword' ] ) ||
402+ if ( ! util . isValidFilter ( filters ,
403+ [ 'id' , 'status' , 'memberOnly' , 'keyword' , 'name' , 'code' , 'customer' , 'manager' ] ) ||
304404 ( sort && _ . indexOf ( sortableProps , sort ) < 0 ) ) {
305405 return util . handleError ( 'Invalid filters or sort' , null , req , next ) ;
306406 }
0 commit comments