import { Service , Context } from 'egg' ; const moment = require ( 'moment' ) ; class RetCodeService extends Service { constructor ( ctx ) { super ( ctx ) ; } public async search ( payload ) { const { startTime , endTime , query , currentPage , pageSize , order } = payload ; const queryParams = [ ] ; const keys = Object . keys ( query ) ; keys . map ( item => { queryParams . push ( { match : { [ item ] : query [ item ] } } ) ; } ) ; const body = { query : { bool : { must : queryParams , filter : { range : { '@timestamp' : { gte : moment ( startTime ) , lte : moment ( endTime ) , } , } , } , } , } , size : pageSize , from : ( currentPage - 1 ) * pageSize , sort : { '@timestamp' : { order , } , } , } ; const res = await this . esSearch ( body ) ; const source = [ ] ; res . hits . hits . map ( item => { source . push ( item . _source ) ; } ) ; return { data : source , total : res . hits . hits . length } ; } param payload public async dimension ( payload ) { const { filters , dimensions , startTime , endTime , order , measures } = payload ; const filterParams = this . filterParams ( filters ) ; const aggsQuery = this . aggsDimensionQuery ( dimensions , order , measures ) ; const body = { size : 0 , query : { bool : { must : filterParams , filter : { range : { '@timestamp' : { gte : moment ( startTime ) , lte : moment ( endTime ) , } , } , } , } , } , aggs : aggsQuery , } ; const res = await this . esSearch ( body ) ; return this . dimensionRes ( res , dimensions , measures ) ; } param payload async indicator ( payload ) { const { filters , startTime , endTime , intervalMillis , measures } = payload ; const filterParams = this . filterParams ( filters ) ; const aggsQuery = this . aggsIndicatorQuery ( measures , intervalMillis ) ; const body = { size : 0 , query : { bool : { must : filterParams , filter : { range : { '@timestamp' : { gte : moment ( startTime ) , lte : moment ( endTime ) , } , } , } , } , } , aggs : aggsQuery , } ; const res = await this . esSearch ( body ) ; return this . indicatorRes ( { res , payload } ) ; } param res param dimensions param measures returns public dimensionRes ( res , dimensions , measures ) { let total = 0 ; const data = [ ] ; const name = dimensions [ 0 ] ; if ( ! res . aggregations || res . aggregations [ name ] . buckets . length === 0 ) return { data : [ ] , total : 0 } ; res . aggregations [ name ] . buckets . map ( b => { const temp = { } ; temp [ name ] = b . key ; measures . map ( item => { if ( b [ item ] . buckets ) { if ( item === 'count' ) temp [ item ] = b . doc_count ; else temp [ item ] = b [ item ] . buckets ; } else { temp [ item ] = b [ item ] . value || 0 ; if ( item === 'count' ) temp [ item ] = b . doc_count ; } } ) ; data . push ( temp ) ; total += b . doc_count ; } ) ; return { data , total , } ; } param res param measures returns public indicatorRes ( params ) { const { payload : { measures , startTime , endTime , intervalMillis } , res } = params ; let total = 0 ; const data = [ ] ; if ( ! res . aggregations || res . aggregations . indicator . buckets . length === 0 ) return { data : [ ] , total : 0 } ; res . aggregations . indicator . buckets . map ( b => { const temp = { } ; temp . date = b . key ; temp . format = b . key_as_string ; measures . map ( item => { if ( b [ item ] . buckets ) { if ( item === 'count' ) temp [ item ] = b . doc_count ; else temp [ item ] = b [ item ] . buckets ; } else { temp [ item ] = b [ item ] . value || 0 ; } } ) ; data . push ( temp ) ; total += b . doc_count ; } ) ; const leftArray = [ ] ; const rightArray = [ ] ; if ( data [ 0 ] . date > startTime ) { const diff = ( data [ 0 ] . date - startTime ) / intervalMillis ; const fillData = { } ; measures . map ( item => { fillData [ item ] = 0 ; } ) ; Array ( Number . parseInt ( diff ) ) . fill ( 1 ) . map ( ( _item , index ) => { const currentDate = startTime + index * intervalMillis ; const formatDate = moment ( currentDate ) . format ( 'YYYY-MM-DD hh:mm:ss' ) ; leftArray . push ( { ... fillData , date : currentDate , format : formatDate } ) ; } ) ; } if ( data [ data . length - 1 ] . date < endTime ) { const diff = ( endTime - data [ data . length - 1 ] . date ) / intervalMillis ; const fillData = { } ; measures . map ( item => { fillData [ item ] = 0 ; } ) ; Array ( Number . parseInt ( diff ) ) . fill ( 1 ) . map ( ( _item , index ) => { const currentDate = data [ data . length - 1 ] . date + ( index + 1 ) * intervalMillis ; const formatDate = moment ( currentDate ) . format ( 'YYYY-MM-DD hh:mm:ss' ) ; rightArray . push ( { ... fillData , date : currentDate , format : formatDate } ) ; } ) ; } const allData = [ ... leftArray , ... data , ... rightArray ] ; return { data : allData , total , } ; } param body returns public async esSearch ( body ) { return await this . app . elasticsearch . search ( { index : 'frontend-event-log-web-report-collect-*' , type : '_doc' , body , } ) ; } param filters returns public filterParams ( filters ) { let filterQuery = [ ] ; Object . keys ( filters ) . map ( item => { const match = { } ; match [ `${ item }` ] = filters [ `${ item }` ] ; filterQuery = [ ... filterQuery , { match } ] ; } ) ; return filterQuery ; } param dimensions param order returns public aggsDimensionQuery ( dimensions , order , measures ) { const aggsQuery = { } ; const name = dimensions [ 0 ] ; const aggs = { } ; measures . map ( item => { if ( item === 'pv' || item === 'uv' ) { if ( item === 'pv' ) { aggs . pv = { sum : { field : 'pv' , } } ; } else { aggs . uv = { cardinality : { field : 'ip.keyword' , } } ; } } else { if ( item . includes ( '_' ) ) { const values = item . split ( '_' ) ; aggs [ item ] = { [ values [ 0 ] ] : { field : `${ values [ 1 ] }` , } , } ; } else { aggs [ item ] = { terms : { field : `${ item }.keyword` , } , } ; } } } ) ; aggsQuery [ name ] = { terms : { field : `${ name }.keyword` , order : { _count : order . toLowerCase ( ) , } , } , aggs , } ; return aggsQuery ; } param dimensions param order returns public aggsIndicatorQuery ( measures , intervalMillis ) { const aggs = { } ; measures . map ( item => { if ( item === 'pv' || item === 'uv' ) { if ( item === 'pv' ) { aggs . pv = { sum : { field : 'pv' , } } ; } else { aggs . uv = { cardinality : { field : 'ip.keyword' , } } ; } } else { if ( item . includes ( '_' ) ) { const values = item . split ( '_' ) ; aggs [ item ] = { [ values [ 0 ] ] : { field : `${ values [ 1 ] }` , } , } ; } else { aggs [ item ] = { terms : { field : `${ item }.keyword` , } , } ; } } } ) ; const aggsQuery = { indicator : { date_histogram : { field : '@timestamp' , interval : intervalMillis , format : 'yyyy-MM-dd hh:mm:ss' , min_doc_count : 0 , } , aggs , } , } ; return aggsQuery ; } } module . exports = RetCodeService ;