Looping through all documents of a specific type

Hello,

I hope somebody can help us on the following:

We sometimes add new properties to existing document types or refactor them, but currently we have to go through all the documents manually and ‘migrate’ these documents afterwards.

What we want to achieve is to automatically migrate these documents and set or copy a specific value automatically.

For instance something like this:

For each documents in brX
  if document.type = 'ProductGrid' then
    if current.ProductGrid.ItemsPerRow is null or empty then
      set ItemsPerRow = 5
    end
    if current.ProductGrid.ItemsPerGrid is null or empty then
      set ItemsPerGrid = 30
    end
  end
end

Is there an easy solution for that?

Thanks for your help,
Thomas

Hello, you probably want to create a Groovy script and run it manually or during startup Updater Scripts - Bloomreach Experience Manager (PaaS/Self-Hosted) - The Fast and Flexible Headless CMS

Use xpath to target specific document types, or target all documents and then have the script determine the changes according to the type.

1 Like

Hi David,

thanks for the input and the link. I am not familiar with groovy, but I hope with the example in the documentation and some help from ChatGPT I created a working script. I am going to test it soon on our test environment.

The goal of the script is to go over all text documents, check if the new headlines compound exists and if not create the headlines compound containing the values of the old properties. We will see if it works that way. :nerd_face: :crossed_fingers:

XPath query:
/jcr:root/content/documents//element(*, myspace:Text)

package org.hippoecm.frontend.plugins.cms.dev.updater

import org.onehippo.repository.update.BaseNodeUpdateVisitor
import javax.jcr.Node

class TextUpdater extends BaseNodeUpdateVisitor {

  private static final PROPERTY_HEADLINES_COMPOUND = 'myspace:headlines'

  boolean doUpdate(Node node) {
    if (!node.hasProperty(PROPERTY_HEADLINES_COMPOUND)) {
        log.debug "Adding headlines compound to node ${node.path}"
		
		// get current values from old fields
		def headlineValue = node.getProperty('myspace:headline')
		def headlineStyleValue = node.getProperty('myspace:headlineStyle')
		def textStyleValue = node.getProperty('myspace:textStyle')
		def alignmentValue = node.getProperty('myspace:alignment')
		
		// create new headlines compound with values of old fields
		def headlinesValue = "myspace:headline: $headlineValue, myspace:headlineStyle: $headlineStyleValue, myspace:textStyle: $textStyleValue, myspace:alignment: $alignmentValue"
        
		// add new headlines compound
		node.setProperty(PROPERTY_HEADLINES_COMPOUND, headlinesValue);
		
        return true;
    }
    return false;
  }

  boolean undoUpdate(Node node) {
    if (node.hasProperty(PROPERTY_HEADLINES_COMPOUND)) {
      node.getProperty(PROPERTY_HEADLINES_COMPOUND).remove();
      return true;
    }
    return false;
  }

}
1 Like

your script looks good, but I think you should also remove the old properties node.getProperty(“old-property-name”).remove()

Hi quang_tran,

We tested the script on our test system yesterday afternoon and to be honest, it didn’t work at first. But we got it working, but we’ll test it some more and then I’ll be happy to post the working script here for others who have a similar problem.

We have not yet removed the old properties because they are currently still in use by the frontend and we can only remove them in a second step. But thanks for the input.

As promised here is our working script. The script checks for all our text documents if a headlines compound exists. If not it creates the headline compound in the document and sets the values from the old obsolete fields as values for the related field in the compound.
For all text documents where the headline compound already exists it overwrites the values of the headline compound fields with the values of the old fields.

After we have run this script we can then remove the old fields from our document structure and only the new headline compound is still there.

Hope this helps somebody with a similar use case.

package org.hippoecm.frontend.plugins.cms.dev.updater

import org.onehippo.repository.update.BaseNodeUpdateVisitor
import javax.jcr.Node

class TextUpdater extends BaseNodeUpdateVisitor {

    private static final PROPERTY_HEADLINES_COMPOUND = 'myshop:headlines'
    private static final PROPERTY_HEADLINES_TYPE = 'myshop:Headlines'

    boolean doUpdate(Node node) {
        if (!node.hasNode(PROPERTY_HEADLINES_COMPOUND)) {
            log.debug "Adding headlines compound to node ${node.path}"

            // get current values from old fields
            def headlineValue = node.getProperty('myshop:headline').getString()

            def headlineStyleValue = "h2"
            if (node.hasProperty('myshop:headlineStyle')) {
                headlineStyleValue = node.getProperty('myshop:headlineStyle').getString().toLowerCase()
                if (headlineStyleValue.isBlank()) {
                    headlineStyleValue = "h2"
                }
            }

            def textStyleValue = "normalCase"
            if(node.hasProperty('myshop:textStyle')) {
                textStyleValue = node.getProperty('myshop:textStyle').getString()
                if (textStyleValue.isBlank()) {
                    textStyleValue = "normalCase"
                }
            }

            def alignmentValue = "center"
            if(node.hasProperty('myshop:alignment')) {
                alignmentValue = node.getProperty('myshop:alignment').getString()
                if (alignmentValue.isBlank()) {
                    alignmentValue = "center"
                }
            }

            // create new headlines compound with values of old fields
            def childNode = node.addNode(PROPERTY_HEADLINES_COMPOUND, PROPERTY_HEADLINES_TYPE)
            childNode.setProperty("myshop:headline", headlineValue)
            childNode.setProperty("myshop:headlineStyle", headlineStyleValue)
            childNode.setProperty("myshop:textStyle", textStyleValue)
            childNode.setProperty("myshop:alignment", alignmentValue)

            return true;
        } else if (node.hasNode(PROPERTY_HEADLINES_COMPOUND)) {
            log.debug "Updating headlines compound of node ${node.path}"

            // first read new Values
            def headlinesNode = node.getNode(PROPERTY_HEADLINES_COMPOUND)

            def newHeadlineValue = headlinesNode.getProperty('myshop:headline').getString()

            def newHeadlineStyleValue = ""
            if (headlinesNode.hasProperty('myshop:headlineStyle')) {
                newHeadlineStyleValue = headlinesNode.getProperty('myshop:headlineStyle').getString().toLowerCase()
            }

            def newTextStyleValue = ""
            if(headlinesNode.hasProperty('myshop:textStyle')) {
                newTextStyleValue = headlinesNode.getProperty('myshop:textStyle').getString()
            }

            def newAlignmentValue = ""
            if(headlinesNode.hasProperty('myshop:alignment')) {
                newAlignmentValue = headlinesNode.getProperty('myshop:alignment').getString()
            }

            // read old values
            def oldHeadlineValue = node.getProperty('myshop:headline').getString()

            def oldHeadlineStyleValue = ""
            if (node.hasProperty('myshop:headlineStyle')) {
                oldHeadlineStyleValue = node.getProperty('myshop:headlineStyle').getString().toLowerCase()
            }

            def oldTextStyleValue = ""
            if(node.hasProperty('myshop:textStyle')) {
                oldTextStyleValue = node.getProperty('myshop:textStyle').getString()
            }

            def oldAlignmentValue = ""
            if(node.hasProperty('myshop:alignment')) {
                oldAlignmentValue = node.getProperty('myshop:alignment').getString()
            }

            // conditionally update the values in the new node
            if(!oldHeadlineValue.isBlank()) {
                headlinesNode.setProperty("myshop:headline", oldHeadlineValue)
            }

            if(!oldHeadlineStyleValue.isBlank()) {
                headlinesNode.setProperty("myshop:headlineStyle", oldHeadlineStyleValue)
            } else if(oldHeadlineStyleValue.isBlank() && newHeadlineStyleValue.isBlank()) {
                headlinesNode.setProperty("myshop:headlineStyle", "h2")
            }

            if(!oldTextStyleValue.isBlank()) {
                headlinesNode.setProperty("myshop:textStyle", oldTextStyleValue)
            } else if(oldTextStyleValue.isBlank() && newTextStyleValue.isBlank()) {
                headlinesNode.setProperty("myshop:textStyle", "normalCase")
            }

            if(!oldAlignmentValue.isBlank()) {
                headlinesNode.setProperty("myshop:alignment", oldAlignmentValue)
            } else if(oldAlignmentValue.isBlank() && newAlignmentValue.isBlank()) {
                headlinesNode.setProperty("myshop:alignment", "center")
            }

            return true;
        }
        return false;
    }

    boolean undoUpdate(Node node) {
        if (node.hasNode(PROPERTY_HEADLINES_COMPOUND)) {
            node.getNode(PROPERTY_HEADLINES_COMPOUND).remove();
            return true;
        }
        return false;
    }
}