@malloydata/malloy 0.0.119-dev240123183113 → 0.0.119-dev240124170348

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.js +1 -3
  3. package/dist/lang/ast/error-factory.js +3 -3
  4. package/dist/lang/ast/expressions/expr-aggregate-function.d.ts +6 -4
  5. package/dist/lang/ast/expressions/expr-aggregate-function.js +24 -13
  6. package/dist/lang/ast/expressions/expr-count.js +1 -1
  7. package/dist/lang/ast/expressions/expr-func.js +4 -4
  8. package/dist/lang/ast/expressions/expr-id-reference.js +2 -2
  9. package/dist/lang/ast/field-space/dynamic-space.js +1 -1
  10. package/dist/lang/ast/field-space/index-field-space.d.ts +5 -3
  11. package/dist/lang/ast/field-space/index-field-space.js +85 -16
  12. package/dist/lang/ast/field-space/query-input-space.d.ts +3 -3
  13. package/dist/lang/ast/field-space/query-spaces.d.ts +15 -12
  14. package/dist/lang/ast/field-space/query-spaces.js +60 -57
  15. package/dist/lang/ast/field-space/reference-field.d.ts +0 -5
  16. package/dist/lang/ast/field-space/reference-field.js +16 -46
  17. package/dist/lang/ast/query-builders/index-builder.d.ts +2 -2
  18. package/dist/lang/ast/query-builders/partial-builder.js +28 -1
  19. package/dist/lang/ast/query-builders/project-builder.d.ts +8 -4
  20. package/dist/lang/ast/query-builders/project-builder.js +5 -6
  21. package/dist/lang/ast/query-builders/reduce-builder.d.ts +13 -8
  22. package/dist/lang/ast/query-builders/reduce-builder.js +12 -9
  23. package/dist/lang/ast/query-items/field-declaration.d.ts +1 -1
  24. package/dist/lang/ast/query-items/field-references.d.ts +5 -1
  25. package/dist/lang/ast/query-items/field-references.js +17 -1
  26. package/dist/lang/ast/query-properties/qop-desc.d.ts +2 -2
  27. package/dist/lang/ast/source-properties/joins.js +1 -1
  28. package/dist/lang/ast/types/field-space.d.ts +2 -2
  29. package/dist/lang/ast/types/query-builder.d.ts +2 -2
  30. package/dist/lang/ast/view-elements/qop-desc-view.d.ts +3 -3
  31. package/dist/lang/ast/view-elements/reference-view.d.ts +3 -3
  32. package/dist/lang/ast/view-elements/reference-view.js +1 -1
  33. package/dist/lang/ast/view-elements/refine-utils.js +20 -18
  34. package/dist/lang/ast/view-elements/view-arrow.d.ts +2 -2
  35. package/dist/lang/ast/view-elements/view-refine.d.ts +3 -3
  36. package/dist/lang/ast/view-elements/view.d.ts +4 -4
  37. package/dist/lang/field-utils.d.ts +3 -3
  38. package/dist/lang/field-utils.js +9 -8
  39. package/dist/lang/test/annotation.spec.js +26 -27
  40. package/dist/lang/test/expressions.spec.js +43 -40
  41. package/dist/lang/test/field-symbols.spec.js +4 -4
  42. package/dist/lang/test/locations.spec.js +13 -6
  43. package/dist/lang/test/query.spec.js +44 -56
  44. package/dist/lang/test/test-translator.d.ts +3 -2
  45. package/dist/lang/test/test-translator.js +30 -24
  46. package/dist/malloy.js +1 -1
  47. package/dist/model/malloy_query.d.ts +14 -36
  48. package/dist/model/malloy_query.js +145 -312
  49. package/dist/model/malloy_types.d.ts +18 -23
  50. package/dist/model/malloy_types.js +2 -15
  51. package/dist/tags.js +19 -4
  52. package/package.json +1 -1
@@ -408,7 +408,7 @@ describe('query operation annotations', () => {
408
408
  const foundYou = m.getQuery('findme');
409
409
  expect(foundYou).toBeDefined();
410
410
  if (foundYou) {
411
- const note_a = (0, test_translator_1.getFieldDef)(foundYou.pipeline[0], 'note_a');
411
+ const note_a = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'note_a');
412
412
  expect(note_a === null || note_a === void 0 ? void 0 : note_a.annotation).matchesAnnotation(defaultTags);
413
413
  }
414
414
  });
@@ -434,7 +434,7 @@ describe('query operation annotations', () => {
434
434
  const foundYou = m.getQuery(0);
435
435
  expect(foundYou).toBeDefined();
436
436
  if (foundYou) {
437
- const note_a = (0, test_translator_1.getFieldDef)(foundYou.pipeline[0], 'note_a');
437
+ const note_a = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'note_a');
438
438
  expect(note_a === null || note_a === void 0 ? void 0 : note_a.annotation).matchesAnnotation({
439
439
  inherits: defaultTags,
440
440
  blockNotes: ['# note1\n'],
@@ -459,7 +459,7 @@ describe('query operation annotations', () => {
459
459
  const foundYou = m.getQuery('findme');
460
460
  expect(foundYou).toBeDefined();
461
461
  if (foundYou) {
462
- const note_a = (0, test_translator_1.getFieldDef)(foundYou.pipeline[0], 'note_a');
462
+ const note_a = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'note_a');
463
463
  expect(note_a === null || note_a === void 0 ? void 0 : note_a.annotation).matchesAnnotation(defaultTags);
464
464
  }
465
465
  });
@@ -481,7 +481,7 @@ describe('query operation annotations', () => {
481
481
  const foundYou = m.getQuery('findme');
482
482
  expect(foundYou).toBeDefined();
483
483
  if (foundYou) {
484
- const note_a = (0, test_translator_1.getFieldDef)(foundYou.pipeline[0], 'note_a');
484
+ const note_a = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'note_a');
485
485
  expect(note_a === null || note_a === void 0 ? void 0 : note_a.annotation).matchesAnnotation(defaultTags);
486
486
  }
487
487
  });
@@ -508,7 +508,7 @@ describe('query operation annotations', () => {
508
508
  const foundYou = m.getQuery('findme');
509
509
  expect(foundYou).toBeDefined();
510
510
  if (foundYou) {
511
- const note_a = (0, test_translator_1.getFieldDef)(foundYou.pipeline[0], 'note_a');
511
+ const note_a = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'note_a');
512
512
  expect(note_a === null || note_a === void 0 ? void 0 : note_a.annotation).matchesAnnotation({
513
513
  blockNotes: ['# note1\n'],
514
514
  inherits: defaultTags,
@@ -533,7 +533,7 @@ describe('query operation annotations', () => {
533
533
  const foundYou = m.getQuery('findme');
534
534
  expect(foundYou).toBeDefined();
535
535
  if (foundYou) {
536
- const note_a = (0, test_translator_1.getFieldDef)(foundYou.pipeline[0], 'note_a');
536
+ const note_a = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'note_a');
537
537
  expect(note_a === null || note_a === void 0 ? void 0 : note_a.annotation).matchesAnnotation(defaultTags);
538
538
  }
539
539
  });
@@ -560,7 +560,7 @@ describe('query operation annotations', () => {
560
560
  const foundYou = m.getQuery('findme');
561
561
  expect(foundYou).toBeDefined();
562
562
  if (foundYou) {
563
- const note_a = (0, test_translator_1.getFieldDef)(foundYou.pipeline[0], 'note_a');
563
+ const note_a = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'note_a');
564
564
  expect(note_a === null || note_a === void 0 ? void 0 : note_a.annotation).matchesAnnotation({
565
565
  blockNotes: ['# note1\n'],
566
566
  inherits: defaultTags,
@@ -585,7 +585,7 @@ describe('query operation annotations', () => {
585
585
  const foundYou = m.getQuery('findme');
586
586
  expect(foundYou).toBeDefined();
587
587
  if (foundYou) {
588
- const note_a = (0, test_translator_1.getFieldDef)(foundYou.pipeline[0], 'note_a');
588
+ const note_a = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'note_a');
589
589
  expect(note_a === null || note_a === void 0 ? void 0 : note_a.annotation).matchesAnnotation(defaultTags);
590
590
  }
591
591
  });
@@ -600,7 +600,7 @@ describe('query operation annotations', () => {
600
600
  const foundYou = m.getQuery(0);
601
601
  expect(foundYou).toBeDefined();
602
602
  if (foundYou) {
603
- const note_b = (0, test_translator_1.getFieldDef)(foundYou.pipeline[0], 'note_b');
603
+ const note_b = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'note_b');
604
604
  expect(note_b === null || note_b === void 0 ? void 0 : note_b.annotation).matchesAnnotation({ inherits: defaultTags });
605
605
  }
606
606
  });
@@ -626,30 +626,29 @@ describe('query operation annotations', () => {
626
626
  const foundYou = m.translator.getQuery(0);
627
627
  expect(foundYou).toBeDefined();
628
628
  if (foundYou) {
629
- const note_b = (0, test_translator_1.getFieldDef)(foundYou.pipeline[0], 'xdim');
629
+ const note_b = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'xdim');
630
630
  expect(note_b === null || note_b === void 0 ? void 0 : note_b.annotation).matchesAnnotation({ inherits: defaultTags });
631
631
  }
632
632
  });
633
- test('annotated references to measures on join preserve expression type', () => {
634
- const m = (0, test_translator_1.model) `
635
- source: na is a extend {
636
- measure:
637
- one_count is sum(pick 1 when ai = 1 else 0)
638
- pct_one is one_count / count();
639
- }
640
-
641
- run: na extend {
642
- join_many: na on ai = na.ai
633
+ test('a reference can have an annotation', () => {
634
+ const m = (0, test_translator_1.model) `run: a extend {
635
+ # noted
636
+ dimension: noted_str is astr
643
637
  } -> {
644
- group_by: ai
645
- aggregate:
646
- # percent
647
- na.pct_one
638
+ select:
639
+ # note
640
+ noted_str
648
641
  }`;
649
642
  expect(m).toTranslate();
650
- const q = m.translator.queryList[0];
651
- const fields = q.pipeline[0].fields;
652
- expect(fields[1]).toMatchObject({ expressionType: 'aggregate' });
643
+ const foundYou = m.translator.getQuery(0);
644
+ expect(foundYou).toBeDefined();
645
+ if (foundYou) {
646
+ const astr = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'noted_str');
647
+ expect(astr === null || astr === void 0 ? void 0 : astr.annotation).matchesAnnotation({
648
+ inherits: { blockNotes: ['# noted\n'] },
649
+ notes: ['# note\n'],
650
+ });
651
+ }
653
652
  });
654
653
  });
655
654
  //# sourceMappingURL=annotation.spec.js.map
@@ -539,46 +539,49 @@ describe('expressions', () => {
539
539
  expect(queryDef).toBeDefined();
540
540
  expect(queryDef === null || queryDef === void 0 ? void 0 : queryDef.type).toBe('query');
541
541
  if (queryDef && queryDef.type === 'query') {
542
- const x = queryDef.pipeline[0].fields[0];
543
- if (typeof x !== 'string' &&
544
- !(0, model_1.isFilteredAliasedName)(x) &&
545
- (0, model_1.isFieldTypeDef)(x) &&
546
- x.type === 'number' &&
547
- x.e) {
548
- expect(x).toMatchObject({
549
- 'e': [
550
- {
551
- 'function': 'numberLiteral',
552
- 'literal': '1',
553
- 'type': 'dialect',
554
- },
555
- // TODO not sure why there are TWO sets of parentheses... A previous version of this test
556
- // just checked that there were ANY parens, so that went under the radar. Not fixing now.
557
- '+((',
558
- {
559
- 'denominator': [
560
- {
561
- 'function': 'numberLiteral',
562
- 'literal': '4',
563
- 'type': 'dialect',
564
- },
565
- ],
566
- 'function': 'div',
567
- 'numerator': [
568
- {
569
- 'function': 'numberLiteral',
570
- 'literal': '3',
571
- 'type': 'dialect',
572
- },
573
- ],
574
- 'type': 'dialect',
575
- },
576
- '))',
577
- ],
578
- });
579
- }
580
- else {
581
- fail('expression with parens compiled oddly');
542
+ const qSeg = queryDef.pipeline[0];
543
+ expect(qSeg.type).toEqual('reduce');
544
+ if (qSeg.type === 'reduce') {
545
+ const x = qSeg.queryFields[0];
546
+ if (x.type !== 'fieldref' &&
547
+ (0, model_1.isFieldTypeDef)(x) &&
548
+ x.type === 'number' &&
549
+ x.e) {
550
+ expect(x).toMatchObject({
551
+ 'e': [
552
+ {
553
+ 'function': 'numberLiteral',
554
+ 'literal': '1',
555
+ 'type': 'dialect',
556
+ },
557
+ // TODO not sure why there are TWO sets of parentheses... A previous version of this test
558
+ // just checked that there were ANY parens, so that went under the radar. Not fixing now.
559
+ '+((',
560
+ {
561
+ 'denominator': [
562
+ {
563
+ 'function': 'numberLiteral',
564
+ 'literal': '4',
565
+ 'type': 'dialect',
566
+ },
567
+ ],
568
+ 'function': 'div',
569
+ 'numerator': [
570
+ {
571
+ 'function': 'numberLiteral',
572
+ 'literal': '3',
573
+ 'type': 'dialect',
574
+ },
575
+ ],
576
+ 'type': 'dialect',
577
+ },
578
+ '))',
579
+ ],
580
+ });
581
+ }
582
+ else {
583
+ fail('expression with parens compiled oddly');
584
+ }
582
585
  }
583
586
  }
584
587
  });
@@ -109,7 +109,7 @@ describe('structdef comprehension', () => {
109
109
  dialect: 'standardsql',
110
110
  structRelationship: {
111
111
  type: 'nested',
112
- field: 'a',
112
+ fieldName: 'a',
113
113
  isArray: false,
114
114
  },
115
115
  structSource: { type: 'nested' },
@@ -145,9 +145,9 @@ describe('structdef comprehension', () => {
145
145
  type: 'one',
146
146
  matrixOperation: 'left',
147
147
  onExpression: [
148
- { type: 'field', path: 'aKey' },
148
+ { type: 'field', path: ['aKey'] },
149
149
  '=',
150
- { type: 'field', path: 't.a' },
150
+ { type: 'field', path: ['t', 'a'] },
151
151
  ],
152
152
  },
153
153
  structSource: { type: 'table', tablePath: 't' },
@@ -166,7 +166,7 @@ describe('structdef comprehension', () => {
166
166
  pipeline: [
167
167
  {
168
168
  type: 'reduce',
169
- fields: ['a'],
169
+ queryFields: [{ type: 'fieldref', path: ['a'] }],
170
170
  },
171
171
  ],
172
172
  };
@@ -72,8 +72,11 @@ describe('source locations', () => {
72
72
  expect(m).toTranslate();
73
73
  const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
74
74
  const x = (0, test_translator_1.getQueryField)(na, 'x');
75
- const y = (0, test_translator_1.getFieldDef)(x.pipeline[0], 'y');
76
- expect(y.location).toMatchObject(source.locations[0]);
75
+ const y = (0, test_translator_1.getQueryFieldDef)(x.pipeline[0], 'y');
76
+ // mtoy todo ask chris if there should be locations
77
+ if (y.type !== 'fieldref') {
78
+ expect(y.location).toMatchObject(source.locations[0]);
79
+ }
77
80
  });
78
81
  test('location of filtered field inside a view', () => {
79
82
  const source = (0, test_translator_1.markSource) `
@@ -87,8 +90,10 @@ describe('source locations', () => {
87
90
  expect(m).toTranslate();
88
91
  const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
89
92
  const x = (0, test_translator_1.getQueryField)(na, 'x');
90
- const z = (0, test_translator_1.getFieldDef)(x.pipeline[0], 'z');
91
- expect(z.location).toMatchObject(source.locations[0]);
93
+ const z = (0, test_translator_1.getQueryFieldDef)(x.pipeline[0], 'z');
94
+ if (z.type !== 'fieldref') {
95
+ expect(z.location).toMatchObject(source.locations[0]);
96
+ }
92
97
  });
93
98
  test('location of field inherited from table', () => {
94
99
  const source = (0, test_translator_1.markSource) `source: na is ${"_db_.table('aTable')"}`;
@@ -144,8 +149,10 @@ describe('source locations', () => {
144
149
  const m = new test_translator_1.TestTranslator(source.code);
145
150
  expect(m).toTranslate();
146
151
  const q = (0, test_translator_1.getModelQuery)(m.modelDef, 'q');
147
- const a = (0, test_translator_1.getFieldDef)(q.pipeline[0], 'b');
148
- expect(a.location).toMatchObject(source.locations[0]);
152
+ const a = (0, test_translator_1.getQueryFieldDef)(q.pipeline[0], 'b');
153
+ if (a.type !== 'fieldref') {
154
+ expect(a.location).toMatchObject(source.locations[0]);
155
+ }
149
156
  });
150
157
  test('pre m4 location of named SQL block', () => {
151
158
  const source = (0, test_translator_1.markSource) `##! -m4warnings\n${'sql: s is { select: """SELECT 1 as one""" }'}`;
@@ -25,6 +25,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
25
25
  const test_translator_1 = require("./test-translator");
26
26
  require("./parse-expects");
27
27
  const model_1 = require("../../model");
28
+ function getFirstQuerySegment(q) {
29
+ const qSeg = q === null || q === void 0 ? void 0 : q.pipeline[0];
30
+ if (qSeg && (0, model_1.isQuerySegment)(qSeg)) {
31
+ return qSeg;
32
+ }
33
+ }
34
+ function getFirstSegmentFields(q) {
35
+ var _a;
36
+ const qSeg = getFirstQuerySegment(q);
37
+ return (_a = qSeg === null || qSeg === void 0 ? void 0 : qSeg.queryFields) !== null && _a !== void 0 ? _a : [];
38
+ }
39
+ function getFirstSegmentFieldNames(q) {
40
+ const qf = getFirstSegmentFields(q);
41
+ return qf.map(f => f.type === 'fieldref' && f.path.length === 1
42
+ ? f.path[0]
43
+ : `expected simple ref, got ${JSON.stringify(f)}`);
44
+ }
28
45
  describe('query:', () => {
29
46
  describe('basic query syntax', () => {
30
47
  test('run:anonymous query', () => expect('run: a -> { group_by: astr }').toTranslate());
@@ -93,11 +110,10 @@ describe('query:', () => {
93
110
  query: nq is q + { group_by: astr }
94
111
  `);
95
112
  expect(x).toTranslate();
96
- const q = x.getQuery('q');
113
+ const q = getFirstSegmentFields(x.getQuery('q'));
97
114
  expect(q).toBeDefined();
98
115
  if (q) {
99
- const qFields = q.pipeline[0].fields;
100
- expect(qFields.length).toBe(1);
116
+ expect(q.length).toBe(1);
101
117
  }
102
118
  });
103
119
  test('query composition preserves original', () => {
@@ -462,13 +478,9 @@ describe('query:', () => {
462
478
  calculate: l is lag(ai, 1, pi)
463
479
  }`).translationToFailWith("Parameter 3 ('default') of lag must be literal or constant, but received output");
464
480
  });
465
- // TODO we don't handle referencing a join as a field correctly in all cases today.
466
- // For now, it at least is considered type `struct` and therefore fails to parse
467
- // as a function argument.
468
- // We add <join_name>_id to the query, but it's not included in the output space
469
481
  test('cannot use struct in function arg', () => {
470
- expect(`run: a extend {join_one: b with astr } -> {
471
- group_by: b
482
+ expect(`run: ab -> {
483
+ group_by: b.astr
472
484
  calculate: foo is lag(b)
473
485
  }`).translationToFailWith('No matching overload for function lag(struct)');
474
486
  });
@@ -558,12 +570,6 @@ describe('query:', () => {
558
570
  expect(`run: a -> {
559
571
  extend: { join_one: b with astr }
560
572
  group_by: b.ai
561
- }`).toTranslate();
562
- });
563
- test('reference join name in group_by', () => {
564
- expect(`run: a -> {
565
- extend: { join_one: b with astr }
566
- group_by: b
567
573
  }`).toTranslate();
568
574
  });
569
575
  test('can reference select: inline join.* field in calculate', () => {
@@ -608,26 +614,22 @@ describe('query:', () => {
608
614
  expect('run: a->{ select: astr, ai calculate: num is lag(ai) }').toTranslate();
609
615
  });
610
616
  test('aggregate reference', () => {
611
- var _a;
612
617
  const doc = (0, test_translator_1.model) `run: a->{ aggregate: ai.sum() }`;
613
618
  expect(doc).toTranslate();
614
- const q = doc.translator.getQuery(0);
619
+ const q = getFirstSegmentFields(doc.translator.getQuery(0));
615
620
  expect(q).toBeDefined();
616
- const ai = (_a = q === null || q === void 0 ? void 0 : q.pipeline[0]) === null || _a === void 0 ? void 0 : _a.fields[0];
617
- expect(ai).toMatchObject({
621
+ expect(q[0]).toMatchObject({
618
622
  name: 'ai',
619
623
  type: 'number',
620
624
  expressionType: 'aggregate',
621
625
  });
622
626
  });
623
627
  test('timeunit reference', () => {
624
- var _a;
625
628
  const doc = (0, test_translator_1.model) `run: a->{ group_by: ats.day }`;
626
629
  expect(doc).toTranslate();
627
- const q = doc.translator.getQuery(0);
630
+ const q = getFirstSegmentFields(doc.translator.getQuery(0));
628
631
  expect(q).toBeDefined();
629
- const ats = (_a = q === null || q === void 0 ? void 0 : q.pipeline[0]) === null || _a === void 0 ? void 0 : _a.fields[0];
630
- expect(ats).toMatchObject({ name: 'ats', type: 'timestamp' });
632
+ expect(q[0]).toMatchObject({ name: 'ats', type: 'timestamp' });
631
633
  });
632
634
  test('aggregate multiple', () => {
633
635
  expect(`
@@ -646,27 +648,23 @@ describe('query:', () => {
646
648
  test('expands star correctly', () => {
647
649
  const selstar = (0, test_translator_1.model) `run: ab->{select: *}`;
648
650
  expect(selstar).toTranslate();
649
- const query = selstar.translator.getQuery(0);
650
- expect(query).toBeDefined();
651
- const fields = query.pipeline[0].fields;
652
- expect(fields.sort()).toEqual(afields);
651
+ const fields = getFirstSegmentFieldNames(selstar.translator.getQuery(0));
652
+ expect(fields).toEqual(afields);
653
653
  });
654
654
  test('expands join dot star correctly', () => {
655
655
  const selstar = (0, test_translator_1.model) `run: ab->{select: b.*}`;
656
656
  expect(selstar).toTranslate();
657
657
  const query = selstar.translator.getQuery(0);
658
658
  expect(query).toBeDefined();
659
- const fields = query.pipeline[0].fields;
660
- expect(fields.sort()).toEqual(afields.map(f => `b.${f}`));
659
+ const fields = getFirstSegmentFields(selstar.translator.getQuery(0)).map(f => (f.type === 'fieldref' ? f.path : `wrong field type ${f.type}`));
660
+ expect(fields).toEqual(afields.map(f => ['b', f]));
661
661
  });
662
662
  test('expands star with exclusions', () => {
663
663
  const selstar = (0, test_translator_1.model) `run: ab->{select: * { except: ai, except: aun, aweird }}`;
664
664
  const filterdFields = afields.filter(f => f !== 'ai' && f !== 'aun' && f !== 'aweird');
665
665
  expect(selstar).toTranslate();
666
- const query = selstar.translator.getQuery(0);
667
- expect(query).toBeDefined();
668
- const fields = query.pipeline[0].fields;
669
- expect(fields.sort()).toEqual(filterdFields);
666
+ const fields = getFirstSegmentFieldNames(selstar.translator.getQuery(0));
667
+ expect(fields).toEqual(filterdFields);
670
668
  });
671
669
  test('star error checking', () => {
672
670
  expect((0, test_translator_1.markSource) `run: a->{select: ${'zzz'}.*}`).translationToFailWith("No such field as 'zzz'");
@@ -686,10 +684,8 @@ describe('query:', () => {
686
684
  test('regress check extend: and star', () => {
687
685
  const m = (0, test_translator_1.model) `run: ab->{ extend: {dimension: x is 1} select: * }`;
688
686
  expect(m).toTranslate();
689
- const q = m.translator.getQuery(0);
690
- expect(q).toBeDefined();
691
- const fields = q.pipeline[0].fields;
692
- expect(fields.sort()).toEqual(afields.concat('x'));
687
+ const fields = getFirstSegmentFieldNames(m.translator.getQuery(0));
688
+ expect(fields).toEqual(afields.concat('x'));
693
689
  });
694
690
  test('project def', () => {
695
691
  expect('run: ab->{ select: one is 1 }').toTranslate();
@@ -701,10 +697,8 @@ describe('query:', () => {
701
697
  test('regress check extend: and star', () => {
702
698
  const m = (0, test_translator_1.model) `run: ab->{ extend: {dimension: x is 1} select: * }`;
703
699
  expect(m).toTranslate();
704
- const q = m.translator.getQuery(0);
705
- expect(q).toBeDefined();
706
- const fields = q.pipeline[0].fields;
707
- expect(fields.sort()).toEqual(afields.concat('x'));
700
+ const fields = getFirstSegmentFieldNames(m.translator.getQuery(0));
701
+ expect(fields).toEqual(afields.concat('x'));
708
702
  });
709
703
  test('project def', () => {
710
704
  expect('run:ab->{ select: one is 1 }').toTranslate();
@@ -720,13 +714,13 @@ describe('query:', () => {
720
714
  expect('run:a->{index: astr}').toTranslate();
721
715
  });
722
716
  test('index path', () => {
723
- expect('run:ab->{index: ab.astr}').toTranslate();
717
+ expect('run:ab->{index: b.astr}').toTranslate();
724
718
  });
725
719
  test('index unique on path', () => {
726
- expect('run:ab->{index: b.astr, ab.astr}').toTranslate();
720
+ expect('run:ab->{index: astr, b.astr}').toTranslate();
727
721
  });
728
722
  test('index join.*', () => {
729
- expect('run:ab->{index: ab.*}').toTranslate();
723
+ expect('run:ab->{index: b.*}').toTranslate();
730
724
  });
731
725
  test('index multiple', () => {
732
726
  const model = new test_translator_1.TestTranslator('run:a->{index: af, astr}');
@@ -736,18 +730,12 @@ describe('query:', () => {
736
730
  if (q) {
737
731
  const index = q.pipeline[0];
738
732
  expect(index.type).toBe('index');
739
- expect(index.fields).toEqual(['af', 'astr']);
740
- }
741
- });
742
- test('index star', () => {
743
- const model = new test_translator_1.TestTranslator('run:a->{index: *, astr}');
744
- expect(model).toTranslate();
745
- const q = model.getQuery(0);
746
- expect(q).toBeDefined();
747
- if (q) {
748
- const index = q.pipeline[0];
749
- expect(index.type).toBe('index');
750
- expect(index.fields).toEqual(['*', 'astr']);
733
+ if (index.type === 'index') {
734
+ expect(index.indexFields).toEqual([
735
+ { type: 'fieldref', path: ['af'] },
736
+ { type: 'fieldref', path: ['astr'] },
737
+ ]);
738
+ }
751
739
  }
752
740
  });
753
741
  test('index by', () => {
@@ -1,4 +1,4 @@
1
- import { DocumentLocation, FieldDef, ModelDef, NamedModelObject, PipeSegment, Query, SQLBlockSource, SQLBlockStructDef, StructDef, TurtleDef } from '../../model/malloy_types';
1
+ import { DocumentLocation, FieldDef, ModelDef, NamedModelObject, PipeSegment, Query, QueryFieldDef, SQLBlockSource, SQLBlockStructDef, StructDef, TurtleDef } from '../../model/malloy_types';
2
2
  import { MalloyElement } from '../ast';
3
3
  import { NameSpace } from '../ast/types/name-space';
4
4
  import { ModelEntry } from '../ast/types/model-entry';
@@ -50,7 +50,8 @@ export declare class BetaExpression extends TestTranslator {
50
50
  }
51
51
  export declare function getExplore(modelDef: ModelDef, name: string): StructDef;
52
52
  export declare function getModelQuery(modelDef: ModelDef, name: string): Query;
53
- export declare function getFieldDef(thing: StructDef | PipeSegment, name: string): FieldDef;
53
+ export declare function getFieldDef(source: StructDef, name: string): FieldDef;
54
+ export declare function getQueryFieldDef(query: PipeSegment, name: string): QueryFieldDef;
54
55
  export declare function getQueryField(structDef: StructDef, name: string): TurtleDef;
55
56
  export declare function getJoinField(structDef: StructDef, name: string): StructDef;
56
57
  export interface MarkedSource {
@@ -23,7 +23,7 @@
23
23
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
24
  */
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.getSelectOneStruct = exports.markSource = exports.makeModelFunc = exports.model = exports.expr = exports.getJoinField = exports.getQueryField = exports.getFieldDef = exports.getModelQuery = exports.getExplore = exports.BetaExpression = exports.TestTranslator = exports.TestChildTranslator = exports.aTableDef = exports.pretty = void 0;
26
+ exports.getSelectOneStruct = exports.markSource = exports.makeModelFunc = exports.model = exports.expr = exports.getJoinField = exports.getQueryField = exports.getQueryFieldDef = exports.getFieldDef = exports.getModelQuery = exports.getExplore = exports.BetaExpression = exports.TestTranslator = exports.TestChildTranslator = exports.aTableDef = exports.pretty = void 0;
27
27
  const util_1 = require("util");
28
28
  const malloy_types_1 = require("../../model/malloy_types");
29
29
  const ast_1 = require("../ast");
@@ -55,7 +55,7 @@ const mockSchema = {
55
55
  type: 'struct',
56
56
  name: 'astruct',
57
57
  structSource: { type: 'nested' },
58
- structRelationship: { type: 'nested', isArray: true, field: 'foo' },
58
+ structRelationship: { type: 'nested', isArray: true, fieldName: 'foo' },
59
59
  fields: [{ type: 'number', name: 'column', numberType: 'integer' }],
60
60
  dialect: 'standardsql',
61
61
  },
@@ -242,9 +242,9 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
242
242
  type: 'one',
243
243
  matrixOperation: 'left',
244
244
  onExpression: [
245
- { type: 'field', path: 'astr' },
245
+ { type: 'field', path: ['astr'] },
246
246
  '=',
247
- { type: 'field', path: 'b.astr' },
247
+ { type: 'field', path: ['b', 'astr'] },
248
248
  ],
249
249
  },
250
250
  },
@@ -262,7 +262,10 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
262
262
  pipeline: [
263
263
  {
264
264
  type: 'reduce',
265
- fields: ['astr', 'acount'],
265
+ queryFields: [
266
+ { type: 'fieldref', path: ['astr'] },
267
+ { type: 'fieldref', path: ['acount'] },
268
+ ],
266
269
  },
267
270
  ],
268
271
  },
@@ -424,30 +427,33 @@ function getModelQuery(modelDef, name) {
424
427
  return modelDef.contents[name];
425
428
  }
426
429
  exports.getModelQuery = getModelQuery;
427
- function getFieldDef(thing, name) {
428
- let found = undefined;
429
- for (const f of thing.fields) {
430
- if (typeof f === 'string') {
431
- if (f === name) {
432
- throw new Error(`Expected def for '${name}', found a ref`);
433
- }
430
+ function getFieldDef(source, name) {
431
+ var _a;
432
+ for (const f of source.fields) {
433
+ if ((_a = f.as) !== null && _a !== void 0 ? _a : f.name === name) {
434
+ return f;
434
435
  }
435
- else if ((f.as || f.name) === name) {
436
- if ((0, malloy_types_1.isFilteredAliasedName)(f)) {
437
- throw new Error(`FilteredAliasedName for '${name}' unexpected`);
436
+ }
437
+ throw new Error(`Compiled source did not contain expected field '${name}'`);
438
+ }
439
+ exports.getFieldDef = getFieldDef;
440
+ function getQueryFieldDef(query, name) {
441
+ var _a;
442
+ if ((0, malloy_types_1.isQuerySegment)(query)) {
443
+ for (const f of query.queryFields) {
444
+ if (f.type === 'fieldref') {
445
+ if (name === f.path[f.path.length - 1]) {
446
+ return f;
447
+ }
448
+ }
449
+ else if ((_a = f.as) !== null && _a !== void 0 ? _a : f.name === name) {
450
+ return f;
438
451
  }
439
- found = f;
440
- break;
441
452
  }
442
453
  }
443
- if (found === undefined) {
444
- throw new Error(`Compiled code did not contain expected field '${name}'`);
445
- }
446
- else {
447
- return found;
448
- }
454
+ throw new Error(`Compiled query did not contain expected field '${name}'`);
449
455
  }
450
- exports.getFieldDef = getFieldDef;
456
+ exports.getQueryFieldDef = getQueryFieldDef;
451
457
  // TODO "as" is almost always a code smell ...
452
458
  function getQueryField(structDef, name) {
453
459
  return getFieldDef(structDef, name);
package/dist/malloy.js CHANGED
@@ -1000,7 +1000,7 @@ class Explore extends Entity {
1000
1000
  pipeline: [
1001
1001
  {
1002
1002
  type: 'reduce',
1003
- fields: [name],
1003
+ queryFields: [{ type: 'fieldref', path: [name] }],
1004
1004
  },
1005
1005
  ],
1006
1006
  };