@@ -92,9 +92,24 @@ function buildBotAnalyticsSql(input: {
9292 dataset : string ;
9393 since : number ;
9494 limit : number ;
95+ includeDoubles ?: boolean ;
9596} ) {
9697 const dataset = analyticsDatasetIdentifier ( input . dataset ) ;
9798 const sinceSeconds = Math . floor ( input . since / 1000 ) ;
99+ const doubleSelect = input . includeDoubles
100+ ? `,
101+ double1 AS receivedAt,
102+ double2 AS asn,
103+ double3 AS latitude,
104+ double4 AS longitude,
105+ double5 AS botScore,
106+ double6 AS userAgentLength`
107+ : "" ;
108+ const doubleFilter = input . includeDoubles
109+ ? `
110+ AND double1 >= ${ input . since } `
111+ : "" ;
112+ const orderBy = input . includeDoubles ? "double1" : "timestamp" ;
98113 return `
99114 SELECT
100115 timestamp,
@@ -117,17 +132,10 @@ function buildBotAnalyticsSql(input: {
117132 blob17 AS verifiedBotCategory,
118133 blob18 AS rayId,
119134 blob19 AS traceId,
120- blob20 AS metadataJson,
121- double1 AS receivedAt,
122- double2 AS asn,
123- double3 AS latitude,
124- double4 AS longitude,
125- double5 AS botScore,
126- double6 AS userAgentLength
135+ blob20 AS metadataJson${ doubleSelect }
127136 FROM ${ dataset }
128- WHERE timestamp >= toDateTime(${ sinceSeconds } )
129- AND double1 >= ${ input . since }
130- ORDER BY double1 DESC
137+ WHERE timestamp >= toDateTime(${ sinceSeconds } )${ doubleFilter }
138+ ORDER BY ${ orderBy } DESC
131139 LIMIT ${ input . limit }
132140 FORMAT JSONEachRow
133141 ` ;
@@ -184,6 +192,16 @@ function parseJsonEachRow(text: string): Record<string, unknown>[] {
184192 . map ( ( line ) => JSON . parse ( line ) as Record < string , unknown > ) ;
185193}
186194
195+ function parseAnalyticsTimestampMs ( value : unknown ) : number {
196+ const text = String ( value || "" ) . trim ( ) ;
197+ if ( ! text ) return 0 ;
198+ const normalized = / ^ \d { 4 } - \d { 2 } - \d { 2 } \d { 2 } : \d { 2 } : \d { 2 } / . test ( text )
199+ ? `${ text . replace ( " " , "T" ) } Z`
200+ : text ;
201+ const parsed = Date . parse ( normalized ) ;
202+ return Number . isFinite ( parsed ) ? parsed : 0 ;
203+ }
204+
187205function normalizeBotRow (
188206 row : Record < string , unknown > ,
189207 sites : Map < string , { name : string ; domain : string } > ,
@@ -195,9 +213,11 @@ function normalizeBotRow(
195213 . map ( ( reason ) => reason . trim ( ) )
196214 . filter ( Boolean ) ;
197215 const botScore = toFiniteNumber ( row . botScore , 0 ) ;
216+ const receivedAt =
217+ toFiniteNumber ( row . receivedAt ) || parseAnalyticsTimestampMs ( row . timestamp ) ;
198218 return {
199219 timestamp : clampString ( String ( row . timestamp || "" ) , 64 ) ,
200- receivedAt : toFiniteNumber ( row . receivedAt ) ,
220+ receivedAt,
201221 siteId,
202222 siteName : clampString ( site ?. name || siteId || "Unknown site" , 160 ) ,
203223 siteDomain : clampString ( site ?. domain || "" , 255 ) ,
@@ -226,6 +246,17 @@ function normalizeBotRow(
226246 } ;
227247}
228248
249+ function shouldRetryBotAnalyticsWithoutDoubles ( result : {
250+ status : number ;
251+ body : string ;
252+ } ) : boolean {
253+ if ( result . status !== 422 ) return false ;
254+ const body = result . body . toLowerCase ( ) ;
255+ return (
256+ body . includes ( "unable to find type of column" ) && / d o u b l e \d + / . test ( body )
257+ ) ;
258+ }
259+
229260function bucketSizeMs ( minutes : number ) : number {
230261 if ( minutes <= 60 ) return 5 * 60 * 1000 ;
231262 if ( minutes <= 360 ) return 30 * 60 * 1000 ;
@@ -512,12 +543,25 @@ export async function handleBotAnalyticsAdmin(
512543 dataset : config . dataset ,
513544 since : from ,
514545 limit,
546+ includeDoubles : true ,
515547 } ) ;
516- const result = await queryCloudflareAnalyticsEngine ( {
548+ let result = await queryCloudflareAnalyticsEngine ( {
517549 accountId : config . accountId ,
518550 token,
519551 sql,
520552 } ) ;
553+ if ( ! result . ok && shouldRetryBotAnalyticsWithoutDoubles ( result ) ) {
554+ result = await queryCloudflareAnalyticsEngine ( {
555+ accountId : config . accountId ,
556+ token,
557+ sql : buildBotAnalyticsSql ( {
558+ dataset : config . dataset ,
559+ since : from ,
560+ limit,
561+ includeDoubles : false ,
562+ } ) ,
563+ } ) ;
564+ }
521565 if ( ! result . ok ) {
522566 return bad (
523567 cloudflareAnalyticsErrorMessage ( result ) ,
0 commit comments