@graffy/pg 0.16.20-alpha.9 → 0.16.21-alpha.1

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/index.cjs CHANGED
@@ -79,6 +79,20 @@ const empty = raw("");
79
79
  function sql(strings, ...values) {
80
80
  return new Sql(strings, values);
81
81
  }
82
+ function formatSql(sql2) {
83
+ const strings = sql2.strings.slice(0);
84
+ const values = sql2.values.slice(0);
85
+ const output = [];
86
+ while (strings.length) {
87
+ output.push(strings.shift().replace(/\s+/g, " "));
88
+ if (!values.length) break;
89
+ const value = values.shift();
90
+ output.push(
91
+ typeof value === "number" ? value.toString() : typeof value === "object" ? `'${JSON.stringify(value)}'` : `'${value}'`
92
+ );
93
+ }
94
+ return output.join("").trim();
95
+ }
82
96
  const getJsonBuildTrusted = (variadic) => {
83
97
  const args = join(
84
98
  Object.entries(variadic).map(([name, value]) => {
@@ -116,6 +130,33 @@ const aggSql = {
116
130
  $max: (prop) => sql`max((${lookupNumeric(prop)})::numeric)`,
117
131
  $min: (prop) => sql`min((${lookupNumeric(prop)})::numeric)`
118
132
  };
133
+ const getOptimisedJsonBuild = (object, path, parentPropertyName, options) => {
134
+ const propertyNames = Object.keys(object);
135
+ const propertyPath = [...path, parentPropertyName];
136
+ const buildConfig = [];
137
+ path = path || [];
138
+ propertyNames.forEach((propertyName) => {
139
+ const property = object[propertyName];
140
+ if (typeof property === "object") {
141
+ const childJSONBuild = getOptimisedJsonBuild(
142
+ property,
143
+ propertyPath,
144
+ propertyName,
145
+ options
146
+ );
147
+ buildConfig.push(
148
+ sql`${propertyName}::text, jsonb_build_object(${join(childJSONBuild, ", ")})`
149
+ );
150
+ } else {
151
+ if (property === true) {
152
+ buildConfig.push(
153
+ sql`${propertyName}::text, ${lookup([...propertyPath, propertyName].join("."), options)}`
154
+ );
155
+ }
156
+ }
157
+ });
158
+ return buildConfig;
159
+ };
119
160
  const getSelectCols = (options, projection = null) => {
120
161
  if (!projection) return sql`*`;
121
162
  const sqls = [];
@@ -130,11 +171,23 @@ const getSelectCols = (options, projection = null) => {
130
171
  sqls.push(
131
172
  sql`jsonb_build_object(${join(subSqls, ", ")}) AS "${raw(key)}"`
132
173
  );
174
+ }
175
+ if (key[0] === "$") continue;
176
+ if (typeof projection[key] === "object") {
177
+ const optimisedJsonBuild = getOptimisedJsonBuild(
178
+ projection[key],
179
+ [],
180
+ key,
181
+ options
182
+ );
183
+ sqls.push(
184
+ sql`jsonb_build_object(${join(optimisedJsonBuild, ", ")}) AS "${raw(key)}"`
185
+ );
133
186
  } else {
134
187
  sqls.push(sql`"${raw(key)}"`);
135
188
  }
136
189
  }
137
- return join(sqls, ", ");
190
+ return sqls.length ? join(sqls, ", ") : sql`TRUE AS "$"`;
138
191
  };
139
192
  function vertexSql(array, nullValue) {
140
193
  return sql`array[${join(
@@ -715,7 +768,7 @@ class Db {
715
768
  /*
716
769
  Adds .schema to tableOptions if it doesn't exist yet.
717
770
  It mutates the argument, to "persist" the results and
718
- avoid this query in every operation.
771
+ avoid this query in every operation.
719
772
  */
720
773
  async ensureSchema(tableOptions, typeOids) {
721
774
  if (tableOptions.schema) return;
@@ -766,19 +819,33 @@ class Db {
766
819
  const prefix = common.encodePath(rawPrefix);
767
820
  await this.ensureSchema(tableOptions);
768
821
  const getByArgs = async (args, projection) => {
769
- const result = await this.readSql(
770
- selectByArgs(args, projection, tableOptions),
771
- tableOptions
772
- );
822
+ const sql2 = selectByArgs(args, projection, tableOptions);
823
+ const result = await this.readSql(sql2, tableOptions);
773
824
  const wrappedGraph = common.encodeGraph(common.wrapObject(result, rawPrefix));
774
825
  log("getByArgs", wrappedGraph);
775
826
  common.merge(results, wrappedGraph);
776
827
  };
777
- const getByIds = async () => {
778
- const result = await this.readSql(
779
- selectByIds(Object.keys(idQueries), null, tableOptions),
780
- tableOptions
828
+ const explainArgs = async (args, projection) => {
829
+ const { $analyze, ...qArgs } = args.$explain;
830
+ const qSql = selectByArgs(qArgs, null, tableOptions);
831
+ const sql$1 = sql`EXPLAIN (${$analyze ? sql`ANALYZE, BUFFERS, TIMING, ` : sql``}COSTS, VERBOSE, FORMAT JSON) ${qSql}`;
832
+ const result = await this.readSql(sql$1, tableOptions);
833
+ const wrappedGraph = common.encodeGraph(
834
+ common.wrapObject(
835
+ {
836
+ $key: args,
837
+ sql: formatSql(qSql),
838
+ plan: result[0]["QUERY PLAN"][0]
839
+ },
840
+ rawPrefix
841
+ )
781
842
  );
843
+ log("explainArgs", wrappedGraph);
844
+ common.merge(results, wrappedGraph);
845
+ };
846
+ const getByIds = async () => {
847
+ const sql2 = selectByIds(Object.keys(idQueries), null, tableOptions);
848
+ const result = await this.readSql(sql2, tableOptions);
782
849
  for (const object of result) {
783
850
  const wrappedGraph = common.encodeGraph(common.wrapObject(object, rawPrefix));
784
851
  log("getByIds", wrappedGraph);
@@ -797,7 +864,11 @@ class Db {
797
864
  }
798
865
  } else {
799
866
  const projection = node.children ? common.decodeQuery(node.children) : null;
800
- promises.push(getByArgs(args, projection));
867
+ if (args.$explain) {
868
+ promises.push(explainArgs(args));
869
+ } else {
870
+ promises.push(getByArgs(args, projection));
871
+ }
801
872
  }
802
873
  } else {
803
874
  idQueries[args] = node.children;
package/index.mjs CHANGED
@@ -77,6 +77,20 @@ const empty = raw("");
77
77
  function sql(strings, ...values) {
78
78
  return new Sql(strings, values);
79
79
  }
80
+ function formatSql(sql2) {
81
+ const strings = sql2.strings.slice(0);
82
+ const values = sql2.values.slice(0);
83
+ const output = [];
84
+ while (strings.length) {
85
+ output.push(strings.shift().replace(/\s+/g, " "));
86
+ if (!values.length) break;
87
+ const value = values.shift();
88
+ output.push(
89
+ typeof value === "number" ? value.toString() : typeof value === "object" ? `'${JSON.stringify(value)}'` : `'${value}'`
90
+ );
91
+ }
92
+ return output.join("").trim();
93
+ }
80
94
  const getJsonBuildTrusted = (variadic) => {
81
95
  const args = join(
82
96
  Object.entries(variadic).map(([name, value]) => {
@@ -114,6 +128,33 @@ const aggSql = {
114
128
  $max: (prop) => sql`max((${lookupNumeric(prop)})::numeric)`,
115
129
  $min: (prop) => sql`min((${lookupNumeric(prop)})::numeric)`
116
130
  };
131
+ const getOptimisedJsonBuild = (object, path, parentPropertyName, options) => {
132
+ const propertyNames = Object.keys(object);
133
+ const propertyPath = [...path, parentPropertyName];
134
+ const buildConfig = [];
135
+ path = path || [];
136
+ propertyNames.forEach((propertyName) => {
137
+ const property = object[propertyName];
138
+ if (typeof property === "object") {
139
+ const childJSONBuild = getOptimisedJsonBuild(
140
+ property,
141
+ propertyPath,
142
+ propertyName,
143
+ options
144
+ );
145
+ buildConfig.push(
146
+ sql`${propertyName}::text, jsonb_build_object(${join(childJSONBuild, ", ")})`
147
+ );
148
+ } else {
149
+ if (property === true) {
150
+ buildConfig.push(
151
+ sql`${propertyName}::text, ${lookup([...propertyPath, propertyName].join("."), options)}`
152
+ );
153
+ }
154
+ }
155
+ });
156
+ return buildConfig;
157
+ };
117
158
  const getSelectCols = (options, projection = null) => {
118
159
  if (!projection) return sql`*`;
119
160
  const sqls = [];
@@ -128,11 +169,23 @@ const getSelectCols = (options, projection = null) => {
128
169
  sqls.push(
129
170
  sql`jsonb_build_object(${join(subSqls, ", ")}) AS "${raw(key)}"`
130
171
  );
172
+ }
173
+ if (key[0] === "$") continue;
174
+ if (typeof projection[key] === "object") {
175
+ const optimisedJsonBuild = getOptimisedJsonBuild(
176
+ projection[key],
177
+ [],
178
+ key,
179
+ options
180
+ );
181
+ sqls.push(
182
+ sql`jsonb_build_object(${join(optimisedJsonBuild, ", ")}) AS "${raw(key)}"`
183
+ );
131
184
  } else {
132
185
  sqls.push(sql`"${raw(key)}"`);
133
186
  }
134
187
  }
135
- return join(sqls, ", ");
188
+ return sqls.length ? join(sqls, ", ") : sql`TRUE AS "$"`;
136
189
  };
137
190
  function vertexSql(array, nullValue) {
138
191
  return sql`array[${join(
@@ -713,7 +766,7 @@ class Db {
713
766
  /*
714
767
  Adds .schema to tableOptions if it doesn't exist yet.
715
768
  It mutates the argument, to "persist" the results and
716
- avoid this query in every operation.
769
+ avoid this query in every operation.
717
770
  */
718
771
  async ensureSchema(tableOptions, typeOids) {
719
772
  if (tableOptions.schema) return;
@@ -764,19 +817,33 @@ class Db {
764
817
  const prefix = encodePath(rawPrefix);
765
818
  await this.ensureSchema(tableOptions);
766
819
  const getByArgs = async (args, projection) => {
767
- const result = await this.readSql(
768
- selectByArgs(args, projection, tableOptions),
769
- tableOptions
770
- );
820
+ const sql2 = selectByArgs(args, projection, tableOptions);
821
+ const result = await this.readSql(sql2, tableOptions);
771
822
  const wrappedGraph = encodeGraph(wrapObject(result, rawPrefix));
772
823
  log("getByArgs", wrappedGraph);
773
824
  merge(results, wrappedGraph);
774
825
  };
775
- const getByIds = async () => {
776
- const result = await this.readSql(
777
- selectByIds(Object.keys(idQueries), null, tableOptions),
778
- tableOptions
826
+ const explainArgs = async (args, projection) => {
827
+ const { $analyze, ...qArgs } = args.$explain;
828
+ const qSql = selectByArgs(qArgs, null, tableOptions);
829
+ const sql$1 = sql`EXPLAIN (${$analyze ? sql`ANALYZE, BUFFERS, TIMING, ` : sql``}COSTS, VERBOSE, FORMAT JSON) ${qSql}`;
830
+ const result = await this.readSql(sql$1, tableOptions);
831
+ const wrappedGraph = encodeGraph(
832
+ wrapObject(
833
+ {
834
+ $key: args,
835
+ sql: formatSql(qSql),
836
+ plan: result[0]["QUERY PLAN"][0]
837
+ },
838
+ rawPrefix
839
+ )
779
840
  );
841
+ log("explainArgs", wrappedGraph);
842
+ merge(results, wrappedGraph);
843
+ };
844
+ const getByIds = async () => {
845
+ const sql2 = selectByIds(Object.keys(idQueries), null, tableOptions);
846
+ const result = await this.readSql(sql2, tableOptions);
780
847
  for (const object of result) {
781
848
  const wrappedGraph = encodeGraph(wrapObject(object, rawPrefix));
782
849
  log("getByIds", wrappedGraph);
@@ -795,7 +862,11 @@ class Db {
795
862
  }
796
863
  } else {
797
864
  const projection = node.children ? decodeQuery(node.children) : null;
798
- promises.push(getByArgs(args, projection));
865
+ if (args.$explain) {
866
+ promises.push(explainArgs(args));
867
+ } else {
868
+ promises.push(getByArgs(args, projection));
869
+ }
799
870
  }
800
871
  } else {
801
872
  idQueries[args] = node.children;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@graffy/pg",
3
3
  "description": "The standard Postgres module for Graffy. Each instance this module mounts a Postgres table as a Graffy subtree.",
4
4
  "author": "aravind (https://github.com/aravindet)",
5
- "version": "0.16.20-alpha.9",
5
+ "version": "0.16.21-alpha.1",
6
6
  "main": "./index.cjs",
7
7
  "exports": {
8
8
  "import": "./index.mjs",
@@ -16,8 +16,8 @@
16
16
  },
17
17
  "license": "Apache-2.0",
18
18
  "dependencies": {
19
- "@graffy/common": "0.16.20-alpha.9",
20
- "debug": "^4.3.7"
19
+ "@graffy/common": "0.16.21-alpha.1",
20
+ "debug": "^4.3.3"
21
21
  },
22
22
  "peerDependencies": {
23
23
  "pg": "^8.0.0"
package/types/Db.d.ts CHANGED
@@ -6,8 +6,8 @@ export default class Db {
6
6
  writeSql(sql: any, tableOptions: any): Promise<any>;
7
7
  ensureSchema(tableOptions: any, typeOids: any): Promise<void>;
8
8
  read(rootQuery: any, tableOptions: any): Promise<{
9
- key: Uint8Array;
10
- end: Uint8Array;
9
+ key: Uint8Array<ArrayBuffer>;
10
+ end: Uint8Array<ArrayBuffer>;
11
11
  version: number;
12
12
  }[]>;
13
13
  write(rootChange: any, tableOptions: any): Promise<any[]>;
@@ -0,0 +1 @@
1
+ export default function formatSql(sql: any): string;