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.
@@ -315,12 +315,13 @@ const bulkUpdaterController = ({ strapi: strapi2 }) => ({
315
315
  async getCollectionEntries(ctx) {
316
316
  try {
317
317
  const { uid } = ctx.params;
318
- const { page, pageSize, attribute, filterOperator, filterQuery, search, sortField, sortOrder } = ctx.query;
318
+ const { page, pageSize, attribute, searchAttribute, filterOperator, filterQuery, search, sortField, sortOrder } = ctx.query;
319
319
  const service = strapi2.plugin("bulk-updater").service("bulkUpdater");
320
320
  const data = await service.getCollectionEntries(uid, {
321
321
  page: page ? Number(page) : 1,
322
322
  pageSize: pageSize ? Number(pageSize) : 10,
323
323
  attribute: attribute ? String(attribute) : void 0,
324
+ searchAttribute: searchAttribute ? String(searchAttribute) : void 0,
324
325
  filterOperator: filterOperator ? String(filterOperator) : void 0,
325
326
  filterQuery: filterQuery !== void 0 ? String(filterQuery) : void 0,
326
327
  search: search ? String(search) : void 0,
@@ -679,6 +680,127 @@ const modelsCache = /* @__PURE__ */ new Map();
679
680
  const MAX_QUERY_CACHE_SIZE = 500;
680
681
  const CACHE_TTL_MS = 60 * 1e3;
681
682
  let staticSchemaCache = null;
683
+ function buildDeepPopulate$1(attributes, allComponents, depth2 = 0) {
684
+ if (depth2 > 5) return true;
685
+ const populate2 = {};
686
+ for (const [key, def] of Object.entries(attributes || {})) {
687
+ if (def.type === "component" && def.component) {
688
+ const comp = allComponents[def.component];
689
+ if (comp?.attributes) {
690
+ populate2[key] = {
691
+ populate: buildDeepPopulate$1(comp.attributes, allComponents, depth2 + 1)
692
+ };
693
+ } else {
694
+ populate2[key] = true;
695
+ }
696
+ } else if (def.type === "dynamiczone") {
697
+ populate2[key] = true;
698
+ } else if (def.type === "relation" || def.type === "media") {
699
+ populate2[key] = true;
700
+ }
701
+ }
702
+ return Object.keys(populate2).length > 0 ? populate2 : true;
703
+ }
704
+ function discoverComponentAttributes(strapi2, compUid, prefix, currentDepth, maxDepth, systemFields, allContentTypes, relationsByAttr, allRelationsMeta, attributesMeta) {
705
+ if (currentDepth > maxDepth || !compUid) return [];
706
+ const compSchema = strapi2.components[compUid];
707
+ if (!compSchema?.attributes) return [];
708
+ const discoveredAttrs = [];
709
+ for (const [subKey, subAttr] of Object.entries(compSchema.attributes)) {
710
+ if (systemFields.includes(subKey)) continue;
711
+ if (subAttr.type === "dynamiczone") continue;
712
+ const fullPath = prefix ? `${prefix}.${subKey}` : subKey;
713
+ if (subAttr.type === "component") {
714
+ if (!subAttr.repeatable && subAttr.component) {
715
+ const deeper = discoverComponentAttributes(
716
+ strapi2,
717
+ subAttr.component,
718
+ fullPath,
719
+ currentDepth + 1,
720
+ maxDepth,
721
+ systemFields,
722
+ allContentTypes,
723
+ relationsByAttr,
724
+ allRelationsMeta,
725
+ attributesMeta
726
+ );
727
+ discoveredAttrs.push(...deeper);
728
+ }
729
+ } else if (subAttr.type === "relation") {
730
+ const targetUid = subAttr.target;
731
+ if (targetUid && allContentTypes[targetUid]) {
732
+ const targetInfo = allContentTypes[targetUid]?.info;
733
+ const isMulti = subAttr.relation === "oneToMany" || subAttr.relation === "manyToMany";
734
+ const relMeta = {
735
+ isRelation: true,
736
+ relationType: subAttr.relation,
737
+ targetUid,
738
+ targetDisplayName: targetInfo?.displayName || targetUid,
739
+ isMulti
740
+ };
741
+ relationsByAttr[fullPath] = relMeta;
742
+ allRelationsMeta[fullPath] = relMeta;
743
+ attributesMeta[fullPath] = { depth: currentDepth, type: "relation", component: compUid, isRelation: true };
744
+ discoveredAttrs.push(fullPath);
745
+ }
746
+ } else {
747
+ attributesMeta[fullPath] = { depth: currentDepth, type: subAttr.type, component: compUid };
748
+ discoveredAttrs.push(fullPath);
749
+ }
750
+ }
751
+ return discoveredAttrs;
752
+ }
753
+ function isDeepComponentField(contentType, allComponents, attribute) {
754
+ if (!attribute || !attribute.includes(".")) return false;
755
+ const parts = attribute.split(".");
756
+ let currentSchema = contentType;
757
+ for (let i = 0; i < parts.length - 1; i++) {
758
+ const part = parts[i];
759
+ const attrDef = currentSchema?.attributes?.[part];
760
+ if (!attrDef || attrDef.type !== "component" || !attrDef.component) return false;
761
+ currentSchema = allComponents[attrDef.component];
762
+ if (!currentSchema) return false;
763
+ }
764
+ const leafField = parts[parts.length - 1];
765
+ return Boolean(currentSchema?.attributes?.[leafField]);
766
+ }
767
+ function getLeafAttributeSchema(contentType, allComponents, attribute) {
768
+ if (!attribute) return null;
769
+ if (!attribute.includes(".")) return contentType?.attributes?.[attribute] || null;
770
+ const parts = attribute.split(".");
771
+ let currentSchema = contentType;
772
+ for (let i = 0; i < parts.length - 1; i++) {
773
+ const part = parts[i];
774
+ const attrDef = currentSchema?.attributes?.[part];
775
+ if (!attrDef || attrDef.type !== "component" || !attrDef.component) return null;
776
+ currentSchema = allComponents[attrDef.component];
777
+ if (!currentSchema) return null;
778
+ }
779
+ return currentSchema?.attributes?.[parts[parts.length - 1]] || null;
780
+ }
781
+ function setDeepProperty(obj, pathParts, value) {
782
+ if (pathParts.length === 0) return value;
783
+ const current = { ...obj || {} };
784
+ const [head, ...tail] = pathParts;
785
+ if (tail.length === 0) {
786
+ current[head] = value;
787
+ } else {
788
+ current[head] = setDeepProperty(current[head] || {}, tail, value);
789
+ }
790
+ return current;
791
+ }
792
+ function getDeepProperty(obj, path2) {
793
+ if (!obj || !path2) return void 0;
794
+ if (!path2.includes(".")) return obj[path2];
795
+ return path2.split(".").reduce((acc, part) => acc && typeof acc === "object" ? acc[part] : void 0, obj);
796
+ }
797
+ function buildDeepFilter(pathParts, condition) {
798
+ if (pathParts.length === 0) return condition;
799
+ const [head, ...tail] = pathParts;
800
+ return {
801
+ [head]: buildDeepFilter(tail, condition)
802
+ };
803
+ }
682
804
  function getSafeTimezone(timeZone) {
683
805
  if (!timeZone || typeof timeZone !== "string") {
684
806
  return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
@@ -887,43 +1009,28 @@ async function executeBatchInternal(strapi2, {
887
1009
  const currentEntry = await getDocumentDeduplicated(target.uid, target.documentId);
888
1010
  const entryLabel = currentEntry?.name || currentEntry?.title || currentEntry?.email || `Entry #${target.documentId}`;
889
1011
  const isNested = attribute.includes(".");
890
- let isComponentNested = false;
891
- let parentKey = "";
892
- let childKey = "";
893
- if (isNested) {
894
- const parts = attribute.split(".");
895
- parentKey = parts[0];
896
- childKey = parts[1];
897
- const parentAttrDef = contentType?.attributes?.[parentKey];
898
- if (parentAttrDef?.type === "component" && !parentAttrDef.repeatable && parentAttrDef.component) {
899
- const compSchema = strapi2.components[parentAttrDef.component];
900
- if (compSchema?.attributes?.[childKey]) {
901
- isComponentNested = true;
902
- }
903
- }
904
- }
1012
+ const isComponentNested = isNested && isDeepComponentField(contentType, strapi2.components, attribute);
1013
+ const parts = isNested ? attribute.split(".") : [];
1014
+ const rootKey = isNested ? parts[0] : "";
905
1015
  const attrDef = contentType?.attributes?.[attribute];
906
1016
  const hasAttribute = Boolean(attrDef) || isComponentNested;
907
1017
  if (hasAttribute) {
908
1018
  if (isComponentNested) {
909
1019
  const currentEntryWithPopulate = await strapi2.documents(target.uid).findOne({
910
1020
  documentId: target.documentId,
911
- populate: [parentKey]
1021
+ populate: buildDeepPopulate$1(contentType.attributes, strapi2.components)
912
1022
  });
913
- const existingParent = currentEntryWithPopulate?.[parentKey] || {};
914
- const currentVal2 = existingParent?.[childKey];
1023
+ const existingRoot = currentEntryWithPopulate?.[rootKey] || {};
1024
+ const currentVal2 = getDeepProperty(existingRoot, parts.slice(1).join("."));
915
1025
  let newValue2 = value;
916
1026
  if (regexObj && typeof currentVal2 === "string") {
917
1027
  newValue2 = currentVal2.replace(regexObj, value);
918
1028
  }
919
- const updatedParent = {
920
- ...existingParent,
921
- [childKey]: newValue2
922
- };
1029
+ const updatedRoot = setDeepProperty(existingRoot, parts.slice(1), newValue2);
923
1030
  await strapi2.documents(target.uid).update({
924
1031
  documentId: target.documentId,
925
1032
  data: {
926
- [parentKey]: updatedParent
1033
+ [rootKey]: updatedRoot
927
1034
  }
928
1035
  });
929
1036
  const isDraftAndPublishComp = Boolean(contentType?.options?.draftAndPublish);
@@ -1358,12 +1465,14 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1358
1465
  }
1359
1466
  const metaByUid = {};
1360
1467
  const allRelationsMeta = {};
1468
+ const allAttributesMeta = {};
1361
1469
  for (const uid of targetUids) {
1362
1470
  const contentType = allContentTypes[uid];
1363
1471
  if (!contentType || contentType.kind !== "collectionType" && contentType.kind !== "singleType") continue;
1364
1472
  const isSingleType = contentType.kind === "singleType";
1365
1473
  const rawAttrs = contentType.attributes || {};
1366
1474
  const relationsByAttr = {};
1475
+ const colAttributesMeta = {};
1367
1476
  const nestedComponentAttrs = [];
1368
1477
  const validAttrs = Object.keys(rawAttrs).filter((attrKey) => {
1369
1478
  if (systemFields.includes(attrKey)) return false;
@@ -1371,14 +1480,19 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1371
1480
  if (rawAttrs[attrKey].type === "component") {
1372
1481
  const compDef = rawAttrs[attrKey];
1373
1482
  if (!compDef.repeatable && compDef.component) {
1374
- const componentSchema = strapi2.components[compDef.component];
1375
- if (componentSchema && componentSchema.attributes) {
1376
- for (const [subKey, subAttr] of Object.entries(componentSchema.attributes)) {
1377
- if (systemFields.includes(subKey)) continue;
1378
- if (["component", "dynamiczone", "relation"].includes(subAttr.type)) continue;
1379
- nestedComponentAttrs.push(`${attrKey}.${subKey}`);
1380
- }
1381
- }
1483
+ const compAttrs = discoverComponentAttributes(
1484
+ strapi2,
1485
+ compDef.component,
1486
+ attrKey,
1487
+ 1,
1488
+ 5,
1489
+ systemFields,
1490
+ allContentTypes,
1491
+ relationsByAttr,
1492
+ allRelationsMeta,
1493
+ colAttributesMeta
1494
+ );
1495
+ nestedComponentAttrs.push(...compAttrs);
1382
1496
  }
1383
1497
  return false;
1384
1498
  }
@@ -1397,12 +1511,19 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1397
1511
  };
1398
1512
  relationsByAttr[attrKey] = relMeta;
1399
1513
  allRelationsMeta[attrKey] = relMeta;
1514
+ colAttributesMeta[attrKey] = { depth: 0, type: "relation", isRelation: true };
1400
1515
  return true;
1401
1516
  }
1517
+ colAttributesMeta[attrKey] = { depth: 0, type: rawAttrs[attrKey].type };
1402
1518
  return true;
1403
1519
  });
1404
1520
  const allColAttrs = [...validAttrs, ...nestedComponentAttrs];
1405
- allColAttrs.forEach((attr) => allAttributesSet.add(attr));
1521
+ allColAttrs.forEach((attr) => {
1522
+ allAttributesSet.add(attr);
1523
+ if (colAttributesMeta[attr]) {
1524
+ allAttributesMeta[attr] = colAttributesMeta[attr];
1525
+ }
1526
+ });
1406
1527
  metaByUid[uid] = {
1407
1528
  uid,
1408
1529
  kind: contentType.kind,
@@ -1412,11 +1533,13 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1412
1533
  pluralName: contentType.info?.pluralName || uid,
1413
1534
  displayName: contentType.info?.displayName || uid,
1414
1535
  attributes: allColAttrs,
1536
+ attributesMeta: colAttributesMeta,
1415
1537
  relations: relationsByAttr
1416
1538
  };
1417
1539
  }
1418
1540
  staticSchemaCache = {
1419
1541
  attributes: Array.from(allAttributesSet),
1542
+ attributesMeta: allAttributesMeta,
1420
1543
  targetUids: Object.keys(metaByUid),
1421
1544
  metaByUid,
1422
1545
  relationsMeta: allRelationsMeta
@@ -1447,17 +1570,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1447
1570
  let entries = [];
1448
1571
  let totalCount = 0;
1449
1572
  try {
1450
- const ctAttrs = allContentTypes[uid]?.attributes || {};
1451
- const relKeys = Object.keys(meta.relations || {});
1452
- const compKeys = Object.keys(ctAttrs).filter((k) => ctAttrs[k].type === "component");
1453
- const populateOptionObj = {};
1454
- relKeys.forEach((k) => {
1455
- populateOptionObj[k] = true;
1456
- });
1457
- compKeys.forEach((k) => {
1458
- populateOptionObj[k] = true;
1459
- });
1460
- const populateOption = Object.keys(populateOptionObj).length > 0 ? populateOptionObj : void 0;
1573
+ const populateOption = buildDeepPopulate$1(allContentTypes[uid]?.attributes || {}, strapi2.components);
1461
1574
  if (meta.isSingleType) {
1462
1575
  const singleDoc = await strapi2.documents(uid).findFirst({
1463
1576
  populate: populateOption
@@ -1516,8 +1629,10 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1516
1629
  return {
1517
1630
  success: true,
1518
1631
  attributes: staticSchemaCache.attributes,
1632
+ attributesMeta: staticSchemaCache.attributesMeta,
1519
1633
  collections,
1520
1634
  relationsMeta: staticSchemaCache.relationsMeta,
1635
+ maxDiscoveredDepth: 5,
1521
1636
  executionWindow: {
1522
1637
  ...executionConfig,
1523
1638
  ...windowStatus
@@ -1536,6 +1651,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1536
1651
  page = 1,
1537
1652
  pageSize = 10,
1538
1653
  attribute,
1654
+ searchAttribute,
1539
1655
  filterOperator,
1540
1656
  filterQuery,
1541
1657
  search,
@@ -1550,8 +1666,9 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1550
1666
  const currentPage = Math.max(Number(page) || 1, 1);
1551
1667
  const start = (currentPage - 1) * limit;
1552
1668
  const isSingleType = contentType.kind === "singleType";
1669
+ const targetFilterAttr = searchAttribute || attribute;
1553
1670
  const signature = await getCollectionSignature(strapi2, uid);
1554
- const cacheKey = `${uid}:${limit}:${start}:${attribute || ""}:${filterOperator || ""}:${filterQuery || ""}:${search || ""}:${sortField || ""}:${sortOrder}`;
1671
+ const cacheKey = `${uid}:${limit}:${start}:${attribute || ""}:${searchAttribute || ""}:${filterOperator || ""}:${filterQuery || ""}:${search || ""}:${sortField || ""}:${sortOrder}`;
1555
1672
  const cached = queryCache.get(cacheKey);
1556
1673
  const etag = `W/"entry-${crypto.createHash("md5").update(`${cacheKey}:${signature}`).digest("hex")}"`;
1557
1674
  if (cached && cached.signature === signature && Date.now() - cached.timestamp < CACHE_TTL_MS) {
@@ -1566,19 +1683,15 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1566
1683
  const attrs = contentType.attributes || {};
1567
1684
  let colHasAttribute = false;
1568
1685
  let isNestedAttr = false;
1569
- let parentKey = "";
1570
- let childKey = "";
1571
- if (attribute) {
1572
- if (attribute.includes(".")) {
1573
- [parentKey, childKey] = attribute.split(".");
1574
- if (attrs[parentKey]?.type === "component" && !attrs[parentKey].repeatable && attrs[parentKey].component) {
1575
- const compSchema = strapi2.components[attrs[parentKey].component];
1576
- if (compSchema?.attributes?.[childKey]) {
1577
- colHasAttribute = true;
1578
- isNestedAttr = true;
1579
- }
1686
+ let parts = [];
1687
+ if (targetFilterAttr) {
1688
+ if (targetFilterAttr.includes(".")) {
1689
+ isNestedAttr = isDeepComponentField(contentType, strapi2.components, targetFilterAttr);
1690
+ if (isNestedAttr) {
1691
+ colHasAttribute = true;
1692
+ parts = targetFilterAttr.split(".");
1580
1693
  }
1581
- } else if (attrs[attribute]) {
1694
+ } else if (attrs[targetFilterAttr]) {
1582
1695
  colHasAttribute = true;
1583
1696
  }
1584
1697
  }
@@ -1603,7 +1716,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1603
1716
  }
1604
1717
  let condition = void 0;
1605
1718
  const q = (filterQuery || "").trim();
1606
- const targetAttrSchema = isNestedAttr ? strapi2.components[attrs[parentKey]?.component]?.attributes?.[childKey] : attrs[attribute];
1719
+ const targetAttrSchema = isNestedAttr ? getLeafAttributeSchema(contentType, strapi2.components, targetFilterAttr) : attrs[targetFilterAttr];
1607
1720
  const isStringLike = ["string", "text", "email", "richtext", "uid"].includes(targetAttrSchema?.type);
1608
1721
  if (normOp === "empty") {
1609
1722
  condition = isStringLike ? { $or: [{ $null: true }, { $eq: "" }] } : { $null: true };
@@ -1633,19 +1746,18 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1633
1746
  }
1634
1747
  if (condition !== void 0) {
1635
1748
  if (isNestedAttr) {
1636
- filters2[parentKey] = {
1637
- [childKey]: condition
1638
- };
1749
+ const deepFilter = buildDeepFilter(parts, condition);
1750
+ Object.assign(filters2, deepFilter);
1639
1751
  } else {
1640
1752
  if (normOp === "empty" && isStringLike) {
1641
1753
  filters2.$or = [
1642
- { [attribute]: { $null: true } },
1643
- { [attribute]: { $eq: "" } }
1754
+ { [targetFilterAttr]: { $null: true } },
1755
+ { [targetFilterAttr]: { $eq: "" } }
1644
1756
  ];
1645
1757
  } else if (normOp === "notempty" && isStringLike) {
1646
- filters2[attribute] = { $notNull: true, $ne: "" };
1758
+ filters2[targetFilterAttr] = { $notNull: true, $ne: "" };
1647
1759
  } else {
1648
- filters2[attribute] = condition;
1760
+ filters2[targetFilterAttr] = condition;
1649
1761
  }
1650
1762
  }
1651
1763
  }
@@ -1668,11 +1780,13 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1668
1780
  }
1669
1781
  }
1670
1782
  }
1783
+ const populateOption = buildDeepPopulate$1(contentType.attributes, strapi2.components);
1671
1784
  if (isSingleType) {
1672
1785
  let singleDoc = null;
1673
1786
  try {
1674
1787
  singleDoc = await strapi2.documents(uid).findFirst({
1675
- filters: Object.keys(filters2).length > 0 ? filters2 : void 0
1788
+ filters: Object.keys(filters2).length > 0 ? filters2 : void 0,
1789
+ populate: populateOption
1676
1790
  });
1677
1791
  } catch {
1678
1792
  singleDoc = null;
@@ -1691,11 +1805,11 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1691
1805
  }
1692
1806
  };
1693
1807
  } else {
1694
- const sortClause = {};
1808
+ let sortClause = {};
1695
1809
  if (sortField && sortField.includes(".")) {
1696
- const [parent, child] = sortField.split(".");
1697
- if (contentType.attributes?.[parent]?.type === "component") {
1698
- sortClause[parent] = { [child]: sortOrder };
1810
+ const sortParts = sortField.split(".");
1811
+ if (isDeepComponentField(contentType, strapi2.components, sortField)) {
1812
+ sortClause = buildDeepFilter(sortParts, sortOrder);
1699
1813
  } else {
1700
1814
  sortClause["createdAt"] = "desc";
1701
1815
  }
@@ -1704,10 +1818,6 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1704
1818
  } else {
1705
1819
  sortClause["createdAt"] = "desc";
1706
1820
  }
1707
- const populateAttrs = Object.keys(contentType.attributes || {}).filter(
1708
- (k) => contentType.attributes[k].type === "relation" || contentType.attributes[k].type === "component"
1709
- );
1710
- const populateOption = populateAttrs.length > 0 ? populateAttrs.reduce((acc, k) => ({ ...acc, [k]: true }), {}) : void 0;
1711
1821
  const [entries, totalEntries] = await Promise.all([
1712
1822
  strapi2.documents(uid).findMany({
1713
1823
  start,
@@ -1829,21 +1939,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1829
1939
  const displayName = contentType?.info?.displayName || target.uid;
1830
1940
  try {
1831
1941
  const isNested = attribute.includes(".");
1832
- let isComponentNested = false;
1833
- let parentKey = "";
1834
- let childKey = "";
1835
- if (isNested) {
1836
- const parts = attribute.split(".");
1837
- parentKey = parts[0];
1838
- childKey = parts[1];
1839
- const parentAttrDef = contentType?.attributes?.[parentKey];
1840
- if (parentAttrDef?.type === "component" && !parentAttrDef.repeatable && parentAttrDef.component) {
1841
- const compSchema = strapi2.components[parentAttrDef.component];
1842
- if (compSchema?.attributes?.[childKey]) {
1843
- isComponentNested = true;
1844
- }
1845
- }
1846
- }
1942
+ const isComponentNested = isNested && isDeepComponentField(contentType, strapi2.components, attribute);
1847
1943
  const attrDef = contentType?.attributes?.[attribute];
1848
1944
  const hasAttribute = Boolean(attrDef) || isComponentNested;
1849
1945
  if (!hasAttribute) {
@@ -1868,7 +1964,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1868
1964
  if (isComponentNested) {
1869
1965
  currentEntry = await strapi2.documents(target.uid).findOne({
1870
1966
  documentId: target.documentId,
1871
- populate: [parentKey]
1967
+ populate: buildDeepPopulate$1(contentType.attributes, strapi2.components)
1872
1968
  });
1873
1969
  } else if (attrDef?.type === "relation") {
1874
1970
  currentEntry = await strapi2.documents(target.uid).findOne({
@@ -1882,8 +1978,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
1882
1978
  }
1883
1979
  const entryLabel = currentEntry?.name || currentEntry?.title || currentEntry?.email || `Entry #${target.documentId}`;
1884
1980
  if (isComponentNested) {
1885
- const existingParent = currentEntry?.[parentKey] || {};
1886
- const currentVal2 = existingParent?.[childKey];
1981
+ const currentVal2 = getDeepProperty(currentEntry, attribute);
1887
1982
  let newValue2 = value;
1888
1983
  if (regexObj && typeof currentVal2 === "string") {
1889
1984
  newValue2 = currentVal2.replace(regexObj, value);