Implementing rich snippets on your WebSphere Commerce site to improve search engine results

A step-by-step tutorial using WebSphere Commerce V7 FEP3

Learn how to mark up data for product reviews and other product information for the Madisons starter store that comes with WebSphere® Commerce V7.

Share:

Naomi Wan (nylwan@ca.ibm.com), Staff Software Developer, IBM

Photo of Naomi WanNaomi Wan is a Staff Software Developer at the IBM Toronto Software Lab, Canada. She has been part of the WebSphere Commerce team since 2001. She has extensive experience in WebSphere Commerce starter stores and Web 2.0 technologies.



24 August 2011

Before you start

Search engine helps users find the page they want by showing a summary of content from web pages. These are called search result snippets. The more information a search result snippet can provide, the easier users will find the page they want. Using rich snippets, webmasters can further label content of their site to help users understand the content a page provides.

For example, the rating of a restaurant can be summarized as shown in Figure 1.

Figure 1. Search result snippet of a restaurant
Search result of a restaurant with rich snippets

Note the search result snippet in Figure 1 displays the summary of rating of a restaurant, along with the total number of reviews available for the restaurant.

About this tutorial

This tutorial will provide examples on how to mark up data for product reviews and other product information for the Madisons starter store that comes with WebSphere Commerce.

There are various mark up formats available to mark up HTML for rich snippets, for example, microdata, microformats, and PDFa. In addition to these formats, schema.org provides a collection of schemas that are recognized by all major search providers like Google®, Yahoo!®, Microsoft®, and Bing®. Microdata is the standard format used by schema.org.

In this tutorial, examples will be provided in microdata format and using the schema.org vocabulary.

Objectives

In this tutorial, you will learn how to:

  • Identify what product data can be marked up.
  • Locate the places to mark up product information in the Madisons store's JSPs.
  • Test your rich snippets using the Rich Snippet Testing Tool from Google.

Prerequisites

You need to be familiar with the following:

System requirements

Examples provided from this article are applied to stores that come from WebSphere Commerce V7 with Feature Pack 3, which contains a new implementation for Search Engine Optimization (SEO). We highly recommend that you leverage Feature Pack 3 for the SEO solution. For more information, see this WebSphere Commerce Information Center topic, Search engine optimization (SEO) for Version 7 Feature Pack 3 or later.

In addition, a Social Commerce feature needs to be enabled for product review function. For instructions on how to enable the Social Commerce feature, see Enabling and configuring Social Commerce.

Duration

This tutorial takes about 1 hour to complete.


Identifying product information to mark up

There are various properties available to mark up your product data. The code snippet in Listing 1 shows how to mark up the product name, description, price, availability, and review of a product in microdata format.

Listing 1. Marking up product data in microdata format
<div itemscope="itemscope" itemtype="http://data-vocabulary.org/Product">
  <span itemprop="name">Sleek Occasional Table</span> 
  
  <span itemprop="description">
    The sleek styling makes this occasional table a perfect addition to your home. 
    Made of oak wood with a tempered glass top and forest green-finish wood frame.
    Measures 48" in width, 24" in length, and 19" in height. 
    Some assembly required.
  </span>
  
  <span itemprop="offerDetails" itemscope itemtype="http://data-vocabulary.org/Offer">
    Regular price: $
    <span itemprop="price">179.99</span>
    <span itemprop="availability" content="in_stock">
      In stock! Order now!
    </span>
  </span>
  
  <span itemprop="review" itemscope 
  itemtype="http://data-vocabulary.org/Review-aggregate">
    <span itemprop="rating">4.4</span> stars, based on
    <span itemprop="count">89</span> reviews
  </span>
</div>

The code in Listing 2 shows how to mark up the same information using the schema.org vocabulary.

Listing 2. Marking up product data using schema.org vocabulary
<div itemscope itemtype="http://schema.org/Product">
  <span itemprop="name">Sleek Occasional Table</span>
  
  Product description:
  <span itemprop="description">
    The sleek styling makes this occasional table a perfect addition to your home. 
    Made of oak wood with a tempered glass top and forest green-finish wood 
    frame. Measures 48" in width, 24" in length, and 19" in 
    height. Some assembly required.
  </span>
  
  <div itemprop="offers" itemscope itemtype="http://schema.org/Offer">
    <span itemprop="price">$179.99</span>
    <span itemprop="availability" href="http://schema.org/InStock" content="in_stock"/>
      In stock
    </span>
  </div>
  
  <div itemprop="aggregateRating" itemscope itemtype="http://schema.org/AggregateRating">
    Rated <span itemprop="ratingValue">4.4</span>/5
    based on <span itemprop="reviewCount">89</span> customer reviews
  </div>
</div>

Marking up the product detail page

Once you identify what information to mark up for your product, find and update the JSPs. This section will provide pointers on how to update the Madisons store JSPs to mark up the product name, description, price, availability, and rating of a product in microdata format.

Defining the itemscope attribute

  1. First, define the itemscope attribute to tell you are using microdata on this page and you are marking up a product in this case. A URL that you can use for the item type is http://data-vocabulary.org/Product.
  2. Open CachedProductOnlyDisplay.jsp and locate the following block of code shown in Listing 3. Add the line highlighted in bold to the JSP.
    Listing 3. Adding itemscope to CachedProductOnlyDisplay.jsp
    <%-- remove double quote from shortdescription --%>
    <c:set var="search" value='"'/>
    <c:set var="replaceStr" value="'"/>
    <c:set var="prodShortDesc" 
    value="${fn:replace(product.description.shortDescription, search, replaceStr)}"/>
    
    <div id="body588">
      <div itemscope="itemscope" itemtype="http://data-vocabulary.org/Product">
        <div id="product">
          <div class="product_images" id="WC_CachedProductOnlyDisplay_div_1">
            <c:if test="${addProductDnD eq 'true'}">
              <div dojoType="dojo.dnd.Source" jsId="dndSource" 
              id="DndItem_<c:out value='${dnd_catalogEntryID}'/>" copyOnly="true" 
              dndType="<c:out value='${dragType}'/>">
                <div class="dojoDndItem" dndType="<c:out value='${dragType}'/>" 
                id="WC_CachedProductOnlyDisplay_div_2">
            </c:if>

    To close the div, go to the end of the file and locate the following block of code shown in Listing 4. Add the line highlighted in bold to the JSP.

    Listing 4. Adding itemscope to CachedProductOnlyDisplay.jsp
          <%-- End - Added for Coremetrics Intelligent Offer --%>	
        </flow:ifEnabled>
        </div>
      </div>
    </div>
    <!-- END CachedProductOnlyDisplay.jsp -->

Defining the itemprop name

  1. Next, you can add an itemprop attribute to mark up the name of the product.
  2. From CachedProductOnlyDisplay.jsp, locate the following block of code shown in Listing 5. Add the code highlighted in bold to the JSP.
    Listing 5. Adding name itemprop to CachedProductOnlyDisplay.jsp
    <div class="product_options" id="WC_CachedProductOnlyDisplay_div_3">
      <h1 id="catalog_link" class="catalog_link">
        <span itemprop="name">
          <c:out value="${product.description.name}" escapeXml="false"/>
        </span>
      </h1>
      
      <%-- in Elite, we do not want to show the price because the price may be different 
      depending on the contract that is selected --%>
      <c:if test="${displayListPriceInProductPage == 'true'}">	
       <div id="WC_CachedProductOnlyDisplay_div_4">
        <span class="price bold"><fmt:message key="PRICE" bundle="${storeText}"/></span>
        <c:set var="catalogEntryDB" value="${product}" />
        <c:set var="displayPriceRange" value="true"/>
        <c:set var="priceHighlightable" value="true"/>
        <%@ include file="../../../Snippets/ReusableObjects/
         CatalogEntryPriceDisplay.jspf"%>
       </div>
      </c:if>

Defining the itemprop description

  1. You can also add an itemprop attribute to mark up the description of the product.
  2. From CachedProductOnlyDisplay.jsp, locate the following block of code shown in Listing 6. Add the code highlighted in bold to the JSP.
    Listing 6. Adding description itemprop to CachedProductOnlyDisplay.jsp
    <p>
      <fmt:message key="SKU" bundle="${storeText}" />: 
      <c:out value="${product.partNumber}" escapeXml="false"/>
    </p>
    <p>
      <c:out value="${product.description.shortDescription}" escapeXml="false"/>
    </p>
    <br />
    <p>
      <span itemprop="description">
        <c:out value="${product.description.longDescription}" escapeXml="false"/>
      </span>
    </p>
    <br />

Note that in this example, you are using the long description as a description of the product. You can choose to use the product short description instead.

Defining the itemprop offer

  1. Next, add the itemprop offer, which specifies the price and availability of the product.
  2. To add the itemprop offer from CachedProductOnlyDisplay.jsp, locate the following block of code shown in Listing 7. Add the code highlighted in bold to the JSP.
    Listing 7. Adding offer itemprop to CachedProductOnlyDisplay.jsp
      <%@ include file="CachedProductOnlyImageExt.jspf"%>
    </div>
    
    <span itemprop="offerDetails" itemscope itemtype="http://data-vocabulary.org/Offer">
    <div class="product_options" id="WC_CachedProductOnlyDisplay_div_3">
      <h1 id="catalog_link"  class="catalog_link">
        <span itemprop="name">
          <c:out value="${product.description.name}" escapeXml="false"/>
        </span>
      </h1>
      
      <%-- in Elite, we do not want to show the price because the price may be different 
      depending on the contract that is selected --%>
      <c:if test="${displayListPriceInProductPage == 'true'}">	
       <div id="WC_CachedProductOnlyDisplay_div_4">
        <span class="price bold"><fmt:message key="PRICE" bundle="${storeText}"/></span>
        <c:set var="catalogEntryDB" value="${product}" />
        <c:set var="displayPriceRange" value="true"/>
        <c:set var="priceHighlightable" value="true"/>
        <%@ include file="../../../Snippets/ReusableObjects/
         CatalogEntryPriceDisplay.jspf"%>
       </div>
      </c:if>
  3. To close the span, locate the following block of code shown in Listing 8. Add the code highlighted in bold to the JSP.
    Listing 8. Adding offer itemprop to CachedProductOnlyDisplay.jsp
      <%out.flush();%>
      <c:import url="${jspStoreDir}Snippets/Catalog/CatalogEntryDisplay/
      CatalogEntryInventoryStatus.jsp">
        <c:param name="fromPage" value="${fromPage}"/>
        <c:param name="catalogEntryID" value="${catalogEntryID}"/>
        <c:param name="catentryId" value="${forInventoryCatentryId}"/>
        <c:param name="numberOfSKUs" value="${numberOfSKUs}"/>
      </c:import>
      <%out.flush();%>
      <%@ include file="CatalogEntryInventoryStatusEIExt.jspf"%>
    	
    </div>
    </span>
    <br />
  4. Next, specify the price itemprop for the product. To do so from CatalogEntryPriceDisplay.jspf, locate the following block of code shown in Listing 9. Add the code highlighted in bold to the JSP.
    Listing 9. Adding price itemprop to CatalogEntryPriceDisplay.jspf
      <%-- If the price string has been set, then we simply print it out. --%>
      <c:when test="${!empty priceString}">
        <span itemprop="price">
          <span <c:if test="${not empty priceHighlightable}">id="price"</c:if> 
          class="price bold"><c:out value="${priceString}" escapeXml="false" /></span>
        </span>
      </c:when>
    	
      <%-- If the list price does not exist or is smaller than the offer price, 
      print out both the offer price only. --%>
      <c:when test="${dataBean && (!listPriced || displayPrice.amount 
         <= offerPrice.amount)}">
        <span <c:if test="${not empty priceHighlightable}">id="price"</c:if> 
        class="price bold">
          <span itemprop="price">
            <fmt:formatNumber value="${offerPrice.amount}" type="currency" 
            currencySymbol="${CurrencySymbolToFormat}" 
            maxFractionDigits="${currencyDecimal}"/>
            <c:out value="${CurrencySymbol}"/>
          </span>
        </span>
      </c:when>
    	
      <c:otherwise>
        <span <c:if test="${not empty priceHighlightable}">id="listPrice"</c:if> 
        class="price listPrice bold">
         <fmt:formatNumber value="${displayPrice.amount}" type="currency" 
         currencySymbol="${CurrencySymbolToFormat}" maxFractionDigits=
          "${currencyDecimal}"/>
         <c:out value="${CurrencySymbol}"/>
        </span>
        <div <c:if test="${not empty priceHighlightable}">id="offerPrice"</c:if> 
        class="price offerprice bold">
          <span itemprop="price">
            <fmt:formatNumber value="${offerPrice.amount}" type="currency" 
            currencySymbol="${CurrencySymbolToFormat}" 
            maxFractionDigits="${currencyDecimal}"/>
            <c:out value="${CurrencySymbol}"/>
          </span>
        </div>
      </c:otherwise>
    </c:choose>
  5. To specify the availability itemprop for the product from CatalogEntryInventoryStatus.jsp, locate the following block of code shown in Listing 10. Add the code highlighted in bold to the JSP.
    Listing 10. Adding availability itemprop to CatalogEntryInventoryStatus.jsp

    Click to see code listing

    Listing 10. Adding availability itemprop to CatalogEntryInventoryStatus.jsp

    <c:otherwise>
    
      <c:set var="invStatus" value="NA"/>
      <fmt:message key="INV_INV_NA" bundle="${storeText}" var="invStatusDisplay"/>
    </c:otherwise>
    </c:choose>
    <c:choose><c:when test="${invStatus == 'Available'}"><c:set var="richSnippetContent" value="in_stock"/></c:when><c:otherwise><c:set var="richSnippetContent" value="out_of_stock"/></c:otherwise></c:choose>
    <p class="stock_status" id="online_availability_inventory_section_${htmlIdentifier}" 
    style="<c:out value="${defaultDisplayStyle}"/>"><
    img id="onlinestore_inventory_status_image_${htmlIdentifier}" 
    src="${jspStoreImgDir}images/${invStatus}.gif" alt="<c:out 
    value="${invStatusDisplay}"/>" border="0" />&nbsp;<span 
    id="onlinestore_inventory_status_${htmlIdentifier}" 
    itemprop="availability" content="${richSnippetContent}"><c:out 
     value="${invStatusDisplay}"/>
    </span></p>
    
    <flow:ifEnabled feature="StoreLocator">
    <p class="instore_availability" id="instore_availability_message_${htmlIdentifier}" 
    style="<c:out value="${defaultDisplayStyle}"/>">
    <fmt:message key="PRODUCT_INV_STORE" bundle="${storeText}"/></p>
    
    <p class="stock_status" id="store_inv_status_p_0_${htmlIdentifier}" 
     style="display:none">
    <img alt="" border="0" id="store_inv_status_img_0_${htmlIdentifier}"/>
    &nbsp;<a href="#" class="bopis_link" id="store_inv_status_link_0_${htmlIdentifier}">
    </a>&nbsp;<span id="store_inv_status_0_${htmlIdentifier}"></span></p>
    <p class="stock_status" id="store_inv_status_p_1_${htmlIdentifier}" 
     style="display:none">
    <img alt="" border="0" id="store_inv_status_img_1_${htmlIdentifier}"/>
    &nbsp;<a href="#" class="bopis_link" id="store_inv_status_link_1_${htmlIdentifier}">
    </a>&nbsp;<span id="store_inv_status_1_${htmlIdentifier}"></span></p>
    <p class="stock_status" id="store_inv_status_p_2_${htmlIdentifier}" 
     style="display:none">
    <img alt="" border="0" id="store_inv_status_img_2_${htmlIdentifier}"/>
    &nbsp;<a href="#" class="bopis_link" id="store_inv_status_link_2_${htmlIdentifier}">
    </a>&nbsp;<span id="store_inv_status_2_${htmlIdentifier}"></span></p>
    <p class="stock_status" id="store_inv_status_p_3_${htmlIdentifier}" 
     style="display:none">
    <img alt="" border="0" id="store_inv_status_img_3_${htmlIdentifier}"/>
    &nbsp;<a href="#" class="bopis_link" id="store_inv_status_link_3_${htmlIdentifier}">
    </a>&nbsp;<span id="store_inv_status_3_${htmlIdentifier}"></span></p>
    <p class="stock_status" id="store_inv_status_p_4_${htmlIdentifier}" 
     style="display:none">
    <img alt="" border="0" id="store_inv_status_img_4_${htmlIdentifier}"/>
    &nbsp;<a href="#" class="bopis_link" id="store_inv_status_link_4_${htmlIdentifier}">
    </a>&nbsp;<span id="store_inv_status_4_${htmlIdentifier}"></span></p>

Defining the itemprop review

Another important piece of information that you can mark up for rich snippets is the product rating. To do so, a Social Commerce feature has to be installed and enabled, and SocialCommerce.sar must be published on top of the Madisons store. For more information about how to enable this Social Commerce feature, see Enabling and configuring Social Commerce.

  1. To specify the itemprop review for the product, find the Social Commerce EAR folder from your application server. Inside the EAR, find and edit Reviews.html. Locate the block of code shown in Listing 11 and add the code highlighted in bold to the HTML.
    Listing 11. Adding review itemprop to Reviews.html
    <div class="reviewsHeader" >
      <div class="itemContainer" dojoAttachPoint="itemContainer">
       <div class="overallRating" tabindex="0" dojoAttachPoint="overallRatingDisplayDiv">
         <span class="overallRatingLabel">${messages.rating_overallRating}</span>
         <span class="overallRatingStarDisplay" 
             dojoAttachPoint="overallRatingDisplayImg">
            <img dojoAttachPoint="prodRatingStar_1" src="${imagePath}/star_grey.png" 
            alt="star"/>
            <img dojoAttachPoint="prodRatingStar_2" src="${imagePath}/star_grey.png" 
            alt="star"/>
            <img dojoAttachPoint="prodRatingStar_3" src="${imagePath}/star_grey.png" 
            alt="star"/>
            <img dojoAttachPoint="prodRatingStar_4" src="${imagePath}/star_grey.png" 
            alt="star"/>
            <img dojoAttachPoint="prodRatingStar_5" src="${imagePath}/star_grey.png" 
            alt="star"/>
          </span>
          <span itemprop="review" 
          itemscope itemtype="http://data-vocabulary.org/Review-aggregate">
            <span class="overallRatingText"dojoAttachPoint="overallRatingText" ></span>
          </span>
          <br>
        </div>
  2. Next, mark up the rating information. From Reviews.js, locate the block of code shown in Listing 12 and add the code highlighted in bold to the JSP.
    Listing 12. Adding rating itemprop to Reviews.js
    case 9: fractionStar.src = this.imagePath + "/star_full.9.png";
       break;
             
        }
    		 
      }
    	 
      this.overallRatingText.innerHTML = "<span itemprop='rating'>" + 
      overallRatingNum.toFixed(1) + "</span>" + this.messages.rating_overallRating_of; 
    }
    
    else{
      this.overallRatingText.innerHTML = this.messages.rating_overallRating_zero_reviews;
    }
  3. When there is no rating specified, you can show the information by modifying ReviewsNJS.js. Locate the block of code shown in Listing 13 and add the code highlighted in bold to the JSP.
    Listing 13. Adding count itemprop to ReviewsNJS.js
      "rating_overallRating": "Overall Rating : ",
      "rating_overallRating_of": " of 5",
      "rating_overallRating_zero_reviews": "<span itemprop='count'>0</span> reviews",
    	
      "reviewTitleLimitExceeded" : "The title you have entered is too long. The title 
       can be a maximum of {0} characters.",
      "reviewCommentsLimitExceeded" : "The comments you have entered is too long. 
      Comments can be a maximum of {0} characters.",

Now, you are ready to test how your search result snippets. Note that the above examples provide a walkthough on how to mark up the product detail page. If you want to mark up the item, package, and bundle pages, you will need to update the corresponding JSPs for those pages.


Testing your rich snippets

Google provides a testing tool online. To see how your search results are displayed in Google, simply go to Rich Snippets Testing Tool, and provide your page's URL to see how your page will appear in the search results.

At the time this article is written, the Rich Snippet Testing Tool from Google cannot display schema.org results. However, you can still use the tool to see what information Google can extract from the pages.

  1. To test how rich snippets show up for your page, just provide the URL of your page to the Rich Snippets Testing Tool as shown in Figure 2.
    Figure 2. Rich Snippets Testing Tool by Google
    Rich Snippets Testing Tool by Google
  2. Figure 3 shows an example of a product page from the Madisons store. The mark up was done in microdata format.
    Figure 3. Madisons product page mark up using microdata
    Madisons product page mark up using microdata
  3. Figure 4 shows an example of the same product page marked up in schema.org vocabulary. Note again Google cannot display schema.org results at the time this article was written. However, you can use still use it to show what rich snippet data Google can extract from the page.
    Figure 4. Madisons product page mark up using schema.org Vocabulary
    Madisons product page mark up using schema.org Vocabulary

You are now done with marking up your product page data.


Providing rich snippets to Google

  1. Submit a data feed to the Google Merchant Center.
  2. Mark up the product content directly on your HTML pages.

For more information, see the Webmaster Tools help page.


Conclusion

This tutorial covered how to mark up a product details page on the Madisons store using the microdata format. In this tutorial, you updated various JSPs to mark up the name, description, price, availability, and review information of a product. You also learned how to test the changes using the Rich Snippet Testing Tool from Google.

Resources

Learn

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=753701
ArticleTitle=Implementing rich snippets on your WebSphere Commerce site to improve search engine results
publish-date=08242011