diff --git a/vendor/magento/module-visual-merchandiser/Plugin/Catalog/Model/ResourceModel/Product/RankCategoryProductPositions.php b/vendor/magento/module-visual-merchandiser/Plugin/Catalog/Model/ResourceModel/Product/RankCategoryProductPositions.php
new file mode 100644
index 00000000000..20a862a347b
--- /dev/null
+++ b/vendor/magento/module-visual-merchandiser/Plugin/Catalog/Model/ResourceModel/Product/RankCategoryProductPositions.php
@@ -0,0 +1,104 @@
+categoryResourceModel = $categoryResourceModel;
+ $this->positionResolver = $positionResolver;
+ $this->resourceConnection = $resourceConnection;
+ }
+
+ /**
+ * Rank category product positions.
+ *
+ * @param CategoryProductLink $categoryProductLink
+ * @param callable $proceed
+ * @param ProductInterface $product
+ * @param array $insertLinks
+ * @param bool $insert
+ * @return array
+ *
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function aroundUpdateCategoryLinks(
+ CategoryProductLink $categoryProductLink,
+ callable $proceed,
+ ProductInterface $product,
+ array $insertLinks,
+ $insert = false
+ ): array {
+ foreach ($insertLinks as $link) {
+ $this->resourceConnection->getConnection()->insertOnDuplicate(
+ $this->categoryResourceModel->getCategoryProductTable(),
+ $this->getCategoryProductPositions((int) $product->getId(), (int) $link['category_id']),
+ ['position']
+ );
+ }
+
+ return array_column($insertLinks, 'category_id');
+ }
+
+ /**
+ * Retrieve and prepare category product positions array.
+ *
+ * @param int $productId
+ * @param int $categoryId
+ * @return array
+ */
+ private function getCategoryProductPositions(int $productId, int $categoryId): array
+ {
+ $categoryProductPositions = [];
+ $existingCategoryProductPositions = array_flip($this->positionResolver->getPositions((int) $categoryId));
+ array_unshift($existingCategoryProductPositions, $productId);
+
+ foreach (array_flip($existingCategoryProductPositions) as $productId => $productPosition) {
+ $categoryProductPositions[] = [
+ 'category_id' => (int) $categoryId,
+ 'product_id' => $productId,
+ 'position' => $productPosition,
+ ];
+ }
+
+ return $categoryProductPositions;
+ }
+}
diff --git a/vendor/magento/module-visual-merchandiser/etc/adminhtml/di.xml b/vendor/magento/module-visual-merchandiser/etc/adminhtml/di.xml
index 9b581da080b..cdbafab69c4 100644
--- a/vendor/magento/module-visual-merchandiser/etc/adminhtml/di.xml
+++ b/vendor/magento/module-visual-merchandiser/etc/adminhtml/di.xml
@@ -15,4 +15,7 @@
+
+
+