@@ -2,6 +2,8 @@ import { NextRequest, NextResponse } from "next/server"
22import { executeQuery as executeClickHouseQuery } from "@/lib/clickhouse"
33import { validateRequest } from "@/lib/auth"
44import { throwIfAborted , getRequestSignal , handleAbortError } from "@/lib/api-helpers"
5+ import { parseSearchQuery } from "@/lib/query-parser"
6+ import { buildDomainReconCondition , buildKeywordReconCondition } from "@/lib/search-query-builder"
57
68export async function POST ( request : NextRequest ) {
79 // ✅ Check abort VERY EARLY - before validateRequest
@@ -24,11 +26,13 @@ export async function POST(request: NextRequest) {
2426 }
2527
2628 // Normalize Domain
27- let cleanDomain = targetDomain . trim ( ) . toLowerCase ( )
28- cleanDomain = cleanDomain . replace ( / ^ h t t p s ? : \/ \/ / , '' ) . replace ( / ^ w w w \. / , '' ) . split ( '/' ) [ 0 ] . split ( ':' ) [ 0 ]
29+ const cleanDomain = targetDomain . trim ( ) . toLowerCase ( )
30+
31+ // Parse query for operator support (OR, NOT, wildcard, exact)
32+ const parsed = parseSearchQuery ( cleanDomain )
2933
3034 // Cleaner log: only show search if present
31- const logData : any = { type : searchType , domain : cleanDomain }
35+ const logData : any = { type : searchType , domain : cleanDomain , terms : parsed . terms . length }
3236 if ( searchQuery && searchQuery . trim ( ) ) {
3337 logData . search = searchQuery . trim ( )
3438 }
@@ -41,7 +45,7 @@ export async function POST(request: NextRequest) {
4145 throwIfAborted ( request )
4246
4347 // Call the new data getter function
44- const credentialsData = await getCredentialsDataOptimized ( cleanDomain , filters , pagination , searchQuery , searchType , body . keywordMode , signal )
48+ const credentialsData = await getCredentialsDataOptimized ( parsed , filters , pagination , searchQuery , searchType , body . keywordMode , signal )
4549
4650 // Check abort after operations
4751 throwIfAborted ( request )
@@ -71,7 +75,7 @@ export async function POST(request: NextRequest) {
7175}
7276
7377async function getCredentialsDataOptimized (
74- query : string ,
78+ parsed : import ( "@/lib/query-parser" ) . ParsedQuery ,
7579 filters ?: any ,
7680 pagination ?: any ,
7781 searchQuery ?: string ,
@@ -99,44 +103,19 @@ async function getCredentialsDataOptimized(
99103 // ==========================================
100104 // 1. BUILD PREWHERE (Main Table Filters)
101105 // ==========================================
102- // PREWHERE is the key to speed in ClickHouse.
103- // It filters before JOIN and before reading heavy columns.
106+ // Use the shared query builder for the main domain/keyword condition
104107
105108 const prewhereConditions : string [ ] = [ ]
106109 const params : Record < string , any > = { }
107110
108111 if ( searchType === 'domain' ) {
109- // DOMAIN OPTIMIZATION:
110- // 1. Check Exact Match domain
111- // 2. Check Subdomain using endsWith (much faster than ilike/regex)
112- // 3. Fallback to URL pattern match only if needed
113-
114- params [ 'targetDomain' ] = query
115- params [ 'dotTargetDomain' ] = '.' + query
116-
117- // Logic: Domain column exact match OR Domain column ends with .target.com
118- // This leverages suffix index if available, or at least fast string scan
119- prewhereConditions . push ( `(
120- c.domain = {targetDomain:String} OR
121- endsWith(c.domain, {dotTargetDomain:String}) OR
122- c.url ilike {urlPattern:String}
123- )` )
124- // Fallback URL pattern for catch-all
125- params [ 'urlPattern' ] = `%${ query } %`
126-
112+ const built = buildDomainReconCondition ( parsed , { notNullCheck : false } )
113+ prewhereConditions . push ( `(${ built . condition } )` )
114+ Object . assign ( params , built . params )
127115 } else {
128- // KEYWORD SEARCH
129- params [ 'keyword' ] = query
130- params [ 'likeKeyword' ] = `%${ query } %`
131-
132- if ( keywordMode === 'domain-only' ) {
133- prewhereConditions . push ( `(c.domain ilike {likeKeyword:String})` )
134- } else {
135- // Optimization: multiSearchAnyCase is faster than OR OR OR
136- // But for simplicity and param binding, we use ilike in PREWHERE
137- // because PREWHERE already significantly reduces cost.
138- prewhereConditions . push ( `(c.url ilike {likeKeyword:String} OR c.domain ilike {likeKeyword:String})` )
139- }
116+ const built = buildKeywordReconCondition ( parsed , keywordMode )
117+ prewhereConditions . push ( `(${ built . condition } )` )
118+ Object . assign ( params , built . params )
140119 }
141120
142121 // Additional Filters to PREWHERE (To filter faster at the start)
0 commit comments