Skip to content

Commit bce12fc

Browse files
committedSep 26, 2024
spline #1155 Move "findEvents()" to Foxx
1 parent 8be794e commit bce12fc

File tree

9 files changed

+77
-59
lines changed

9 files changed

+77
-59
lines changed
 

‎arangodb-foxx-api/pom.xml

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
<argument>za.co.absa.spline.consumer.service.model.Frame</argument>
6363
<argument>za.co.absa.spline.consumer.service.model.ExecutionPlanInfo</argument>
6464
<argument>za.co.absa.spline.consumer.service.model.ExecutionEventInfo</argument>
65+
<argument>za.co.absa.spline.consumer.service.model.Label</argument>
6566
</arguments>
6667
</configuration>
6768
<dependencies>

‎arangodb-foxx-services/src/main/routes/events-router.ts

+26-23
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { listExecutionEvents, storeExecutionEvent } from '../services/execution-
2424
import { TxManager } from '../services/txm'
2525
import { checkKeyExistence } from '../services/store'
2626
import { NodeCollectionName } from '../persistence/model'
27+
import * as Logger from '../utils/logger'
2728

2829

2930
export const eventsRouter: Foxx.Router = createRouter()
@@ -32,6 +33,10 @@ function toBoolean(str: string): boolean {
3233
return str == null ? null : str.toLowerCase() === 'true'
3334
}
3435

36+
function toNumber(str: string): number {
37+
return str == null ? null : +str
38+
}
39+
3540
// Store execution event
3641
eventsRouter
3742
.post('/',
@@ -48,37 +53,35 @@ eventsRouter
4853
eventsRouter
4954
.get('/',
5055
(req: Foxx.Request, res: Foxx.Response) => {
56+
Logger.debug(`Foxx: GET ${req.url}`)
5157
const events = listExecutionEvents(
52-
+req.queryParams.asAtTime,
53-
+req.queryParams.timestampStart,
54-
+req.queryParams.timestampEnd,
55-
+req.queryParams.pageOffset,
56-
+req.queryParams.pageSize,
57-
req.queryParams.sortField,
58-
req.queryParams.sortOrder,
58+
toNumber(req.queryParams.asAtTime),
59+
toNumber(req.queryParams.timestampStart),
60+
toNumber(req.queryParams.timestampEnd),
5961
req.queryParams.searchTerm || null,
6062
toBoolean(req.queryParams.writeAppends),
6163
req.queryParams.applicationId || null,
6264
req.queryParams.dataSourceUri || null,
63-
req.queryParams.lblNames || [],
64-
req.queryParams.lblValues || []
65+
JSON.parse(req.queryParams.labels || '[]'),
66+
req.queryParams.sortField,
67+
req.queryParams.sortOrder,
68+
toNumber(req.queryParams.offset),
69+
toNumber(req.queryParams.limit),
6570
)
6671
res.send(events)
6772
})
68-
//todo: add query params validation
69-
// .queryParam('asAtTime', Joi.number().required())
70-
// .queryParam('timestampStart', Joi.number().optional())
71-
// .queryParam('timestampEnd', Joi.number().optional())
72-
// .queryParam('pageOffset', Joi.number().required())
73-
// .queryParam('pageSize', Joi.number().required())
74-
// .queryParam('sortField', Joi.string().required())
75-
// .queryParam('sortOrder', Joi.string().required())
76-
// .queryParam('searchTerm', Joi.string().optional())
77-
// .queryParam('writeAppends', Joi.boolean().optional())
78-
// .queryParam('applicationId', Joi.string().optional())
79-
// .queryParam('dataSourceUri', Joi.string().optional())
80-
// .queryParam('lblNames', Joi.array().items(Joi.string()).required())
81-
// .queryParam('lblValues', Joi.array().items(Joi.string()).required())
73+
.queryParam('asAtTime', Joi.number().required())
74+
.queryParam('timestampStart', Joi.number().optional())
75+
.queryParam('timestampEnd', Joi.number().optional())
76+
.queryParam('searchTerm', Joi.string().optional())
77+
.queryParam('writeAppends', Joi.boolean().optional())
78+
.queryParam('applicationId', Joi.string().optional())
79+
.queryParam('dataSourceUri', Joi.string().optional())
80+
.queryParam('labels', Joi.string().optional()) // serialized JSON array
81+
.queryParam('sortField', Joi.string().required())
82+
.queryParam('sortOrder', Joi.string().required())
83+
.queryParam('offset', Joi.number().required())
84+
.queryParam('limit', Joi.number().required())
8285
.response(200, ['application/json'])
8386
.summary('List execution events')
8487

‎arangodb-foxx-services/src/main/services/data-source-store.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ import * as persistence from '../model'
2424
export function storeDataSources(ds: api.DataSource): persistence.DataSource {
2525
return withTimeTracking(`STORE DATA SOURCE ${ds}`, () => {
2626
return db._query(aql`
27-
WITH ${NodeCollectionName.DataSource}
28-
UPSERT { uri: ${ds.uri} }
27+
WITH ${aql.literal(NodeCollectionName.DataSource)}
28+
UPSERT { uri: ${ds}.uri }
2929
INSERT KEEP(${ds}, ['_created', 'uri', 'name'])
3030
UPDATE {} IN ${aql.literal(NodeCollectionName.DataSource)}
3131
RETURN KEEP(NEW, ['_key', 'uri'])

‎arangodb-foxx-services/src/main/services/execution-event-store.ts

+30-18
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@
1515
*/
1616

1717

18-
import { ExecPlanDetails, ExecutionEventInfo, Frame, Progress } from '../../external/api.model'
19-
import { CollectionName, edge, WriteTxInfo } from '../persistence/model'
18+
import { ExecPlanDetails, ExecutionEventInfo, Frame, Label, Progress } from '../../external/api.model'
19+
import { CollectionName, edge, ViewName, WriteTxInfo } from '../persistence/model'
2020
import { store } from './store'
2121
import { aql, db } from '@arangodb'
2222
import { withTimeTracking } from '../utils/common'
2323
import { TxManager } from './txm'
2424
import { TxTemplate } from './txm/tx-template'
25+
import * as Logger from '../utils/logger'
2526

2627

2728
const SEARCH_FIELDS = [
@@ -32,35 +33,46 @@ const SEARCH_FIELDS = [
3233
'execPlanDetails.dataSourceType',
3334
]
3435

35-
function escapeJavaScript(str: string): string {
36-
return str.replace(/[\-\[\]\/{}()*+?.\\^$|'"\n]/g, '\\$&')
37-
}
38-
3936
export function listExecutionEvents(
4037
asAtTime: number,
4138
timestampStart: number | null,
4239
timestampEnd: number | null,
43-
pageOffset: number,
44-
pageSize: number,
45-
sortField: string,
46-
sortOrder: string,
4740
searchTerm: string | null,
4841
writeAppends: boolean | null,
4942
applicationId: string | null,
5043
dataSourceUri: string | null,
51-
lblNames: string[],
52-
lblValues: string[]
44+
labels: Label[],
45+
sortField: string,
46+
sortOrder: string,
47+
offset: number,
48+
limit: number,
5349
): Frame<Partial<ExecutionEventInfo>> {
50+
const lblNames = labels.map(lbl => lbl.name)
51+
const lblValues = labels.map(lbl => lbl.values)
5452

55-
const q = aql`
56-
WITH progress_view
57-
FOR ee IN progress_view
53+
const q: ArangoDB.Query = aql`
54+
WITH ${aql.literal(ViewName.ProgressSearchView)}
55+
FOR ee IN ${aql.literal(ViewName.ProgressSearchView)}
5856
SEARCH ee._created <= ${asAtTime}
5957
AND (${timestampStart} == null OR IN_RANGE(ee.timestamp, ${timestampStart}, ${timestampEnd}, true, true))
6058
AND (${applicationId} == null OR ${applicationId} == ee.extra.appId)
6159
AND (${dataSourceUri} == null OR ${dataSourceUri} == ee.execPlanDetails.dataSourceUri)
6260
AND (${writeAppends} == null OR ee.execPlanDetails.append IN ${writeAppends})
6361
62+
${aql.join(lblNames.map((lblName, i) => aql`
63+
AND (
64+
${lblValues[i]} ANY == ee.labels[${lblName}]
65+
OR ${lblValues[i]} ANY == ee.execPlanDetails.labels[${lblName}]
66+
)
67+
`))}
68+
69+
AND (
70+
${searchTerm} == null
71+
${aql.join(SEARCH_FIELDS.map(fld => aql`
72+
OR ANALYZER(LIKE(ee.${aql.literal(fld)}, CONCAT("%", TOKENS(${searchTerm}, "norm_en")[0], "%")), "norm_en")
73+
`))}
74+
)
75+
6476
LET resItem = {
6577
"executionEventId" : ee._key,
6678
"executionPlanId" : ee.execPlanDetails.executionPlanKey,
@@ -79,12 +91,12 @@ export function listExecutionEvents(
7991
}
8092
8193
SORT resItem.${sortField} ${sortOrder}
82-
LIMIT ${pageOffset * pageSize}, ${pageSize}
94+
LIMIT ${offset}, ${limit}
8395
8496
RETURN resItem
8597
`
8698

87-
console.log('AQL query: ', q)
99+
Logger.debug('AQL query: ', q)
88100

89101
const cursor = db._query(q, { fullCount: true })
90102
const items: ExecutionEventInfo[] = cursor.toArray()
@@ -93,7 +105,7 @@ export function listExecutionEvents(
93105
return {
94106
items,
95107
totalCount,
96-
offset: pageOffset * pageSize,
108+
offset,
97109
}
98110
}
99111

‎consumer-services/src/main/scala/za/co/absa/spline/consumer/service/model/PageRequest.scala

+6-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,9 @@
1515
*/
1616
package za.co.absa.spline.consumer.service.model
1717

18-
case class PageRequest(page: Int, size: Int)
18+
case class PageRequest(page: Int, size: Int) {
19+
20+
def offset: Int = (page - 1) * size
21+
22+
def limit: Int = size
23+
}

‎consumer-services/src/main/scala/za/co/absa/spline/consumer/service/model/SortRequest.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515
*/
1616
package za.co.absa.spline.consumer.service.model
1717

18-
case class SortRequest(sortField: String, sortOrder: String)
18+
case class SortRequest(field: String, order: String)

‎consumer-services/src/main/scala/za/co/absa/spline/consumer/service/repo/DataSourceRepositoryImpl.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ class DataSourceRepositoryImpl @Autowired()(db: ArangoDatabaseAsync) extends Dat
112112
"timestampEnd" -> maybeWriteTimestampEnd.map(Long.box).orNull,
113113
"pageOffset" -> Int.box(pageRequest.page - 1),
114114
"pageSize" -> Int.box(pageRequest.size),
115-
"sortField" -> sortRequest.sortField,
116-
"sortOrder" -> sortRequest.sortOrder,
115+
"sortField" -> sortRequest.field,
116+
"sortOrder" -> sortRequest.order,
117117
"searchTerm" -> maybeSearchTerm.map(escapeAQLSearch).orNull,
118118
"writeAppends" -> (if (writeAppendOptions.isEmpty) null else writeAppendOptions.flatten.map(Boolean.box)),
119119
"includeNoWrite" -> Boolean.box(writeAppendOptions.contains(None)),

‎consumer-services/src/main/scala/za/co/absa/spline/consumer/service/repo/ExecutionEventRepositoryImpl.scala

+7-10
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import org.springframework.stereotype.Repository
2121
import za.co.absa.spline.common.StringEscapeUtils.escapeAQLSearch
2222
import za.co.absa.spline.consumer.service.model._
2323
import za.co.absa.spline.consumer.service.repo.ExecutionEventRepositoryImpl._
24+
import za.co.absa.spline.persistence.DefaultJsonSerDe._
2425
import za.co.absa.spline.persistence.FoxxRouter
2526

2627
import scala.concurrent.{ExecutionContext, Future}
@@ -42,23 +43,19 @@ class ExecutionEventRepositoryImpl @Autowired()(
4243
maybeApplicationId: Option[String],
4344
maybeDataSourceUri: Option[String]
4445
)(implicit ec: ExecutionContext): Future[Frame[ExecutionEventInfo]] = {
45-
val lblNames = labels.map(_.name)
46-
val lblValues = labels.map(_.values)
47-
4846
foxxRouter.get[Frame[ExecutionEventInfo]]("/spline/execution-events", Map(
4947
"asAtTime" -> Long.box(asAtTime),
5048
"timestampStart" -> maybeTimestampStart.map(Long.box).orNull,
5149
"timestampEnd" -> maybeTimestampEnd.map(Long.box).orNull,
52-
"pageOffset" -> Int.box(pageRequest.page - 1),
53-
"pageSize" -> Int.box(pageRequest.size),
54-
"sortField" -> sortRequest.sortField,
55-
"sortOrder" -> sortRequest.sortOrder,
5650
"searchTerm" -> maybeSearchTerm.map(escapeAQLSearch).orNull,
57-
"writeAppends" -> (if (writeAppendOptions.isEmpty) null else writeAppendOptions.flatten.map(Boolean.box)),
5851
"applicationId" -> maybeApplicationId.orNull,
5952
"dataSourceUri" -> maybeDataSourceUri.orNull,
60-
"lblNames" -> lblNames.toSeq,
61-
"lblValues" -> lblValues.toSeq,
53+
"writeAppends" -> (if (writeAppendOptions.isEmpty) null else writeAppendOptions.flatten.map(Boolean.box)),
54+
"labels" -> labels.toJson,
55+
"sortField" -> sortRequest.field,
56+
"sortOrder" -> sortRequest.order,
57+
"offset" -> pageRequest.offset,
58+
"limit" -> pageRequest.limit,
6259
))
6360
}
6461
}

‎consumer-services/src/main/scala/za/co/absa/spline/consumer/service/repo/ExecutionPlanRepositoryImpl.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,8 @@ class ExecutionPlanRepositoryImpl @Autowired()(db: ArangoDatabaseAsync) extends
190190
"asAtTime" -> Long.box(asAtTime),
191191
"pageOffset" -> Int.box(pageRequest.page - 1),
192192
"pageSize" -> Int.box(pageRequest.size),
193-
"sortField" -> sortRequest.sortField,
194-
"sortOrder" -> sortRequest.sortOrder
193+
"sortField" -> sortRequest.field,
194+
"sortOrder" -> sortRequest.order
195195
),
196196
new AqlQueryOptions().fullCount(true)
197197
)

0 commit comments

Comments
 (0)
Please sign in to comment.