@peerbit/indexer-sqlite3 1.0.2 → 1.0.3

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/src/schema.ts CHANGED
@@ -72,6 +72,13 @@ export const convertToSQLType = (
72
72
  const nullAsUndefined = (value: any) => (value === null ? undefined : value);
73
73
  export const escapeColumnName = (name: string) => `"${name}"`;
74
74
 
75
+ export class MissingFieldError extends Error {
76
+ constructor(message: string) {
77
+ super(message);
78
+ this.name = "MissingFieldError";
79
+ }
80
+ }
81
+
75
82
  export const convertFromSQLType = (
76
83
  value: boolean | bigint | string | number | Uint8Array,
77
84
  type?: FieldType,
@@ -814,10 +821,22 @@ export const getTablePrefixedField = (
814
821
  `${skipPrefix ? "" : table.name + "#"}${getInlineTableFieldName(table.path.slice(1), key)}`;
815
822
  export const getTableNameFromPrefixedField = (prefixedField: string) =>
816
823
  prefixedField.split("#")[0];
824
+
817
825
  export const getInlineTableFieldName = (
818
826
  path: string[] | undefined,
819
- key: string,
820
- ) => (path && path.length > 0 ? `${path.join("_")}__${key}` : key);
827
+ key?: string,
828
+ ) => {
829
+ if (key) {
830
+ return path && path.length > 0 ? `${path.join("_")}__${key}` : key;
831
+ } else {
832
+ // last element in the path is the key, the rest is the path
833
+ // join key with __ , rest with _
834
+
835
+ return path!.length > 2
836
+ ? `${path!.slice(0, -1).join("_")}__${path![path!.length - 1]}`
837
+ : path!.join("__");
838
+ }
839
+ };
821
840
 
822
841
  const matchFieldInShape = (
823
842
  shape: types.Shape | undefined,
@@ -851,13 +870,73 @@ const matchFieldInShape = (
851
870
  export const selectChildren = (childrenTable: Table) =>
852
871
  "select * from " + childrenTable.name + " where " + PARENT_TABLE_ID + " = ?";
853
872
 
854
- export const selectAllFields = (
873
+ export const generateSelectQuery = (
874
+ table: Table,
875
+ selects: { from: string; as: string }[],
876
+ ) => {
877
+ return `SELECT ${selects.map((x) => `${x.from} as ${x.as}`).join(", ")} FROM ${table.name}`;
878
+ };
879
+
880
+ export const selectAllFieldsFromTables = (
881
+ tables: Table[],
882
+ shape: types.Shape | undefined,
883
+ ) => {
884
+ const selectsPerTable: {
885
+ selects: {
886
+ from: string;
887
+ as: string;
888
+ }[];
889
+ joins: Map<string, JoinTable>;
890
+ }[] = [];
891
+
892
+ for (const table of tables) {
893
+ const { selects, join: joinFromSelect } = selectAllFieldsFromTable(
894
+ table,
895
+ shape,
896
+ );
897
+ selectsPerTable.push({ selects, joins: joinFromSelect });
898
+ }
899
+
900
+ // pad with empty selects to make sure all selects have the same length
901
+ /* const maxSelects = Math.max(...selectsPerTable.map(x => x.selects.length)); */
902
+
903
+ let newSelects: {
904
+ from: string;
905
+ as: string;
906
+ }[][] = [];
907
+ for (const [i, selects] of selectsPerTable.entries()) {
908
+ const newSelect = [];
909
+ for (const [j, selectsOther] of selectsPerTable.entries()) {
910
+ if (i !== j) {
911
+ for (const select of selectsOther.selects) {
912
+ newSelect.push({ from: "NULL", as: select.as });
913
+ }
914
+ } else {
915
+ selects.selects.forEach((select) => newSelect.push(select));
916
+ }
917
+ }
918
+ newSelects.push(newSelect);
919
+
920
+ /* let pad = 0;
921
+ while (select.selects.length < maxSelects) {
922
+ select.selects.push({ from: "NULL", as: `'pad#${++pad}'` });
923
+ } */
924
+ }
925
+ // also return table name
926
+ for (const [i, selects] of selectsPerTable.entries()) {
927
+ selects.selects = newSelects[i];
928
+ }
929
+
930
+ return selectsPerTable;
931
+ };
932
+
933
+ export const selectAllFieldsFromTable = (
855
934
  table: Table,
856
935
  shape: types.Shape | undefined,
857
936
  ) => {
858
937
  let stack: { table: Table; shape?: types.Shape }[] = [{ table, shape }];
859
938
  let join: Map<string, JoinTable> = new Map();
860
- const fieldResolvers: string[] = [];
939
+ const fieldResolvers: { from: string; as: string }[] = [];
861
940
  for (const tableAndShape of stack) {
862
941
  if (!tableAndShape.table.inline) {
863
942
  for (const field of tableAndShape.table.fields) {
@@ -866,8 +945,10 @@ export const selectAllFields = (
866
945
  !tableAndShape.shape ||
867
946
  matchFieldInShape(tableAndShape.shape, [], field)
868
947
  ) {
869
- const value = `${tableAndShape.table.name}.${escapeColumnName(field.name)} as '${getTablePrefixedField(tableAndShape.table, field.name)}'`;
870
- fieldResolvers.push(value);
948
+ fieldResolvers.push({
949
+ from: `${tableAndShape.table.name}.${escapeColumnName(field.name)}`,
950
+ as: `'${getTablePrefixedField(tableAndShape.table, field.name)}'`,
951
+ });
871
952
  }
872
953
  }
873
954
  }
@@ -903,7 +984,7 @@ export const selectAllFields = (
903
984
  }
904
985
 
905
986
  return {
906
- query: `SELECT ${fieldResolvers.join(", ")} FROM ${table.name}`,
987
+ selects: fieldResolvers, // `SELECT ${fieldResolvers.join(", ")} FROM ${table.name}`,
907
988
  join,
908
989
  };
909
990
  };
@@ -1120,7 +1201,7 @@ export const convertSumRequestToQuery = (
1120
1201
  tables: Map<string, Table>,
1121
1202
  table: Table,
1122
1203
  ) => {
1123
- return `SELECT SUM(${table.name}.${request.key.join(".")}) as sum FROM ${table.name} ${convertRequestToQuery(request, tables, table).query}`;
1204
+ return `SELECT SUM(${table.name}.${getInlineTableFieldName(request.key)}) as sum FROM ${table.name} ${convertRequestToQuery(request, tables, table).query}`;
1124
1205
  };
1125
1206
 
1126
1207
  export const convertCountRequestToQuery = (
@@ -1138,23 +1219,45 @@ export const convertSearchRequestToQuery = (
1138
1219
  shape: types.Shape | undefined,
1139
1220
  ) => {
1140
1221
  let unionBuilder = "";
1141
- let orderByClause: string | undefined = undefined;
1142
- for (const table of rootTables) {
1143
- const { query: selectQuery, join: joinFromSelect } = selectAllFields(
1144
- table,
1145
- shape,
1146
- );
1147
- const { orderBy, query } = convertRequestToQuery(
1148
- request,
1149
- tables,
1150
- table,
1151
- joinFromSelect,
1152
- );
1153
- unionBuilder += `${unionBuilder.length > 0 ? " UNION ALL " : ""} ${selectQuery} ${query}`;
1154
- orderByClause = orderBy?.length > 0 ? orderBy : orderByClause;
1222
+ let orderByClause: string = "";
1223
+
1224
+ let matchedOnce = false;
1225
+ let lastError: Error | undefined = undefined;
1226
+
1227
+ const selectsPerTable = selectAllFieldsFromTables(rootTables, shape);
1228
+
1229
+ for (const [i, table] of rootTables.entries()) {
1230
+ const { selects, joins: joinFromSelect } = selectsPerTable[i];
1231
+ const selectQuery = generateSelectQuery(table, selects);
1232
+ try {
1233
+ const { orderBy, query } = convertRequestToQuery(
1234
+ request,
1235
+ tables,
1236
+ table,
1237
+ joinFromSelect,
1238
+ );
1239
+ unionBuilder += `${unionBuilder.length > 0 ? " UNION ALL " : ""} ${selectQuery} ${query}`;
1240
+ orderByClause =
1241
+ orderBy?.length > 0
1242
+ ? orderByClause.length > 0
1243
+ ? orderByClause + ", " + orderBy
1244
+ : orderBy
1245
+ : orderByClause;
1246
+ matchedOnce = true;
1247
+ } catch (error) {
1248
+ if (error instanceof MissingFieldError) {
1249
+ lastError = error;
1250
+ continue;
1251
+ }
1252
+ throw error;
1253
+ }
1155
1254
  }
1156
1255
 
1157
- return `${unionBuilder} ${orderByClause ? orderByClause : ""} limit ? offset ?`;
1256
+ if (!matchedOnce) {
1257
+ throw lastError;
1258
+ }
1259
+
1260
+ return `${unionBuilder} ${orderByClause ? "ORDER BY " + orderByClause : ""} limit ? offset ?`;
1158
1261
  };
1159
1262
 
1160
1263
  type SearchQueryParts = { query: string; orderBy: string };
@@ -1197,9 +1300,7 @@ const convertRequestToQuery = <
1197
1300
 
1198
1301
  if (request instanceof types.SearchRequest) {
1199
1302
  if (request.sort.length > 0) {
1200
- if (request.sort.length > 0) {
1201
- orderByBuilder = "ORDER BY ";
1202
- }
1303
+ orderByBuilder = "";
1203
1304
  let once = false;
1204
1305
  for (const sort of request.sort) {
1205
1306
  const { foreignTables, queryKey } = resolveTableToQuery(
@@ -1403,10 +1504,7 @@ const resolveTableToQuery = (
1403
1504
  // this means we need to also check if the key is a field in the current table
1404
1505
 
1405
1506
  if (searchSelf) {
1406
- const inlineName = getInlineTableFieldName(
1407
- path.slice(0, -1),
1408
- path[path.length - 1],
1409
- );
1507
+ const inlineName = getInlineTableFieldName(path);
1410
1508
  let field = table.fields.find((x) => x.name === inlineName);
1411
1509
  if (field) {
1412
1510
  return {
@@ -1428,7 +1526,7 @@ const resolveTableToQuery = (
1428
1526
  const field = schema.fields.find((x) => x.key === key)!;
1429
1527
  if (!field && currentTable.children.length > 0) {
1430
1528
  // second arg is needed because of polymorphic fields we might end up here intentially to check what tables to query
1431
- throw new Error(
1529
+ throw new MissingFieldError(
1432
1530
  `Property with key "${key}" is not found in the schema ${JSON.stringify(schema.fields.map((x) => x.key))}`,
1433
1531
  );
1434
1532
  }
@@ -1482,6 +1580,9 @@ const resolveTableToQuery = (
1482
1580
  let foreignTables: JoinTable[] = currentTables.filter((x) =>
1483
1581
  x.table.fields.find((x) => x.key === path[path.length - 1]),
1484
1582
  );
1583
+ if (foreignTables.length === 0) {
1584
+ throw new MissingFieldError("Failed to find field to join");
1585
+ }
1485
1586
  let tableToQuery: Table | undefined =
1486
1587
  foreignTables[foreignTables.length - 1].table;
1487
1588
  let queryKeyPath = [path[path.length - 1]];
@@ -1494,10 +1595,7 @@ const resolveTableToQuery = (
1494
1595
 
1495
1596
  let queryKey =
1496
1597
  queryKeyPath.length > 0
1497
- ? getInlineTableFieldName(
1498
- queryKeyPath.slice(0, -1),
1499
- queryKeyPath[queryKeyPath.length - 1],
1500
- )
1598
+ ? getInlineTableFieldName(queryKeyPath)
1501
1599
  : FOREIGN_VALUE_PROPERTY;
1502
1600
  return { queryKey, foreignTables };
1503
1601
  };
@@ -1511,10 +1609,7 @@ const convertStateFieldQuery = (
1511
1609
  tableAlias: string | undefined = undefined,
1512
1610
  ): { where: string } => {
1513
1611
  // if field id represented as foreign table, do join and compare
1514
- const inlinedName = getInlineTableFieldName(
1515
- query.key.slice(0, query.key.length - 1),
1516
- query.key[query.key.length - 1],
1517
- );
1612
+ const inlinedName = getInlineTableFieldName(query.key);
1518
1613
  const tableField = table.fields.find(
1519
1614
  (x) => x.name === inlinedName,
1520
1615
  ); /* stringArraysEquals(query.key, [...table.parentPath, x.name]) )*/