@strapi/database 4.22.0 → 4.23.0

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.mjs CHANGED
@@ -2,9 +2,9 @@ import semver from "semver";
2
2
  import path from "path";
3
3
  import fse from "fs-extra";
4
4
  import createDebug from "debug";
5
- import _, { isNil, castArray, prop, omit, toString, toNumber, isString as isString$1, padCharsEnd, isArray, keys, isPlainObject, isFinite, groupBy, pipe, mapValues, map, isEmpty, maxBy, pick, flow, mergeWith, has, uniqBy, isNull, differenceWith, isEqual, compact, difference, isObject, isInteger, isNumber as isNumber$1, isUndefined, uniqWith } from "lodash/fp";
5
+ import _, { isNil, castArray, prop, omit, toString, toNumber, isString as isString$1, padCharsEnd, isArray, isPlainObject, isFinite, groupBy, pipe, mapValues, map, isEmpty, maxBy, pick, flow, mergeWith, has, uniqBy, isNull, differenceWith, isEqual, compact, difference, isObject, isInteger, isNumber as isNumber$1, isUndefined, uniqWith } from "lodash/fp";
6
6
  import crypto, { randomBytes } from "crypto";
7
- import { isOperatorOfType, mapAsync } from "@strapi/utils";
7
+ import { isOperatorOfType, isOperator, mapAsync } from "@strapi/utils";
8
8
  import * as dateFns from "date-fns";
9
9
  import KnexBuilder from "knex/lib/query/querybuilder";
10
10
  import KnexRaw from "knex/lib/raw";
@@ -1920,12 +1920,11 @@ const createManyToMany = (attributeName, attribute, meta, metadata) => {
1920
1920
  }
1921
1921
  };
1922
1922
  const createMorphToOne = (attributeName, attribute) => {
1923
- const idColumnName = "target_id";
1924
- const typeColumnName = "target_type";
1923
+ const idColumnName = `target_id`;
1924
+ const typeColumnName = `target_type`;
1925
1925
  Object.assign(attribute, {
1926
1926
  owner: true,
1927
- morphColumn: {
1928
- // TODO: add referenced column
1927
+ morphColumn: attribute.morphColumn ?? {
1929
1928
  typeColumn: {
1930
1929
  name: typeColumnName
1931
1930
  },
@@ -2797,11 +2796,54 @@ const createPivotJoin = (ctx, { alias, refAlias, joinTable, targetMeta }) => {
2797
2796
  return subAlias;
2798
2797
  };
2799
2798
  const createJoin = (ctx, { alias, refAlias, attributeName, attribute }) => {
2800
- const { db, qb } = ctx;
2799
+ const { db, qb, uid } = ctx;
2801
2800
  if (attribute.type !== "relation") {
2802
2801
  throw new Error(`Cannot join on non relational field ${attributeName}`);
2803
2802
  }
2804
2803
  const targetMeta = db.metadata.get(attribute.target);
2804
+ if (["morphOne", "morphMany"].includes(attribute.relation)) {
2805
+ const targetAttribute = targetMeta.attributes[attribute.morphBy];
2806
+ const { joinTable: joinTable2, morphColumn } = targetAttribute;
2807
+ if (morphColumn) {
2808
+ const subAlias = refAlias || qb.getAlias();
2809
+ qb.join({
2810
+ alias: subAlias,
2811
+ referencedTable: targetMeta.tableName,
2812
+ referencedColumn: morphColumn.idColumn.name,
2813
+ rootColumn: morphColumn.idColumn.referencedColumn,
2814
+ rootTable: alias,
2815
+ on: {
2816
+ [morphColumn.typeColumn.name]: uid,
2817
+ ...morphColumn.on
2818
+ }
2819
+ });
2820
+ return subAlias;
2821
+ }
2822
+ if (joinTable2) {
2823
+ const joinAlias = qb.getAlias();
2824
+ qb.join({
2825
+ alias: joinAlias,
2826
+ referencedTable: joinTable2.name,
2827
+ referencedColumn: joinTable2.morphColumn.idColumn.name,
2828
+ rootColumn: joinTable2.morphColumn.idColumn.referencedColumn,
2829
+ rootTable: alias,
2830
+ on: {
2831
+ [joinTable2.morphColumn.typeColumn.name]: uid,
2832
+ field: attributeName
2833
+ }
2834
+ });
2835
+ const subAlias = refAlias || qb.getAlias();
2836
+ qb.join({
2837
+ alias: subAlias,
2838
+ referencedTable: targetMeta.tableName,
2839
+ referencedColumn: joinTable2.joinColumn.referencedColumn,
2840
+ rootColumn: joinTable2.joinColumn.name,
2841
+ rootTable: joinAlias
2842
+ });
2843
+ return subAlias;
2844
+ }
2845
+ return alias;
2846
+ }
2805
2847
  const { joinColumn } = attribute;
2806
2848
  if (joinColumn) {
2807
2849
  const subAlias = refAlias || qb.getAlias();
@@ -3451,6 +3493,28 @@ const processNested = (where, ctx) => {
3451
3493
  }
3452
3494
  return processWhere(where, ctx);
3453
3495
  };
3496
+ const processRelationWhere = (where, ctx) => {
3497
+ const { qb, alias } = ctx;
3498
+ const idAlias = qb.aliasColumn("id", alias);
3499
+ if (!isRecord$1(where)) {
3500
+ return { [idAlias]: where };
3501
+ }
3502
+ const keys = Object.keys(where);
3503
+ const operatorKeys = keys.filter((key) => isOperator(key));
3504
+ if (operatorKeys.length > 0 && operatorKeys.length !== keys.length) {
3505
+ throw new Error(`Operator and non-operator keys cannot be mixed in a relation where clause`);
3506
+ }
3507
+ if (operatorKeys.length > 1) {
3508
+ throw new Error(
3509
+ `Only one operator key is allowed in a relation where clause, but found: ${operatorKeys}`
3510
+ );
3511
+ }
3512
+ if (operatorKeys.length === 1) {
3513
+ const operator = operatorKeys[0];
3514
+ return { [idAlias]: { [operator]: processNested(where[operator], ctx) } };
3515
+ }
3516
+ return processWhere(where, ctx);
3517
+ };
3454
3518
  function processWhere(where, ctx) {
3455
3519
  if (!isArray(where) && !isRecord$1(where)) {
3456
3520
  throw new Error("Where must be an array or an object");
@@ -3463,7 +3527,10 @@ function processWhere(where, ctx) {
3463
3527
  const filters = {};
3464
3528
  for (const key of Object.keys(where)) {
3465
3529
  const value = where[key];
3466
- if (isOperatorOfType("group", key) && Array.isArray(value)) {
3530
+ if (isOperatorOfType("group", key)) {
3531
+ if (!Array.isArray(value)) {
3532
+ throw new Error(`Operator ${key} must be an array`);
3533
+ }
3467
3534
  filters[key] = value.map((sub) => processNested(sub, ctx));
3468
3535
  continue;
3469
3536
  }
@@ -3487,15 +3554,12 @@ function processWhere(where, ctx) {
3487
3554
  attributeName: key,
3488
3555
  attribute
3489
3556
  });
3490
- let nestedWhere = processNested(value, {
3557
+ const nestedWhere = processRelationWhere(value, {
3491
3558
  db,
3492
3559
  qb,
3493
3560
  alias: subAlias,
3494
3561
  uid: attribute.target
3495
3562
  });
3496
- if (!isRecord$1(nestedWhere) || isOperatorOfType("where", keys(nestedWhere)[0])) {
3497
- nestedWhere = { [qb.aliasColumn("id", subAlias)]: nestedWhere };
3498
- }
3499
3563
  Object.assign(filters, nestedWhere);
3500
3564
  continue;
3501
3565
  }
@@ -3648,8 +3712,8 @@ const applyWhereToColumn = (qb, column, columnWhere) => {
3648
3712
  }
3649
3713
  return qb.where(column, columnWhere);
3650
3714
  }
3651
- const keys2 = Object.keys(columnWhere);
3652
- keys2.forEach((operator) => {
3715
+ const keys = Object.keys(columnWhere);
3716
+ keys.forEach((operator) => {
3653
3717
  const value = columnWhere[operator];
3654
3718
  applyOperator(qb, column, operator, value);
3655
3719
  });