@@ -123,14 +123,16 @@ export class CubeEvaluator extends CubeSymbols {
123123 }
124124
125125 addIncludes ( cube , errorReporter ) {
126- if ( ! cube . includes ) {
126+ if ( ! cube . includes && ! cube . cubes ) {
127127 return ;
128128 }
129129 const types = [ 'measures' , 'dimensions' , 'segments' ] ;
130130 for ( const type of types ) {
131+ const cubeIncludes = cube . cubes && this . membersFromCubes ( cube . cubes , type , errorReporter ) || [ ] ;
131132 const includes = cube . includes && this . membersFromIncludeExclude ( cube . includes , cube . name , type ) || [ ] ;
132133 const excludes = cube . excludes && this . membersFromIncludeExclude ( cube . excludes , cube . name , type ) || [ ] ;
133- const finalIncludes = R . difference ( includes , excludes ) ;
134+ // cube includes will take precedence in case of member clash
135+ const finalIncludes = this . diffByMember ( this . diffByMember ( includes , cubeIncludes ) . concat ( cubeIncludes ) , excludes ) ;
134136 const includeMembers = this . generateIncludeMembers ( finalIncludes , cube . name , type ) ;
135137 for ( const [ memberName , memberDefinition ] of includeMembers ) {
136138 if ( cube [ type ] ?. [ memberName ] ) {
@@ -142,16 +144,71 @@ export class CubeEvaluator extends CubeSymbols {
142144 }
143145 }
144146
147+ membersFromCubes ( cubes , type , errorReporter ) {
148+ return R . unnest ( cubes . map ( cubeInclude => {
149+ const fullPath = this . evaluateReferences ( null , cubeInclude . cube , { collectJoinHints : true } ) ;
150+ const split = fullPath . split ( '.' ) ;
151+ const cubeReference = split [ split . length - 1 ] ;
152+ const cubeName = cubeInclude . name || cubeReference ;
153+ let includes ;
154+ if ( cubeInclude . includes === '*' ) {
155+ const membersObj = this . symbols [ cubeReference ] ?. cubeObj ( ) ?. [ type ] || { } ;
156+ includes = Object . keys ( membersObj ) . map ( memberName => ( { member : `${ fullPath } .${ memberName } ` } ) ) ;
157+ } else {
158+ includes = cubeInclude . includes . map ( include => {
159+ const member = include . member || include ;
160+ if ( member . indexOf ( '.' ) !== - 1 ) {
161+ errorReporter . error ( `Paths aren't allowed in cube includes but '${ member } ' provided as include member` ) ;
162+ }
163+ let name = include . name || member ;
164+ name = cubeInclude . prefix ? `${ cubeName } _${ name } ` : name ;
165+ if ( include . member ) {
166+ const resolvedMember = this . symbols [ cubeReference ] ?. cubeObj ( ) ?. [ type ] ?. [ include . member ] ;
167+ return resolvedMember ? {
168+ member : `${ fullPath } .${ include . member } ` ,
169+ name,
170+ } : undefined ;
171+ } else {
172+ const resolvedMember = this . symbols [ cubeReference ] ?. cubeObj ( ) ?. [ type ] ?. [ include ] ;
173+ return resolvedMember ? {
174+ member : `${ fullPath } .${ include } ` ,
175+ name
176+ } : undefined ;
177+ }
178+ } ) ;
179+ }
180+
181+ const excludes = ( cubeInclude . excludes || [ ] ) . map ( exclude => {
182+ if ( exclude . indexOf ( '.' ) !== - 1 ) {
183+ errorReporter . error ( `Paths aren't allowed in cube excludes but '${ exclude } ' provided as exclude member` ) ;
184+ }
185+ const resolvedMember = this . symbols [ cubeReference ] ?. cubeObj ( ) ?. [ type ] ?. [ exclude ] ;
186+ return resolvedMember ? {
187+ member : `${ cubeReference } .${ exclude } `
188+ } : undefined ;
189+ } ) ;
190+ return this . diffByMember ( includes . filter ( Boolean ) , excludes . filter ( Boolean ) ) ;
191+ } ) ) ;
192+ }
193+
194+ diffByMember ( includes , excludes ) {
195+ const excludesMap = new Map ( ) ;
196+ for ( const exclude of excludes ) {
197+ excludesMap . set ( exclude . member , true ) ;
198+ }
199+ return includes . filter ( include => ! excludesMap . get ( include . member ) ) ;
200+ }
201+
145202 membersFromIncludeExclude ( referencesFn , cubeName , type ) {
146203 const references = this . evaluateReferences ( cubeName , referencesFn ) ;
147204 return R . unnest ( references . map ( ref => {
148205 const path = ref . split ( '.' ) ;
149206 if ( path . length === 1 ) {
150207 const membersObj = this . symbols [ path [ 0 ] ] ?. cubeObj ( ) ?. [ type ] || { } ;
151- return Object . keys ( membersObj ) . map ( memberName => `${ ref } .${ memberName } ` ) ;
208+ return Object . keys ( membersObj ) . map ( memberName => ( { member : `${ ref } .${ memberName } ` } ) ) ;
152209 } else if ( path . length === 2 ) {
153210 const resolvedMember = this . symbols [ path [ 0 ] ] ?. cubeObj ( ) ?. [ type ] ?. [ path [ 1 ] ] ;
154- return resolvedMember ? [ ref ] : undefined ;
211+ return resolvedMember ? [ { member : ref } ] : undefined ;
155212 } else {
156213 throw new Error ( `Unexpected path length ${ path . length } for ${ ref } ` ) ;
157214 }
@@ -160,33 +217,40 @@ export class CubeEvaluator extends CubeSymbols {
160217
161218 generateIncludeMembers ( members , cubeName , type ) {
162219 return members . map ( memberRef => {
163- const path = memberRef . split ( '.' ) ;
164- const resolvedMember = this . symbols [ path [ 0 ] ] ?. cubeObj ( ) ?. [ type ] ?. [ path [ 1 ] ] ;
220+ const path = memberRef . member . split ( '.' ) ;
221+ const resolvedMember = this . symbols [ path [ path . length - 2 ] ] ?. cubeObj ( ) ?. [ type ] ?. [ path [ path . length - 1 ] ] ;
165222 if ( ! resolvedMember ) {
166- throw new Error ( `Can't resolve '${ memberRef } ' while generating include members` ) ;
223+ throw new Error ( `Can't resolve '${ memberRef . member } ' while generating include members` ) ;
167224 }
168225
169226 // eslint-disable-next-line no-new-func
170- const sql = new Function ( path [ 0 ] , `return \`\${${ path [ 0 ] } . ${ path [ 1 ] } }\`;` ) ;
227+ const sql = new Function ( path [ 0 ] , `return \`\${${ memberRef . member } }\`;` ) ;
171228 let memberDefinition ;
172229 if ( type === 'measures' ) {
173230 memberDefinition = {
174231 sql,
175- type : 'number'
232+ type : 'number' ,
233+ aggType : resolvedMember . type ,
234+ meta : resolvedMember . meta ,
235+ description : resolvedMember . description ,
176236 } ;
177237 } else if ( type === 'dimensions' ) {
178238 memberDefinition = {
179239 sql,
180- type : resolvedMember . type
240+ type : resolvedMember . type ,
241+ meta : resolvedMember . meta ,
242+ description : resolvedMember . description ,
181243 } ;
182244 } else if ( type === 'segments' ) {
183245 memberDefinition = {
184- sql
246+ sql,
247+ meta : resolvedMember . meta ,
248+ description : resolvedMember . description ,
185249 } ;
186250 } else {
187251 throw new Error ( `Unexpected member type: ${ type } ` ) ;
188252 }
189- return [ path [ 1 ] , memberDefinition ] ;
253+ return [ memberRef . name || path [ path . length - 1 ] , memberDefinition ] ;
190254 } ) ;
191255 }
192256
@@ -408,6 +472,14 @@ export class CubeEvaluator extends CubeSymbols {
408472 evaluateReferences ( cube , referencesFn , options = { } ) {
409473 const cubeEvaluator = this ;
410474
475+ const fullPath = ( joinHints , path ) => {
476+ if ( joinHints ?. length > 0 ) {
477+ return R . uniq ( joinHints . concat ( path ) ) ;
478+ } else {
479+ return path ;
480+ }
481+ } ;
482+
411483 const arrayOrSingle = cubeEvaluator . resolveSymbolsCall ( referencesFn , ( name ) => {
412484 const referencedCube = cubeEvaluator . symbols [ name ] && name || cube ;
413485 const resolvedSymbol =
@@ -419,10 +491,13 @@ export class CubeEvaluator extends CubeSymbols {
419491 if ( resolvedSymbol . _objectWithResolvedProperties ) {
420492 return resolvedSymbol ;
421493 }
422- return cubeEvaluator . pathFromArray ( [ referencedCube , name ] ) ;
494+ return cubeEvaluator . pathFromArray ( fullPath ( cubeEvaluator . joinHints ( ) , [ referencedCube , name ] ) ) ;
423495 } , {
424496 // eslint-disable-next-line no-shadow
425- sqlResolveFn : ( symbol , cube , n ) => cubeEvaluator . pathFromArray ( [ cube , n ] )
497+ sqlResolveFn : ( symbol , cube , n ) => cubeEvaluator . pathFromArray ( fullPath ( cubeEvaluator . joinHints ( ) , [ cube , n ] ) ) ,
498+ // eslint-disable-next-line no-shadow
499+ cubeAliasFn : ( cube ) => cubeEvaluator . pathFromArray ( fullPath ( cubeEvaluator . joinHints ( ) , [ cube ] ) ) ,
500+ collectJoinHints : options . collectJoinHints ,
426501 } ) ;
427502 if ( ! Array . isArray ( arrayOrSingle ) ) {
428503 return arrayOrSingle . toString ( ) ;
0 commit comments