@malloydata/malloy 0.0.105-dev231122194042 → 0.0.105-dev231127164722

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.
package/dist/malloy.d.ts CHANGED
@@ -1234,6 +1234,7 @@ declare class DataNull extends Data<null> {
1234
1234
  export declare class DataArray extends Data<QueryData> implements Iterable<DataRecord> {
1235
1235
  private queryData;
1236
1236
  protected _field: Explore;
1237
+ private rowCache;
1237
1238
  constructor(queryData: QueryData, field: Explore, parent: DataArrayOrRecord | undefined, parentRecord: DataRecord | undefined);
1238
1239
  /**
1239
1240
  * @return The `Explore` that describes the structure of this data.
@@ -1256,6 +1257,7 @@ export declare class DataRecord extends Data<{
1256
1257
  private queryDataRow;
1257
1258
  protected _field: Explore;
1258
1259
  readonly index: number | undefined;
1260
+ private cellCache;
1259
1261
  constructor(queryDataRow: QueryDataRow, index: number | undefined, field: Explore, parent: DataArrayOrRecord | undefined, parentRecord: DataRecord | undefined);
1260
1262
  toObject(): QueryDataRow;
1261
1263
  path(...path: (number | string)[]): DataColumn;
package/dist/malloy.js CHANGED
@@ -2611,6 +2611,7 @@ class DataNull extends Data {
2611
2611
  class DataArray extends Data {
2612
2612
  constructor(queryData, field, parent, parentRecord) {
2613
2613
  super(field, parent, parentRecord);
2614
+ this.rowCache = new Map();
2614
2615
  this.queryData = queryData;
2615
2616
  this._field = field;
2616
2617
  }
@@ -2630,6 +2631,12 @@ class DataArray extends Data {
2630
2631
  return getPath(this, path);
2631
2632
  }
2632
2633
  row(index) {
2634
+ let record = this.rowCache.get(index);
2635
+ if (!record) {
2636
+ record = new DataRecord(this.queryData[index], index, this.field, this, this.parentRecord);
2637
+ this.rowCache.set(index, record);
2638
+ }
2639
+ return record;
2633
2640
  return new DataRecord(this.queryData[index], index, this.field, this, this.parentRecord);
2634
2641
  }
2635
2642
  get rowCount() {
@@ -2674,6 +2681,7 @@ function getPath(data, path) {
2674
2681
  class DataRecord extends Data {
2675
2682
  constructor(queryDataRow, index, field, parent, parentRecord) {
2676
2683
  super(field, parent, parentRecord);
2684
+ this.cellCache = new Map();
2677
2685
  this.queryDataRow = queryDataRow;
2678
2686
  this._field = field;
2679
2687
  this.index = index;
@@ -2687,41 +2695,48 @@ class DataRecord extends Data {
2687
2695
  cell(fieldOrName) {
2688
2696
  const fieldName = typeof fieldOrName === 'string' ? fieldOrName : fieldOrName.name;
2689
2697
  const field = this._field.getFieldByName(fieldName);
2690
- const value = this.queryDataRow[fieldName];
2691
- if (value === null) {
2692
- return new DataNull(field, this, this);
2693
- }
2694
- if (field.isAtomicField()) {
2695
- if (field.isBoolean()) {
2696
- return new DataBoolean(value, field, this, this);
2697
- }
2698
- else if (field.isDate()) {
2699
- return new DataDate(value, field, this, this);
2700
- }
2701
- else if (field.isJSON()) {
2702
- return new DataJSON(value, field, this, this);
2703
- }
2704
- else if (field.isTimestamp()) {
2705
- return new DataTimestamp(value, field, this, this);
2698
+ let column = this.cellCache.get(fieldName);
2699
+ if (!column) {
2700
+ const value = this.queryDataRow[fieldName];
2701
+ if (value === null) {
2702
+ column = new DataNull(field, this, this);
2706
2703
  }
2707
- else if (field.isNumber()) {
2708
- return new DataNumber(value, field, this, this);
2709
- }
2710
- else if (field.isString()) {
2711
- return new DataString(value, field, this, this);
2712
- }
2713
- else if (field.isUnsupported()) {
2714
- return new DataUnsupported(value, field, this, this);
2715
- }
2716
- }
2717
- else if (field.isExploreField()) {
2718
- if (Array.isArray(value)) {
2719
- return new DataArray(value, field, this, this);
2704
+ else if (field.isAtomicField()) {
2705
+ if (field.isBoolean()) {
2706
+ column = new DataBoolean(value, field, this, this);
2707
+ }
2708
+ else if (field.isDate()) {
2709
+ column = new DataDate(value, field, this, this);
2710
+ }
2711
+ else if (field.isJSON()) {
2712
+ column = new DataJSON(value, field, this, this);
2713
+ }
2714
+ else if (field.isTimestamp()) {
2715
+ column = new DataTimestamp(value, field, this, this);
2716
+ }
2717
+ else if (field.isNumber()) {
2718
+ column = new DataNumber(value, field, this, this);
2719
+ }
2720
+ else if (field.isString()) {
2721
+ column = new DataString(value, field, this, this);
2722
+ }
2723
+ else if (field.isUnsupported()) {
2724
+ column = new DataUnsupported(value, field, this, this);
2725
+ }
2720
2726
  }
2721
- else {
2722
- return new DataRecord(value, undefined, field, this, this);
2727
+ else if (field.isExploreField()) {
2728
+ if (Array.isArray(value)) {
2729
+ column = new DataArray(value, field, this, this);
2730
+ }
2731
+ else {
2732
+ column = new DataRecord(value, undefined, field, this, this);
2733
+ }
2723
2734
  }
2735
+ if (column)
2736
+ this.cellCache.set(fieldName, column);
2724
2737
  }
2738
+ if (column)
2739
+ return column;
2725
2740
  throw new Error(`Internal Error: could not construct data column for field '${fieldName}'.`);
2726
2741
  }
2727
2742
  get value() {
@@ -230,8 +230,9 @@ declare class QueryQuery extends QueryField {
230
230
  rootResult: FieldInstanceResultRoot;
231
231
  resultStage: string | undefined;
232
232
  stageWriter: StageWriter | undefined;
233
- constructor(fieldDef: TurtleDef, parent: QueryStruct, stageWriter: StageWriter | undefined);
234
- static makeQuery(fieldDef: TurtleDef, parentStruct: QueryStruct, stageWriter?: StageWriter | undefined): QueryQuery;
233
+ isJoinedSubquery: boolean;
234
+ constructor(fieldDef: TurtleDef, parent: QueryStruct, stageWriter: StageWriter | undefined, isJoinedSubquery: boolean);
235
+ static makeQuery(fieldDef: TurtleDef, parentStruct: QueryStruct, stageWriter: StageWriter | undefined, isJoinedSubquery: boolean): QueryQuery;
235
236
  inNestedPipeline(): boolean;
236
237
  getFieldList(): QueryFieldDef[];
237
238
  expandField(f: QueryFieldDef): {
@@ -355,7 +356,7 @@ export declare class QueryModel {
355
356
  };
356
357
  getStructByName(name: string): QueryStruct;
357
358
  getStructFromRef(structRef: StructRef): QueryStruct;
358
- loadQuery(query: Query, stageWriter: StageWriter | undefined, emitFinalStage?: boolean): QueryResults;
359
+ loadQuery(query: Query, stageWriter: StageWriter | undefined, emitFinalStage?: boolean, isJoinedSubquery?: boolean): QueryResults;
359
360
  compileQuery(query: Query, finalize?: boolean): CompiledQuery;
360
361
  exploreSearchSQLMap: Map<any, any>;
361
362
  searchIndex(connection: Connection, explore: string, searchValue: string, limit?: number, searchField?: string | undefined): Promise<SearchIndexResult[] | undefined>;
@@ -1262,15 +1262,15 @@ class Segment {
1262
1262
  name: 'ignoreme',
1263
1263
  pipeline: [segment],
1264
1264
  };
1265
- const queryQueryQuery = QueryQuery.makeQuery(turtleDef, qs, new StageWriter(true, undefined) // stage write indicates we want to get a result.
1266
- );
1265
+ const queryQueryQuery = QueryQuery.makeQuery(turtleDef, qs, new StageWriter(true, undefined), // stage write indicates we want to get a result.
1266
+ false);
1267
1267
  return queryQueryQuery.getResultStructDef();
1268
1268
  }
1269
1269
  }
1270
1270
  exports.Segment = Segment;
1271
1271
  /** Query builder object. */
1272
1272
  class QueryQuery extends QueryField {
1273
- constructor(fieldDef, parent, stageWriter) {
1273
+ constructor(fieldDef, parent, stageWriter, isJoinedSubquery) {
1274
1274
  super(fieldDef, parent);
1275
1275
  this.prepared = false;
1276
1276
  this.maxDepth = 0;
@@ -1280,8 +1280,9 @@ class QueryQuery extends QueryField {
1280
1280
  this.stageWriter = stageWriter;
1281
1281
  // do some magic here to get the first segment.
1282
1282
  this.firstSegment = fieldDef.pipeline[0];
1283
+ this.isJoinedSubquery = isJoinedSubquery;
1283
1284
  }
1284
- static makeQuery(fieldDef, parentStruct, stageWriter = undefined) {
1285
+ static makeQuery(fieldDef, parentStruct, stageWriter = undefined, isJoinedSubquery) {
1285
1286
  let flatTurtleDef = parentStruct.flattenTurtleDef(fieldDef);
1286
1287
  let parent = parentStruct;
1287
1288
  const firstStage = flatTurtleDef.pipeline[0];
@@ -1315,11 +1316,11 @@ class QueryQuery extends QueryField {
1315
1316
  }
1316
1317
  switch (firstStage.type) {
1317
1318
  case 'reduce':
1318
- return new QueryQueryReduce(flatTurtleDef, parent, stageWriter);
1319
+ return new QueryQueryReduce(flatTurtleDef, parent, stageWriter, isJoinedSubquery);
1319
1320
  case 'project':
1320
- return new QueryQueryProject(flatTurtleDef, parent, stageWriter);
1321
+ return new QueryQueryProject(flatTurtleDef, parent, stageWriter, isJoinedSubquery);
1321
1322
  case 'index':
1322
- return new QueryQueryIndex(flatTurtleDef, parent, stageWriter);
1323
+ return new QueryQueryIndex(flatTurtleDef, parent, stageWriter, isJoinedSubquery);
1323
1324
  case 'partial':
1324
1325
  throw new Error('Attempt to make query out of partial stage');
1325
1326
  }
@@ -1370,7 +1371,7 @@ class QueryQuery extends QueryField {
1370
1371
  const newFieldDef = (0, cloneDeep_1.default)(field.fieldDef);
1371
1372
  newFieldDef.as = f.name;
1372
1373
  newFieldDef.filterList = f.filterList;
1373
- field = QueryQuery.makeQuery(newFieldDef, this.parent);
1374
+ field = QueryQuery.makeQuery(newFieldDef, this.parent, undefined, this.isJoinedSubquery);
1374
1375
  }
1375
1376
  else if (!(field instanceof QueryFieldTimestamp ||
1376
1377
  field instanceof QueryFieldDate)) {
@@ -1984,6 +1985,22 @@ class QueryQuery extends QueryField {
1984
1985
  if (this.firstSegment.type === 'project' && !queryDef.orderBy) {
1985
1986
  return ''; // No default ordering for project.
1986
1987
  }
1988
+ // Intermediate results (in a pipeline or join) that have no limit, don't need an orderby
1989
+ // Some database don't have this optimization.
1990
+ if (this.fieldDef.pipeline.length > 1 && queryDef.limit === undefined) {
1991
+ return '';
1992
+ }
1993
+ // ignore orderby if all aggregates.
1994
+ if (resultStruct.getRepeatedResultType() === 'inline_all_numbers') {
1995
+ return '';
1996
+ }
1997
+ // if we are in the last stage of a query and the query is a subquery
1998
+ // and has no limit, ORDER BY is superfluous
1999
+ if (this.isJoinedSubquery &&
2000
+ this.fieldDef.pipeline.length === 1 &&
2001
+ queryDef.limit === undefined) {
2002
+ return '';
2003
+ }
1987
2004
  const orderBy = queryDef.orderBy || resultStruct.calculateDefaultOrderBy();
1988
2005
  const o = [];
1989
2006
  for (const f of orderBy) {
@@ -2439,7 +2456,7 @@ class QueryQuery extends QueryField {
2439
2456
  const qs = new QueryStruct(structDef, {
2440
2457
  model: this.parent.getModel(),
2441
2458
  });
2442
- const q = QueryQuery.makeQuery(newTurtle, qs, stageWriter);
2459
+ const q = QueryQuery.makeQuery(newTurtle, qs, stageWriter, this.isJoinedSubquery);
2443
2460
  pipeOut = q.generateSQLFromPipeline(stageWriter);
2444
2461
  // console.log(stageWriter.generateSQLStages());
2445
2462
  structDef = pipeOut.outputStruct;
@@ -2497,7 +2514,7 @@ class QueryQuery extends QueryField {
2497
2514
  const s = new QueryStruct(structDef, {
2498
2515
  model: this.parent.getModel(),
2499
2516
  });
2500
- const q = QueryQuery.makeQuery({ type: 'turtle', name: 'ignoreme', pipeline: [transform] }, s, stageWriter);
2517
+ const q = QueryQuery.makeQuery({ type: 'turtle', name: 'ignoreme', pipeline: [transform] }, s, stageWriter, this.isJoinedSubquery);
2501
2518
  q.prepare(stageWriter);
2502
2519
  lastStageName = q.generateSQL(stageWriter);
2503
2520
  outputStruct = q.getResultStructDef();
@@ -2518,8 +2535,8 @@ class QueryQueryProject extends QueryQuery {
2518
2535
  // wildcards have been expanded
2519
2536
  // nested repeated fields are safe to use.
2520
2537
  class QueryQueryIndexStage extends QueryQuery {
2521
- constructor(fieldDef, parent, stageWriter) {
2522
- super(fieldDef, parent, stageWriter);
2538
+ constructor(fieldDef, parent, stageWriter, isJoinedSubquery) {
2539
+ super(fieldDef, parent, stageWriter, isJoinedSubquery);
2523
2540
  this.fieldDef = fieldDef;
2524
2541
  }
2525
2542
  // get a field ref and expand it.
@@ -2632,8 +2649,8 @@ FROM ${resultStage}\n`);
2632
2649
  }
2633
2650
  }
2634
2651
  class QueryQueryIndex extends QueryQuery {
2635
- constructor(fieldDef, parent, stageWriter) {
2636
- super(fieldDef, parent, stageWriter);
2652
+ constructor(fieldDef, parent, stageWriter, isJoinedSubquery) {
2653
+ super(fieldDef, parent, stageWriter, isJoinedSubquery);
2637
2654
  this.rootFields = [];
2638
2655
  this.fanPrefixMap = {};
2639
2656
  this.fieldDef = fieldDef;
@@ -2720,7 +2737,7 @@ class QueryQueryIndex extends QueryQuery {
2720
2737
  fields: fields,
2721
2738
  },
2722
2739
  ],
2723
- }, this.parent, stageWriter);
2740
+ }, this.parent, stageWriter, this.isJoinedSubquery);
2724
2741
  q.prepare(stageWriter);
2725
2742
  const lastStageName = q.generateSQL(stageWriter);
2726
2743
  outputStageNames.push(lastStageName);
@@ -2799,7 +2816,7 @@ class QueryStruct extends QueryNode {
2799
2816
  // case "reduce" || "project" || "index": {
2800
2817
  case 'turtle': {
2801
2818
  // not sure why we need to cast here...
2802
- this.addFieldToNameMap(as, QueryQuery.makeQuery(field, this));
2819
+ this.addFieldToNameMap(as, QueryQuery.makeQuery(field, this, undefined, false));
2803
2820
  break;
2804
2821
  }
2805
2822
  default: {
@@ -3077,12 +3094,13 @@ class QueryStruct extends QueryNode {
3077
3094
  // should be cached.
3078
3095
  if (name.includes('cache')) {
3079
3096
  const dtStageWriter = new StageWriter(true, stageWriter);
3080
- this.model.loadQuery(this.fieldDef.structSource.query, dtStageWriter);
3097
+ this.model.loadQuery(this.fieldDef.structSource.query, dtStageWriter, false, false);
3081
3098
  return dtStageWriter.addPDT(name, this.dialect);
3082
3099
  }
3083
3100
  else {
3084
3101
  // returns the stage name.
3085
- return this.model.loadQuery(this.fieldDef.structSource.query, stageWriter).lastStageName;
3102
+ return this.model.loadQuery(this.fieldDef.structSource.query, stageWriter, false, true // this is an intermediate stage.
3103
+ ).lastStageName;
3086
3104
  }
3087
3105
  }
3088
3106
  default:
@@ -3269,7 +3287,7 @@ class QueryModel {
3269
3287
  }
3270
3288
  return new QueryStruct(structDef, { model: this });
3271
3289
  }
3272
- loadQuery(query, stageWriter, emitFinalStage = false) {
3290
+ loadQuery(query, stageWriter, emitFinalStage = false, isJoinedSubquery = false) {
3273
3291
  const malloy = '';
3274
3292
  if (!stageWriter) {
3275
3293
  stageWriter = new StageWriter(true, undefined);
@@ -3281,7 +3299,7 @@ class QueryModel {
3281
3299
  pipeline: query.pipeline,
3282
3300
  filterList: query.filterList,
3283
3301
  };
3284
- const q = QueryQuery.makeQuery(turtleDef, this.getStructFromRef(query.structRef), stageWriter);
3302
+ const q = QueryQuery.makeQuery(turtleDef, this.getStructFromRef(query.structRef), stageWriter, isJoinedSubquery);
3285
3303
  const ret = q.generateSQLFromPipeline(stageWriter);
3286
3304
  if (emitFinalStage && q.parent.dialect.hasFinalStage) {
3287
3305
  // const fieldNames: string[] = [];
@@ -3302,7 +3320,7 @@ class QueryModel {
3302
3320
  compileQuery(query, finalize = true) {
3303
3321
  let newModel;
3304
3322
  const m = newModel || this;
3305
- const ret = m.loadQuery(query, undefined, finalize);
3323
+ const ret = m.loadQuery(query, undefined, finalize, false);
3306
3324
  const sourceExplore = typeof query.structRef === 'string'
3307
3325
  ? query.structRef
3308
3326
  : // LTNOTE: the parser needs to capture the query before the |. This will work
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloy",
3
- "version": "0.0.105-dev231122194042",
3
+ "version": "0.0.105-dev231127164722",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",