[CONTEDIT-487] MigrateBlockToIntermediateParentTask doesn't preserve order of nodes Created: 25/Oct/22  Updated: 18/Dec/22  Resolved: 30/Nov/22

Status: Closed
Project: Content Editor
Component/s: None
Affects Version/s: 2.1.2
Fix Version/s: 2.1.3, 2.2.0

Type: Bug Priority: Major
Reporter: Richard Gange Assignee: Lam Nguyen Bao
Resolution: Fixed Votes: 0
Labels: VN-Analysis, VN-Implementation, VN-Testing
Σ Remaining Estimate: 1h Remaining Estimate: 1h
Σ Time Spent: 6d 5h Time Spent: 6d 5h
Σ Original Estimate: Not Specified Original Estimate: Not Specified

Attachments: PNG File 6.2.10.png     PNG File 6.2.24.png     XML File scripts.getPreviousStoryOrder.xml     XML File scripts.repairStoryBlockOrder.xml     File stories.stories-demo.lost-and-found-in-swiss-alps-6.2.10.yaml     File stories.stories-demo.lost-and-found-in-swiss-alps-6.2.24.yaml    
Issue Links:
dependency
depends upon MAGNOLIA-8638 Getting h2 error while upgrading 6.2.... Closed
documentation
to be documented by CONTEDIT-488 DOC: Add version info for the migrati... Closed
relation
Sub-Tasks:
Key
Summary
Type
Status
Assignee
CONTEDIT-490 QA Sub-task Completed Thuy To  
CONTEDIT-491 Port to master Sub-task Completed  
Template:
Acceptance criteria:
Empty
Task DoD:
[X]* Doc/release notes changes? Comment present?
[X]* Downstream builds green?
[X]* Solution information and context easily available?
[X]* Tests
[X]* FixVersion filled and not yet released
[ ]  Architecture Decision Record (ADR)
Bug DoR:
[ ]* Steps to reproduce, expected, and actual results filled
[ ]* Affected version filled
Release notes required:
Yes
Documentation update required:
Yes
Epic Link: AuthorX Support
Sprint: AuthX 21, AuthX 22
Story Points: 5
Team: AuthorX
Work Started:

 Description   

The task MigrateBlockToIntermediateParentTask doesn't preserve the order of nodes. Running the task can actually reorder the nodes to an incorrect state.

Reproduce

Before migration 6.2.10:

After migration 6.2.24:

Expected
The blocks should be in the same order after the migration of data.

Actual
The blocks have been reordered as shown in the screenshots.

Notes
The problem is the task is not using NodeUtil#moveNodeAfter(). You have to iterate over the old list and keep track of the last node which was moved so that the nodes can be moved in the same order.

Workaround
You can fix the order as long as you have an older system to retrieve the order from.
Execute scripts.getPreviousStoryOrder.xml to get a JSON object from the old system which can be fed into scripts.repairStoryBlockOrder.xml to be run on the new system. See comment below for more details.



 Comments   
Comment by Richard Gange [ 26/Oct/22 ]

To fix the issue of an already incorrectly migrated stories workspace.

Assuming you still have access to the old data/system
Step 1: Get the previous order using this groovy script scripts.getPreviousStoryOrder.xml

import info.magnolia.cms.util.QueryUtil;
import javax.jcr.query.Query;
import java.lang.StringBuilder;

// parameters
path = "/stories-demo";

// create statement
storySQL = "select * from [mgnl:composition] WHERE ISDESCENDANTNODE('" + path + "')";

//execute query
storiesList = QueryUtil.search("stories", storySQL, Query.JCR_SQL2, "mgnl:composition");

// print results
story_data = new StringBuilder("");
while (storiesList.hasNext()) {
    story = storiesList.nextNode();
    story_data.append('{"story_id":"' + story.id + '", "');
    blocks = story.getNodes();
    
    story_data.append('blocks": [');
    while (blocks.hasNext()) {
        block = blocks.nextNode();
        story_data.append(block.name + ', ');
    }
    
    int lastComma = story_data.lastIndexOf(",");
    story_data.replace(lastComma, lastComma+1, "]},");
}

int lastComma = story_data.lastIndexOf(",");
story_data.replace(lastComma, lastComma+1, "");
println story_data;

This will produce a JSON object including the story_id and the block order.

{"story_id":"07d3a126-2882-4329-802a-357ae2e1aeeb", "blocks": [28, 1, 0, 21, 22, 38, 2, 3, 23, 36, 4, 5, 30, 6, 7, 29, 8, 9, 24, 27, 10, 26, 11, 12, 32, 13, 14, 35, 15, 33, 16, 20, 34, 25, 17, 18, 19]}, {"story_id":"9ffafe31-c443-49e2-94e8-d5e1355e0037", "blocks": [24, 0, 17, 15, 1, 25, 2, 21, 23, 3, 22, 26, 4, 16, 5, 27, 32, 7, 30, 20, 8, 28, 19, 18, 9, 10, 11, 31, 12, 29, 13, 14]}, {"story_id":"8e9cf0ef-e4c4-4f9c-ae97-aaa6427e62ab", "blocks": [32, 23, 0, 25, 26, 1, 31, 33, 2, 3, 4, 30, 5, 34, 6, 7, 40, 8, 9, 35, 39, 37, 10, 11, 12, 13, 27, 14, 15, 38, 16, 28, 29, 17, 18, 19, 36, 20, 21, 42, 22, 24]} 

 

Step 2: Copy the output from Step 1 into the next groovy script as a parameter scripts.repairStoryBlockOrder.xml

import info.magnolia.cms.util.QueryUtil;
import javax.jcr.query.Query;
import java.lang.StringBuilder;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.HashMap;
import info.magnolia.jcr.util.NodeUtil;

// parameters
path = '/stories-demo';
previous_order = '[  ]'; // take the output from step 1 and copy into the brackets

// create statement
storySQL = 'select * from [mgnl:composition] WHERE ISDESCENDANTNODE("' + path + '")';

//execute query
storiesList = QueryUtil.search('stories', storySQL, Query.JCR_SQL2, 'mgnl:composition');


tokens = new ArrayList<>();
tokenizer = new StringTokenizer(previous_order, '[{":,} ]');
while (tokenizer.hasMoreElements()) {
    tokens.add(tokenizer.nextToken());
}

orderMap = new HashMap();
iterator = tokens.iterator();
lastBlock = null;

while (iterator.hasNext()) {
    token = iterator.next();
    
    switch (token) {
        case 'story_id':
          token = iterator.next();
          storySQL = 'select * from [mgnl:composition] WHERE [jcr:uuid] = "' + token + '"';
          story = (QueryUtil.search('stories', storySQL, Query.JCR_SQL2, 'mgnl:composition')).nextNode();
          blocks = story.getNode('blocks');
          println "Ordering --> " + blocks;
          break;
        case 'blocks':
          lastBlock = null
          break;
        default:
          if (lastBlock != null) {
              node = blocks.getNode(token);
              NodeUtil.orderAfter(node, lastBlock);
              node.getSession().save();
              lastBlock = token;
          }
          else {
              node = blocks.getNode(token);
              NodeUtil.orderFirst(node);
              node.getSession().save();
              lastBlock = token;
          }
          break;
    }
}
println "Done!"
Comment by Richard Gange [ 26/Oct/22 ]

For doc update we need to mention this issue and which version it's fixed on. It should also be mentioned in the release notes so customer and partners don't need to go through the hassle of using the attached repair scripts.

Generated at Mon Feb 12 00:20:54 CET 2024 using Jira 9.4.2#940002-sha1:46d1a51de284217efdcb32434eab47a99af2938b.