* List member profiles with filtering and search
(options: ListMemberProfilesOptions = {})
| 234 | * List member profiles with filtering and search |
| 235 | */ |
| 236 | async listProfiles(options: ListMemberProfilesOptions = {}): Promise<MemberProfile[]> { |
| 237 | const conditions: string[] = []; |
| 238 | const params: unknown[] = []; |
| 239 | let paramIndex = 1; |
| 240 | |
| 241 | // Filter by public visibility |
| 242 | if (options.is_public !== undefined) { |
| 243 | conditions.push(`is_public = $${paramIndex++}`); |
| 244 | params.push(options.is_public); |
| 245 | } |
| 246 | |
| 247 | // Filter by carousel visibility |
| 248 | if (options.show_in_carousel !== undefined) { |
| 249 | conditions.push(`show_in_carousel = $${paramIndex++}`); |
| 250 | params.push(options.show_in_carousel); |
| 251 | } |
| 252 | |
| 253 | // Filter by featured |
| 254 | if (options.featured !== undefined) { |
| 255 | conditions.push(`featured = $${paramIndex++}`); |
| 256 | params.push(options.featured); |
| 257 | } |
| 258 | |
| 259 | // Filter by offerings (array overlap) |
| 260 | if (options.offerings && options.offerings.length > 0) { |
| 261 | conditions.push(`offerings && $${paramIndex++}::text[]`); |
| 262 | params.push(options.offerings); |
| 263 | } |
| 264 | |
| 265 | // Filter by markets (array overlap) |
| 266 | if (options.markets && options.markets.length > 0) { |
| 267 | conditions.push(`markets && $${paramIndex++}::text[]`); |
| 268 | params.push(options.markets); |
| 269 | } |
| 270 | |
| 271 | // Full-text search - tokenize query and match ANY word (OR logic) |
| 272 | // This allows "DSP demand-side platform" to match on "DSP" alone |
| 273 | if (options.search) { |
| 274 | // Split search into words, filter short/common words |
| 275 | const searchWords = options.search |
| 276 | .split(/\s+/) |
| 277 | .map(w => w.trim()) |
| 278 | .filter(w => w.length >= 2); // Keep words with 2+ chars |
| 279 | |
| 280 | if (searchWords.length === 0) { |
| 281 | // Fall back to original search if no valid words |
| 282 | conditions.push(`( |
| 283 | display_name ILIKE $${paramIndex} OR |
| 284 | tagline ILIKE $${paramIndex} OR |
| 285 | description ILIKE $${paramIndex} OR |
| 286 | headquarters ILIKE $${paramIndex} OR |
| 287 | tags::text ILIKE $${paramIndex} OR |
| 288 | offerings::text ILIKE $${paramIndex} |
| 289 | )`); |
| 290 | params.push(`%${escapeLikePattern(options.search)}%`); |
| 291 | paramIndex++; |
| 292 | } else { |
| 293 | // Build OR conditions for each word |
no test coverage detected