I am currently working on a project where I need to create a website using Adobe Experience Manager (AEM). The website is designed to represent multiple malls, with each mall housing numerous stores. Additionally, these stores will be providing various offers.
The client’s requirement is to store all the data related to the malls, stores, and offers in the AEM Java Content Repository (JCR). They have specifically requested not to use SQL, MySQL, or any other external data storage systems.
Given this scenario, I am seeking advice on the best way to structure this data in the JCR. Is it appropriate and efficient to store such hierarchical data in the JCR? If so, could you provide some guidance or best practices on how to achieve this?
Is the following way or organizing this data in JCR is proper way ?
Any insights or suggestions would be greatly appreciated.
Solved! Go to Solution.
Topics help categorize Community content and increase your ability to discover relevant content.
Views
Replies
Total Likes
Hi @touseefk2181136 ,
JCR is designed to handle hierarchical data, and your structure aligns well with this model.
Here are some best practices you might consider:
Use meaningful node names: Node names should be descriptive and meaningful. In your case, using the mall name, store name, and offer name as node names is a good approach.
Leverage properties: Use properties to store data about nodes. For example, you can store the name, location, and type of a store as properties of the store node.
Avoid deep hierarchies: While JCR can handle deep hierarchies, it’s generally a good practice to avoid overly deep structures as they can impact performance and complicate queries.
Use node types: Node types allow you to define the structure and constraints of a node. You can define a node type for malls, stores, and offers, specifying what child nodes and properties they can have.
Indexing: Make sure to set up proper indexing. This will greatly improve the performance of your queries.
Your structure would look something like this in terms of JCR nodes and properties:
/content
/malls
/mall1
- jcr:title = "Mall 1"
- location = "Location 1"
/stores
/store1
- jcr:title = "Store 1"
- type = "Electronic"
/offers
/offer1
- jcr:title = "10% off"
In this structure, jcr:title is a commonly used property to store the display name of a node. Each mall, store, and offer is a node with properties. The stores and offers are intermediate nodes to group stores and offers respectively.
Thanks,
Madhur
Hi @touseefk2181136 ,
JCR is designed to handle hierarchical data, and your structure aligns well with this model.
Here are some best practices you might consider:
Use meaningful node names: Node names should be descriptive and meaningful. In your case, using the mall name, store name, and offer name as node names is a good approach.
Leverage properties: Use properties to store data about nodes. For example, you can store the name, location, and type of a store as properties of the store node.
Avoid deep hierarchies: While JCR can handle deep hierarchies, it’s generally a good practice to avoid overly deep structures as they can impact performance and complicate queries.
Use node types: Node types allow you to define the structure and constraints of a node. You can define a node type for malls, stores, and offers, specifying what child nodes and properties they can have.
Indexing: Make sure to set up proper indexing. This will greatly improve the performance of your queries.
Your structure would look something like this in terms of JCR nodes and properties:
/content
/malls
/mall1
- jcr:title = "Mall 1"
- location = "Location 1"
/stores
/store1
- jcr:title = "Store 1"
- type = "Electronic"
/offers
/offer1
- jcr:title = "10% off"
In this structure, jcr:title is a commonly used property to store the display name of a node. Each mall, store, and offer is a node with properties. The stores and offers are intermediate nodes to group stores and offers respectively.
Thanks,
Madhur
Hi @Madhur-Madan thanks for you reply. I have few question, can please take a look
Hi @touseefk2181136 ,
In the structure I recommended, here’s how cq:Page and nt:unstructured could be used:
cq:Page: This node type is used for the mall1 node. This is because each mall could represent a page in your website.
/content
/malls
/mall1 (cq:Page) - jcr:title:"Mall 1"
sling:resourceType: "myapp/mall"
/jcr:content - location: "Location 1"
nt:unstructured: This node type is used for the store1 and offer1 nodes. This is because these nodes do not necessarily represent pages in your website, but rather components or elements within a page.
/content
/malls
/mall1 (cq:Page)
/jcr:content
/store1 (nt:unstructured) - name : "Store1"
type:"Electronic"
/offer1 (nt:unstructured) : "10% off"
This structure allows you to leverage AEM’s page editing features for each mall, while also providing flexibility for the stores and offers. However, this is just one possible way to structure your data, and the actual structure may vary based on your specific requirements.
On the approach part basically all three can be used and it depends on your use case so as which to pick.
Custom Servlet (Utility): This method is powerful and flexible. It allows you to create a user interface for uploading the CSV file, and you can handle errors and edge cases in a user-friendly way. However, it requires more development effort compared to the other methods, as you need to write the servlet, create the user interface, and handle the file upload.
Groovy Scripts: This method is quick and easy, especially if you have the ACS AEM Commons package installed. It allows you to quickly write and execute scripts without needing to compile or deploy code. However, it’s less user-friendly, as you need to manually run the script each time you want to import data. Also, error handling and edge cases might be more difficult to manage.
JCR API: This method is also powerful and flexible, and it doesn’t require any additional packages. However, it requires you to write a standalone Java application, which might be overkill for a simple data import. Also, like the Groovy script method, it’s less user-friendly and requires manual execution.
Thanks,
Madhur
@Madhur-Madan for explaining comprehensively. I have another requirement. I have a list(hyperlinks) of stores on a page (a store component on page). The requirement is when user clicks on any specific store, a page for that store should be dynamically created and it should redirect user to that page and fetch the information for that store and show its information on that dynamically created page (store detail). Is it possible or I will have to create a separate page for each store.
Hi @touseefk2181136 ,
The dynamic page creation is very much possible but keep in mind the dispatcher configs if you want the dynamic pages to be cached.
I will list down two approaches which you can use to fulfill the requirement. You can either use vanilla Js or you can use Sling Resource resolution approach.
1. Vanilla Js
<!-- Store Component (/apps/myproject/components/store/store.html) -->
<div class="store">
<h2 class="store-name">${properties.storeName}</h2>
<p class="store-description">${properties.storeDescription}</p>
<a href="#" class="view-details">View Details</a>
</div>
// Store Component JavaScript (/apps/myproject/components/store/clientlibs/store.js)
$(document).ready(function() {
$('.view-details').on('click', function(e) {
e.preventDefault(); // Prevent default link behavior
var storeName = $(this).closest('.store').find('.store-name').text();
// Check if the page already exists for the selected store
$.ajax({
url: '/content/myproject/stores/' + encodeURIComponent(storeName) + '.html',
type: 'HEAD',
success: function() {
// Page already exists, redirect to it
window.location.href = '/content/myproject/stores/' + encodeURIComponent(storeName) + '.html';
},
error: function() {
// Page doesn't exist, create it dynamically
createStorePage(storeName);
}
});
});
});
// Function to create store page dynamically
function createStorePage(storeName) {
// Sample Java code for dynamic page creation
Resource parentPage = resourceResolver.getResource("/content/myproject/stores");
PageManager pageManager = resourceResolver.adaptTo(PageManager.class);
// Create a new page for the store
Page storePage = pageManager.create(parentPage, storeName, "/apps/myproject/templates/store-detail.html", storeName);
// Redirect to the newly created store detail page
window.location.href = '/content/myproject/stores/' + encodeURIComponent(storeName) + '.html';
}
<!-- Store Detail Template (/apps/myproject/templates/store-detail.html) -->
<sly data-sly-use.page="com.adobe.cq.wcm.core.components.models.Page">
<h1>${page.properties.storeName}</h1>
<p>${page.properties.storeDescription}</p>
<!-- Additional components or placeholders for displaying store information -->
</sly>
// Sample Java code for dynamic page creation
Resource parentPage = resourceResolver.getResource("/content/myproject/stores");
PageManager pageManager = resourceResolver.adaptTo(PageManager.class);
// Create a new page for the store
Page storePage = pageManager.create(parentPage, storeName, "/apps/myproject/templates/store-detail.html", storeName);
<!-- Store Detail Page (/content/myproject/stores/store-name.html) -->
<sly data-sly-use.page="com.adobe.cq.wcm.core.components.models.Page">
<h1>${page.properties.storeName}</h1>
<p>${page.properties.storeDescription}</p>
<!-- Additional components or placeholders for displaying store information -->
</sly>
2. Sling Resource Resolution
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.api.servlets.ServletResolverConstants;
import org.apache.sling.api.servlets.Servlet;
import org.osgi.service.component.annotations.Component;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Reference;
import org.apache.sling.servlets.annotations.ServletResourceTypes;
import org.apache.sling.servlets.annotations.SlingServletResourceTypes;
@Component(service = Servlet.class,
property = {
Constants.SERVICE_DESCRIPTION + "=Dynamic Store Page Creation Servlet",
ServletResolverConstants.SLING_SERVLET_PATHS + "=/bin/createStorePage"
})
@SlingServletResourceTypes(resourceTypes = "sling/servlet/default", methods = HttpConstants.METHOD_GET)
public class CreateStorePageServlet extends SlingAllMethodsServlet {
@Reference
private ResourceResolver resourceResolver;
@Override
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) {
String storeName = request.getParameter("storeName");
if (storeName != null) {
// Check if page already exists for the store name
if (!pageExistsForStore(storeName)) {
// Page doesn't exist, dynamically create it
createPageForStore(storeName);
}
}
}
private boolean pageExistsForStore(String storeName) {
// Logic to check if page exists for the given store name
// Implement your own logic to check if a page exists
}
private void createPageForStore(String storeName) {
// Logic to dynamically create the store detail page
// Use AEM's page creation API or workflow capabilities
Resource parentPage = resourceResolver.getResource("/content/myproject/stores");
PageManager pageManager = resourceResolver.adaptTo(PageManager.class);
Page storePage = pageManager.create(parentPage, storeName, "/apps/myproject/templates/store-detail.html", storeName);
}
}
I hope it helps
Thanks,
Madhur
@Madhur-Madan Thank you for explaining. I come to conclusion is to use a single store detail page and use content fragment on page to change the content related to each store. In this scenario, only thing I am thinking about is URL of the store page which should contain the name of the store to make it SEO friendly.
I agree with this.
At first sight I would agree to your structure. But there are several unknowns, which can influence this. A few thoughts:
In that case you should think how you want to use the MSM relations, and how the MSM rollouts will be done. Also about local changes, which are overriding MSM inheritance.
(You should also do the math and think about if the MSM is the right tool here. Rollouts can be costly/performance-intense, and if you are just duplicating content without ever overriding it locally you should thinking about replacing duplicated content with a reference.)
Also think about how you want to structure your content processes. You might want to do a paper-and-pencil exercise with your future authoring users to see if that content creation workflow is okay.