strapi-plugin-bulk-updater 1.2.13 → 1.3.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.
@@ -10,7 +10,7 @@ const index = {
10
10
  id: `${PLUGIN_ID}.plugin.name`,
11
11
  defaultMessage: "Bulk Attribute Updater"
12
12
  },
13
- Component: () => Promise.resolve().then(() => require("../_chunks/BulkUpdater-CWJYmuGU.js")),
13
+ Component: () => Promise.resolve().then(() => require("../_chunks/BulkUpdater-CRvwX326.js")),
14
14
  permissions: [],
15
15
  position: 3
16
16
  });
@@ -9,7 +9,7 @@ const index = {
9
9
  id: `${PLUGIN_ID}.plugin.name`,
10
10
  defaultMessage: "Bulk Attribute Updater"
11
11
  },
12
- Component: () => import("../_chunks/BulkUpdater-Cfig2f7d.mjs"),
12
+ Component: () => import("../_chunks/BulkUpdater-DowSmA56.mjs"),
13
13
  permissions: [],
14
14
  position: 3
15
15
  });
@@ -328,12 +328,13 @@ const bulkUpdaterController = ({ strapi: strapi2 }) => ({
328
328
  async getCollectionEntries(ctx) {
329
329
  try {
330
330
  const { uid } = ctx.params;
331
- const { page, pageSize, attribute, filterOperator, filterQuery, search, sortField, sortOrder } = ctx.query;
331
+ const { page, pageSize, attribute, searchAttribute, filterOperator, filterQuery, search, sortField, sortOrder } = ctx.query;
332
332
  const service = strapi2.plugin("bulk-updater").service("bulkUpdater");
333
333
  const data = await service.getCollectionEntries(uid, {
334
334
  page: page ? Number(page) : 1,
335
335
  pageSize: pageSize ? Number(pageSize) : 10,
336
336
  attribute: attribute ? String(attribute) : void 0,
337
+ searchAttribute: searchAttribute ? String(searchAttribute) : void 0,
337
338
  filterOperator: filterOperator ? String(filterOperator) : void 0,
338
339
  filterQuery: filterQuery !== void 0 ? String(filterQuery) : void 0,
339
340
  search: search ? String(search) : void 0,
@@ -692,6 +693,127 @@ const modelsCache = /* @__PURE__ */ new Map();
692
693
  const MAX_QUERY_CACHE_SIZE = 500;
693
694
  const CACHE_TTL_MS = 60 * 1e3;
694
695
  let staticSchemaCache = null;
696
+ function buildDeepPopulate$1(attributes, allComponents, depth2 = 0) {
697
+ if (depth2 > 5) return true;
698
+ const populate2 = {};
699
+ for (const [key, def] of Object.entries(attributes || {})) {
700
+ if (def.type === "component" && def.component) {
701
+ const comp = allComponents[def.component];
702
+ if (comp?.attributes) {
703
+ populate2[key] = {
704
+ populate: buildDeepPopulate$1(comp.attributes, allComponents, depth2 + 1)
705
+ };
706
+ } else {
707
+ populate2[key] = true;
708
+ }
709
+ } else if (def.type === "dynamiczone") {
710
+ populate2[key] = true;
711
+ } else if (def.type === "relation" || def.type === "media") {
712
+ populate2[key] = true;
713
+ }
714
+ }
715
+ return Object.keys(populate2).length > 0 ? populate2 : true;
716
+ }
717
+ function discoverComponentAttributes(strapi2, compUid, prefix, currentDepth, maxDepth, systemFields, allContentTypes, relationsByAttr, allRelationsMeta, attributesMeta) {
718
+ if (currentDepth > maxDepth || !compUid) return [];
719
+ const compSchema = strapi2.components[compUid];
720
+ if (!compSchema?.attributes) return [];
721
+ const discoveredAttrs = [];
722
+ for (const [subKey, subAttr] of Object.entries(compSchema.attributes)) {
723
+ if (systemFields.includes(subKey)) continue;
724
+ if (subAttr.type === "dynamiczone") continue;
725
+ const fullPath = prefix ? `${prefix}.${subKey}` : subKey;
726
+ if (subAttr.type === "component") {
727
+ if (!subAttr.repeatable && subAttr.component) {
728
+ const deeper = discoverComponentAttributes(
729
+ strapi2,
730
+ subAttr.component,
731
+ fullPath,
732
+ currentDepth + 1,
733
+ maxDepth,
734
+ systemFields,
735
+ allContentTypes,
736
+ relationsByAttr,
737
+ allRelationsMeta,
738
+ attributesMeta
739
+ );
740
+ discoveredAttrs.push(...deeper);
741
+ }
742
+ } else if (subAttr.type === "relation") {
743
+ const targetUid = subAttr.target;
744
+ if (targetUid && allContentTypes[targetUid]) {
745
+ const targetInfo = allContentTypes[targetUid]?.info;
746
+ const isMulti = subAttr.relation === "oneToMany" || subAttr.relation === "manyToMany";
747
+ const relMeta = {
748
+ isRelation: true,
749
+ relationType: subAttr.relation,
750
+ targetUid,
751
+ targetDisplayName: targetInfo?.displayName || targetUid,
752
+ isMulti
753
+ };
754
+ relationsByAttr[fullPath] = relMeta;
755
+ allRelationsMeta[fullPath] = relMeta;
756
+ attributesMeta[fullPath] = { depth: currentDepth, type: "relation", component: compUid, isRelation: true };
757
+ discoveredAttrs.push(fullPath);
758
+ }
759
+ } else {
760
+ attributesMeta[fullPath] = { depth: currentDepth, type: subAttr.type, component: compUid };
761
+ discoveredAttrs.push(fullPath);
762
+ }
763
+ }
764
+ return discoveredAttrs;
765
+ }
766
+ function isDeepComponentField(contentType, allComponents, attribute) {
767
+ if (!attribute || !attribute.includes(".")) return false;
768
+ const parts = attribute.split(".");
769
+ let currentSchema = contentType;
770
+ for (let i = 0; i < parts.length - 1; i++) {
771
+ const part = parts[i];
772
+ const attrDef = currentSchema?.attributes?.[part];
773
+ if (!attrDef || attrDef.type !== "component" || !attrDef.component) return false;
774
+ currentSchema = allComponents[attrDef.component];
775
+ if (!currentSchema) return false;
776
+ }
777
+ const leafField = parts[parts.length - 1];
778
+ return Boolean(currentSchema?.attributes?.[leafField]);
779
+ }
780
+ function getLeafAttributeSchema(contentType, allComponents, attribute) {
781
+ if (!attribute) return null;
782
+ if (!attribute.includes(".")) return contentType?.attributes?.[attribute] || null;
783
+ const parts = attribute.split(".");
784
+ let currentSchema = contentType;
785
+ for (let i = 0; i < parts.length - 1; i++) {
786
+ const part = parts[i];
787
+ const attrDef = currentSchema?.attributes?.[part];
788
+ if (!attrDef || attrDef.type !== "component" || !attrDef.component) return null;
789
+ currentSchema = allComponents[attrDef.component];
790
+ if (!currentSchema) return null;
791
+ }
792
+ return currentSchema?.attributes?.[parts[parts.length - 1]] || null;
793
+ }
794
+ function setDeepProperty(obj, pathParts, value) {
795
+ if (pathParts.length === 0) return value;
796
+ const current = { ...obj || {} };
797
+ const [head, ...tail] = pathParts;
798
+ if (tail.length === 0) {
799
+ current[head] = value;
800
+ } else {
801
+ current[head] = setDeepProperty(current[head] || {}, tail, value);
802
+ }
803
+ return current;
804
+ }
805
+ function getDeepProperty(obj, path2) {
806
+ if (!obj || !path2) return void 0;
807
+ if (!path2.includes(".")) return obj[path2];
808
+ return path2.split(".").reduce((acc, part) => acc && typeof acc === "object" ? acc[part] : void 0, obj);
809
+ }
810
+ function buildDeepFilter(pathParts, condition) {
811
+ if (pathParts.length === 0) return condition;
812
+ const [head, ...tail] = pathParts;
813
+ return {
814
+ [head]: buildDeepFilter(tail, condition)
815
+ };
816
+ }
695
817
  function getSafeTimezone(timeZone) {
696
818
  if (!timeZone || typeof timeZone !== "string") {
697
819
  return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
@@ -900,43 +1022,28 @@ async function executeBatchInternal(strapi2, {
900
1022
  const currentEntry = await getDocumentDeduplicated(target.uid, target.documentId);
901
1023
  const entryLabel = currentEntry?.name || currentEntry?.title || currentEntry?.email || `Entry #${target.documentId}`;
902
1024
  const isNested = attribute.includes(".");
903
- let isComponentNested = false;
904
- let parentKey = "";
905
- let childKey = "";
906
- if (isNested) {
907
- const parts = attribute.split(".");
908
- parentKey = parts[0];
909
- childKey = parts[1];
910
- const parentAttrDef = contentType?.attributes?.[parentKey];
911
- if (parentAttrDef?.type === "component" && !parentAttrDef.repeatable && parentAttrDef.component) {
912
- const compSchema = strapi2.components[parentAttrDef.component];
913
- if (compSchema?.attributes?.[childKey]) {
914
- isComponentNested = true;
915
- }
916
- }
917
- }
1025
+ const isComponentNested = isNested && isDeepComponentField(contentType, strapi2.components, attribute);
1026
+ const parts = isNested ? attribute.split(".") : [];
1027
+ const rootKey = isNested ? parts[0] : "";
918
1028
  const attrDef = contentType?.attributes?.[attribute];
919
1029
  const hasAttribute = Boolean(attrDef) || isComponentNested;
920
1030
  if (hasAttribute) {
921
1031
  if (isComponentNested) {
922
1032
  const currentEntryWithPopulate = await strapi2.documents(target.uid).findOne({
923
1033
  documentId: target.documentId,
924
- populate: [parentKey]
1034
+ populate: buildDeepPopulate$1(contentType.attributes, strapi2.components)
925
1035
  });
926
- const existingParent = currentEntryWithPopulate?.[parentKey] || {};
927
- const currentVal2 = existingParent?.[childKey];
1036
+ const existingRoot = currentEntryWithPopulate?.[rootKey] || {};
1037
+ const currentVal2 = getDeepProperty(existingRoot, parts.slice(1).join("."));
928
1038
  let newValue2 = value;
929
1039
  if (regexObj && typeof currentVal2 === "string") {
930
1040
  newValue2 = currentVal2.replace(regexObj, value);
931
1041
  }
932
- const updatedParent = {
933
- ...existingParent,
934
- [childKey]: newValue2
935
- };
1042
+ const updatedRoot = setDeepProperty(existingRoot, parts.slice(1), newValue2);
936
1043
  await strapi2.documents(target.uid).update({
937
1044
  documentId: target.documentId,
938
1045
  data: {
939
- [parentKey]: updatedParent
1046
+ [rootKey]: updatedRoot
940
1047
  }
941
1048
  });
942
1049
  const isDraftAndPublishComp = Boolean(contentType?.options?.draftAndPublish);
@@ -1371,12 +1478,14 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1371
1478
  }
1372
1479
  const metaByUid = {};
1373
1480
  const allRelationsMeta = {};
1481
+ const allAttributesMeta = {};
1374
1482
  for (const uid of targetUids) {
1375
1483
  const contentType = allContentTypes[uid];
1376
1484
  if (!contentType || contentType.kind !== "collectionType" && contentType.kind !== "singleType") continue;
1377
1485
  const isSingleType = contentType.kind === "singleType";
1378
1486
  const rawAttrs = contentType.attributes || {};
1379
1487
  const relationsByAttr = {};
1488
+ const colAttributesMeta = {};
1380
1489
  const nestedComponentAttrs = [];
1381
1490
  const validAttrs = Object.keys(rawAttrs).filter((attrKey) => {
1382
1491
  if (systemFields.includes(attrKey)) return false;
@@ -1384,14 +1493,19 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1384
1493
  if (rawAttrs[attrKey].type === "component") {
1385
1494
  const compDef = rawAttrs[attrKey];
1386
1495
  if (!compDef.repeatable && compDef.component) {
1387
- const componentSchema = strapi2.components[compDef.component];
1388
- if (componentSchema && componentSchema.attributes) {
1389
- for (const [subKey, subAttr] of Object.entries(componentSchema.attributes)) {
1390
- if (systemFields.includes(subKey)) continue;
1391
- if (["component", "dynamiczone", "relation"].includes(subAttr.type)) continue;
1392
- nestedComponentAttrs.push(`${attrKey}.${subKey}`);
1393
- }
1394
- }
1496
+ const compAttrs = discoverComponentAttributes(
1497
+ strapi2,
1498
+ compDef.component,
1499
+ attrKey,
1500
+ 1,
1501
+ 5,
1502
+ systemFields,
1503
+ allContentTypes,
1504
+ relationsByAttr,
1505
+ allRelationsMeta,
1506
+ colAttributesMeta
1507
+ );
1508
+ nestedComponentAttrs.push(...compAttrs);
1395
1509
  }
1396
1510
  return false;
1397
1511
  }
@@ -1410,12 +1524,19 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1410
1524
  };
1411
1525
  relationsByAttr[attrKey] = relMeta;
1412
1526
  allRelationsMeta[attrKey] = relMeta;
1527
+ colAttributesMeta[attrKey] = { depth: 0, type: "relation", isRelation: true };
1413
1528
  return true;
1414
1529
  }
1530
+ colAttributesMeta[attrKey] = { depth: 0, type: rawAttrs[attrKey].type };
1415
1531
  return true;
1416
1532
  });
1417
1533
  const allColAttrs = [...validAttrs, ...nestedComponentAttrs];
1418
- allColAttrs.forEach((attr) => allAttributesSet.add(attr));
1534
+ allColAttrs.forEach((attr) => {
1535
+ allAttributesSet.add(attr);
1536
+ if (colAttributesMeta[attr]) {
1537
+ allAttributesMeta[attr] = colAttributesMeta[attr];
1538
+ }
1539
+ });
1419
1540
  metaByUid[uid] = {
1420
1541
  uid,
1421
1542
  kind: contentType.kind,
@@ -1425,11 +1546,13 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1425
1546
  pluralName: contentType.info?.pluralName || uid,
1426
1547
  displayName: contentType.info?.displayName || uid,
1427
1548
  attributes: allColAttrs,
1549
+ attributesMeta: colAttributesMeta,
1428
1550
  relations: relationsByAttr
1429
1551
  };
1430
1552
  }
1431
1553
  staticSchemaCache = {
1432
1554
  attributes: Array.from(allAttributesSet),
1555
+ attributesMeta: allAttributesMeta,
1433
1556
  targetUids: Object.keys(metaByUid),
1434
1557
  metaByUid,
1435
1558
  relationsMeta: allRelationsMeta
@@ -1460,17 +1583,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1460
1583
  let entries = [];
1461
1584
  let totalCount = 0;
1462
1585
  try {
1463
- const ctAttrs = allContentTypes[uid]?.attributes || {};
1464
- const relKeys = Object.keys(meta.relations || {});
1465
- const compKeys = Object.keys(ctAttrs).filter((k) => ctAttrs[k].type === "component");
1466
- const populateOptionObj = {};
1467
- relKeys.forEach((k) => {
1468
- populateOptionObj[k] = true;
1469
- });
1470
- compKeys.forEach((k) => {
1471
- populateOptionObj[k] = true;
1472
- });
1473
- const populateOption = Object.keys(populateOptionObj).length > 0 ? populateOptionObj : void 0;
1586
+ const populateOption = buildDeepPopulate$1(allContentTypes[uid]?.attributes || {}, strapi2.components);
1474
1587
  if (meta.isSingleType) {
1475
1588
  const singleDoc = await strapi2.documents(uid).findFirst({
1476
1589
  populate: populateOption
@@ -1529,8 +1642,10 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1529
1642
  return {
1530
1643
  success: true,
1531
1644
  attributes: staticSchemaCache.attributes,
1645
+ attributesMeta: staticSchemaCache.attributesMeta,
1532
1646
  collections,
1533
1647
  relationsMeta: staticSchemaCache.relationsMeta,
1648
+ maxDiscoveredDepth: 5,
1534
1649
  executionWindow: {
1535
1650
  ...executionConfig,
1536
1651
  ...windowStatus
@@ -1549,6 +1664,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1549
1664
  page = 1,
1550
1665
  pageSize = 10,
1551
1666
  attribute,
1667
+ searchAttribute,
1552
1668
  filterOperator,
1553
1669
  filterQuery,
1554
1670
  search,
@@ -1563,8 +1679,9 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1563
1679
  const currentPage = Math.max(Number(page) || 1, 1);
1564
1680
  const start = (currentPage - 1) * limit;
1565
1681
  const isSingleType = contentType.kind === "singleType";
1682
+ const targetFilterAttr = searchAttribute || attribute;
1566
1683
  const signature = await getCollectionSignature(strapi2, uid);
1567
- const cacheKey = `${uid}:${limit}:${start}:${attribute || ""}:${filterOperator || ""}:${filterQuery || ""}:${search || ""}:${sortField || ""}:${sortOrder}`;
1684
+ const cacheKey = `${uid}:${limit}:${start}:${attribute || ""}:${searchAttribute || ""}:${filterOperator || ""}:${filterQuery || ""}:${search || ""}:${sortField || ""}:${sortOrder}`;
1568
1685
  const cached = queryCache.get(cacheKey);
1569
1686
  const etag = `W/"entry-${crypto__default.default.createHash("md5").update(`${cacheKey}:${signature}`).digest("hex")}"`;
1570
1687
  if (cached && cached.signature === signature && Date.now() - cached.timestamp < CACHE_TTL_MS) {
@@ -1579,19 +1696,15 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1579
1696
  const attrs = contentType.attributes || {};
1580
1697
  let colHasAttribute = false;
1581
1698
  let isNestedAttr = false;
1582
- let parentKey = "";
1583
- let childKey = "";
1584
- if (attribute) {
1585
- if (attribute.includes(".")) {
1586
- [parentKey, childKey] = attribute.split(".");
1587
- if (attrs[parentKey]?.type === "component" && !attrs[parentKey].repeatable && attrs[parentKey].component) {
1588
- const compSchema = strapi2.components[attrs[parentKey].component];
1589
- if (compSchema?.attributes?.[childKey]) {
1590
- colHasAttribute = true;
1591
- isNestedAttr = true;
1592
- }
1699
+ let parts = [];
1700
+ if (targetFilterAttr) {
1701
+ if (targetFilterAttr.includes(".")) {
1702
+ isNestedAttr = isDeepComponentField(contentType, strapi2.components, targetFilterAttr);
1703
+ if (isNestedAttr) {
1704
+ colHasAttribute = true;
1705
+ parts = targetFilterAttr.split(".");
1593
1706
  }
1594
- } else if (attrs[attribute]) {
1707
+ } else if (attrs[targetFilterAttr]) {
1595
1708
  colHasAttribute = true;
1596
1709
  }
1597
1710
  }
@@ -1616,7 +1729,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1616
1729
  }
1617
1730
  let condition = void 0;
1618
1731
  const q = (filterQuery || "").trim();
1619
- const targetAttrSchema = isNestedAttr ? strapi2.components[attrs[parentKey]?.component]?.attributes?.[childKey] : attrs[attribute];
1732
+ const targetAttrSchema = isNestedAttr ? getLeafAttributeSchema(contentType, strapi2.components, targetFilterAttr) : attrs[targetFilterAttr];
1620
1733
  const isStringLike = ["string", "text", "email", "richtext", "uid"].includes(targetAttrSchema?.type);
1621
1734
  if (normOp === "empty") {
1622
1735
  condition = isStringLike ? { $or: [{ $null: true }, { $eq: "" }] } : { $null: true };
@@ -1646,19 +1759,18 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1646
1759
  }
1647
1760
  if (condition !== void 0) {
1648
1761
  if (isNestedAttr) {
1649
- filters2[parentKey] = {
1650
- [childKey]: condition
1651
- };
1762
+ const deepFilter = buildDeepFilter(parts, condition);
1763
+ Object.assign(filters2, deepFilter);
1652
1764
  } else {
1653
1765
  if (normOp === "empty" && isStringLike) {
1654
1766
  filters2.$or = [
1655
- { [attribute]: { $null: true } },
1656
- { [attribute]: { $eq: "" } }
1767
+ { [targetFilterAttr]: { $null: true } },
1768
+ { [targetFilterAttr]: { $eq: "" } }
1657
1769
  ];
1658
1770
  } else if (normOp === "notempty" && isStringLike) {
1659
- filters2[attribute] = { $notNull: true, $ne: "" };
1771
+ filters2[targetFilterAttr] = { $notNull: true, $ne: "" };
1660
1772
  } else {
1661
- filters2[attribute] = condition;
1773
+ filters2[targetFilterAttr] = condition;
1662
1774
  }
1663
1775
  }
1664
1776
  }
@@ -1681,11 +1793,13 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1681
1793
  }
1682
1794
  }
1683
1795
  }
1796
+ const populateOption = buildDeepPopulate$1(contentType.attributes, strapi2.components);
1684
1797
  if (isSingleType) {
1685
1798
  let singleDoc = null;
1686
1799
  try {
1687
1800
  singleDoc = await strapi2.documents(uid).findFirst({
1688
- filters: Object.keys(filters2).length > 0 ? filters2 : void 0
1801
+ filters: Object.keys(filters2).length > 0 ? filters2 : void 0,
1802
+ populate: populateOption
1689
1803
  });
1690
1804
  } catch {
1691
1805
  singleDoc = null;
@@ -1704,11 +1818,11 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1704
1818
  }
1705
1819
  };
1706
1820
  } else {
1707
- const sortClause = {};
1821
+ let sortClause = {};
1708
1822
  if (sortField && sortField.includes(".")) {
1709
- const [parent, child] = sortField.split(".");
1710
- if (contentType.attributes?.[parent]?.type === "component") {
1711
- sortClause[parent] = { [child]: sortOrder };
1823
+ const sortParts = sortField.split(".");
1824
+ if (isDeepComponentField(contentType, strapi2.components, sortField)) {
1825
+ sortClause = buildDeepFilter(sortParts, sortOrder);
1712
1826
  } else {
1713
1827
  sortClause["createdAt"] = "desc";
1714
1828
  }
@@ -1717,10 +1831,6 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1717
1831
  } else {
1718
1832
  sortClause["createdAt"] = "desc";
1719
1833
  }
1720
- const populateAttrs = Object.keys(contentType.attributes || {}).filter(
1721
- (k) => contentType.attributes[k].type === "relation" || contentType.attributes[k].type === "component"
1722
- );
1723
- const populateOption = populateAttrs.length > 0 ? populateAttrs.reduce((acc, k) => ({ ...acc, [k]: true }), {}) : void 0;
1724
1834
  const [entries, totalEntries] = await Promise.all([
1725
1835
  strapi2.documents(uid).findMany({
1726
1836
  start,
@@ -1842,21 +1952,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1842
1952
  const displayName = contentType?.info?.displayName || target.uid;
1843
1953
  try {
1844
1954
  const isNested = attribute.includes(".");
1845
- let isComponentNested = false;
1846
- let parentKey = "";
1847
- let childKey = "";
1848
- if (isNested) {
1849
- const parts = attribute.split(".");
1850
- parentKey = parts[0];
1851
- childKey = parts[1];
1852
- const parentAttrDef = contentType?.attributes?.[parentKey];
1853
- if (parentAttrDef?.type === "component" && !parentAttrDef.repeatable && parentAttrDef.component) {
1854
- const compSchema = strapi2.components[parentAttrDef.component];
1855
- if (compSchema?.attributes?.[childKey]) {
1856
- isComponentNested = true;
1857
- }
1858
- }
1859
- }
1955
+ const isComponentNested = isNested && isDeepComponentField(contentType, strapi2.components, attribute);
1860
1956
  const attrDef = contentType?.attributes?.[attribute];
1861
1957
  const hasAttribute = Boolean(attrDef) || isComponentNested;
1862
1958
  if (!hasAttribute) {
@@ -1881,7 +1977,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1881
1977
  if (isComponentNested) {
1882
1978
  currentEntry = await strapi2.documents(target.uid).findOne({
1883
1979
  documentId: target.documentId,
1884
- populate: [parentKey]
1980
+ populate: buildDeepPopulate$1(contentType.attributes, strapi2.components)
1885
1981
  });
1886
1982
  } else if (attrDef?.type === "relation") {
1887
1983
  currentEntry = await strapi2.documents(target.uid).findOne({
@@ -1895,8 +1991,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1895
1991
  }
1896
1992
  const entryLabel = currentEntry?.name || currentEntry?.title || currentEntry?.email || `Entry #${target.documentId}`;
1897
1993
  if (isComponentNested) {
1898
- const existingParent = currentEntry?.[parentKey] || {};
1899
- const currentVal2 = existingParent?.[childKey];
1994
+ const currentVal2 = getDeepProperty(currentEntry, attribute);
1900
1995
  let newValue2 = value;
1901
1996
  if (regexObj && typeof currentVal2 === "string") {
1902
1997
  newValue2 = currentVal2.replace(regexObj, value);