inibase 1.2.9 → 1.2.10

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/index.d.ts CHANGED
@@ -122,6 +122,7 @@ export default class Inibase {
122
122
  private processObjectField;
123
123
  private isTableField;
124
124
  private processTableField;
125
+ private _setNestedKey;
125
126
  private applyCriteria;
126
127
  private _filterSchemaByColumns;
127
128
  /**
package/dist/index.js CHANGED
@@ -927,47 +927,49 @@ export default class Inibase {
927
927
  }
928
928
  }
929
929
  }
930
+ _setNestedKey(obj, path, value) {
931
+ const keys = path.split(".");
932
+ const lastKey = keys.pop();
933
+ const target = keys.reduce((acc, key) => {
934
+ if (typeof acc[key] !== "object" || acc[key] === null) {
935
+ acc[key] = {};
936
+ }
937
+ return acc[key];
938
+ }, obj);
939
+ target[lastKey] = value;
940
+ }
930
941
  async applyCriteria(tableName, options, criteria, allTrue, searchIn) {
931
942
  const tablePath = join(this.databasePath, tableName);
932
- /** result container – we mutate rows in-place instead of deep-merging */
933
- const RETURN = {};
934
- /* ---------- fast exit ---------- */
943
+ let RETURN = {};
935
944
  if (!criteria || Object.keys(criteria).length === 0)
936
945
  return null;
937
- /* ---------- split top-level logical groups ---------- */
938
946
  const criteriaAND = criteria.and;
939
- const criteriaOR = criteria.or;
940
947
  if (criteriaAND)
941
948
  delete criteria.and;
949
+ const criteriaOR = criteria.or;
942
950
  if (criteriaOR)
943
951
  delete criteria.or;
944
- /** cache table schema once – Utils.getField() is cheap but we call it a lot */
945
952
  const schema = globalConfig[this.databasePath].tables.get(tableName).schema;
946
- /* ---------- MAIN LOOP (top-level criteria only) ---------- */
947
953
  if (Object.keys(criteria).length) {
948
954
  if (allTrue === undefined)
949
955
  allTrue = true;
950
- for (const [key, rawValue] of Object.entries(criteria)) {
951
- /* bail early if no candidates left for an “AND” chain */
952
- if (allTrue && searchIn && searchIn.size === 0)
953
- return null;
956
+ for await (let [key, value] of Object.entries(criteria)) {
954
957
  const field = Utils.getField(key, schema);
955
958
  if (!field)
956
959
  continue;
957
- let value = rawValue;
958
- /* ----- resolve relational sub-queries ----- */
959
960
  if (field.table && Utils.isObject(value)) {
960
- const rel = await this.get(field.table, value, {
961
+ const items = await this.get(field.table, value, {
961
962
  columns: "id",
963
+ perPage: Number.POSITIVE_INFINITY,
962
964
  });
963
- value = rel?.length ? `[]${rel.map(({ id }) => id)}` : undefined; // ⟵ no match → abort whole AND-chain
964
- if (value === undefined && allTrue)
965
+ if (items?.length)
966
+ value = `[]${items.map(({ id }) => id)}`;
967
+ else if (allTrue)
965
968
  return null;
966
969
  }
967
- /* ----- determine operator / compare value ----- */
968
- let searchOperator;
969
- let searchComparedAtValue;
970
- let searchLogicalOperator;
970
+ let searchOperator = undefined;
971
+ let searchComparedAtValue = undefined;
972
+ let searchLogicalOperator = undefined;
971
973
  if (Utils.isObject(value)) {
972
974
  /* nested object with .and / .or inside */
973
975
  const nestedAnd = value.and;
@@ -985,6 +987,7 @@ export default class Inibase {
985
987
  searchComparedAtValue = crit.map((c) => c[1]);
986
988
  searchLogicalOperator = logic;
987
989
  }
990
+ delete value[logic];
988
991
  }
989
992
  }
990
993
  else if (Array.isArray(value)) {
@@ -1006,45 +1009,51 @@ export default class Inibase {
1006
1009
  searchOperator = "=";
1007
1010
  searchComparedAtValue = value;
1008
1011
  }
1009
- /* ---------- disk search ---------- */
1010
- const [hitRows, totalLines, lineSet] = await File.search(join(tablePath, `${key}${this.getFileExtension(tableName)}`), searchOperator ?? "=", searchComparedAtValue ?? null, searchLogicalOperator, searchIn, {
1012
+ const [searchResult, totalLines, linesNumbers] = await File.search(join(tablePath, `${key}${this.getFileExtension(tableName)}`), searchOperator ?? "=", searchComparedAtValue, searchLogicalOperator, searchIn, {
1011
1013
  ...field,
1012
1014
  databasePath: this.databasePath,
1013
1015
  table: field.table ?? tableName,
1014
1016
  }, options.perPage, (options.page - 1) * options.perPage +
1015
1017
  (options.page > 1 ? 1 : 0), true);
1016
- if (!hitRows) {
1018
+ if (!searchResult) {
1017
1019
  if (allTrue)
1018
- return null; // AND-chain broken
1019
- continue; // OR-chain: just skip
1020
- }
1021
- /* ---------- map rows into RETURN without deep-merge ---------- */
1022
- for (const [lnStr, val] of Object.entries(hitRows)) {
1023
- const ln = +lnStr;
1024
- const row = (RETURN[ln] ??= {});
1025
- row[key] = val;
1020
+ return null;
1021
+ continue;
1026
1022
  }
1023
+ const formatedSearchResult = Object.fromEntries(Object.entries(searchResult).map(([id, value]) => {
1024
+ const nestedObj = {};
1025
+ this._setNestedKey(nestedObj, key, value);
1026
+ return [id, nestedObj];
1027
+ }));
1028
+ RETURN = allTrue
1029
+ ? formatedSearchResult
1030
+ : Utils.deepMerge(RETURN, formatedSearchResult);
1027
1031
  this.totalItems.set(`${tableName}-${key}`, totalLines);
1028
- /* shrink search domain for next AND condition */
1029
- if (lineSet?.size && allTrue)
1030
- searchIn = lineSet;
1032
+ if (linesNumbers?.size && allTrue)
1033
+ searchIn = linesNumbers;
1031
1034
  }
1032
1035
  }
1033
- /* ---------- process nested .and / .or recursively (unchanged) ---------- */
1034
1036
  if (criteriaAND && Utils.isObject(criteriaAND)) {
1035
- const res = await this.applyCriteria(tableName, options, criteriaAND, true, searchIn);
1036
- if (!res)
1037
+ const searchResult = await this.applyCriteria(tableName, options, criteriaAND, true, searchIn);
1038
+ if (searchResult)
1039
+ RETURN = Utils.deepMerge(RETURN, Object.fromEntries(Object.entries(searchResult).filter(([_k, v], _i) => Object.keys(v).filter((key) => Object.keys(criteriaAND).includes(key)).length)));
1040
+ else
1037
1041
  return null;
1038
- for (const [lnStr, data] of Object.entries(res))
1039
- (RETURN[+lnStr] ??= {}), Object.assign(RETURN[+lnStr], data);
1040
1042
  }
1041
1043
  if (criteriaOR && Utils.isObject(criteriaOR)) {
1042
- const res = await this.applyCriteria(tableName, options, criteriaOR, false, undefined);
1043
- if (res)
1044
- for (const [lnStr, data] of Object.entries(res))
1045
- (RETURN[+lnStr] ??= {}), Object.assign(RETURN[+lnStr], data);
1044
+ const searchResult = await this.applyCriteria(tableName, options, criteriaOR, false, searchIn);
1045
+ if (searchResult) {
1046
+ RETURN = Utils.deepMerge(RETURN, searchResult);
1047
+ if (!Object.keys(RETURN).length)
1048
+ RETURN = {};
1049
+ RETURN = Object.fromEntries(Object.entries(RETURN).filter(([_index, item]) => Object.keys(item).filter((key) => Object.keys(criteriaOR).includes(key) ||
1050
+ Object.keys(criteriaOR).some((criteriaKey) => criteriaKey.startsWith(`${key}.`))).length));
1051
+ if (!Object.keys(RETURN).length)
1052
+ RETURN = {};
1053
+ }
1054
+ else
1055
+ RETURN = {};
1046
1056
  }
1047
- /* ---------- final answer ---------- */
1048
1057
  return Object.keys(RETURN).length ? RETURN : null;
1049
1058
  }
1050
1059
  _filterSchemaByColumns(schema, columns) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inibase",
3
- "version": "1.2.9",
3
+ "version": "1.2.10",
4
4
  "type": "module",
5
5
  "author": {
6
6
  "name": "Karim Amahtil",