Archive for the ‘Uncategorized’ Category

Justin Bieber Loves the Semantic Web

Posted in: Uncategorized

justin bieber striking a pose The magic continues as we try to move the technology dial forward on bestbuy.com. Along with some other fun forward-thinking stuff, we also encoded all our music product detail pages with RDFa, featuring such great vocabularies as GoodRelations, the Music Ontology, Dublin Core, Facebook OpenGraph markup, and Google’s (Rich Snippets) Breadcrumbs RDFa spec to provide a complete front-end semantic solution to assist machines in reaching valuable product data directly through the browser.

Now I know many of you aren’t the CD buyin’ kind (what the heck is a CD?!), but the following Justin Bieber example can serve as an example for physical media as well as digital scenarios.


Feast your eyes on the human version of Justin Bieber in all his teenage glory as he funks it up with the greatest artists of our time like Ludacris, Usher, and Jessica Jarrell (http://www.bestbuy.com/site/My+World+2.0+-+CD/9771571.p?id=2086661&skuId=9771571):

Justin Bieber my life 2.0

Justin Bieber on bestbuy.com (click for full image)

Running it through an RDFa distiller produces some interesting data results. First the GoodRelations offering:

goodrelations justin bieber offering

gr:Offering of Justin Bieber (click for full image)

Breaking the offer down, we see this offer has two price specifications. The first specifies the current price. The second highlights the standard retail price (list). Hey, it looks like this masterpiece is on sale!

justin bieber haspricespecification

Justin Bieber hasPriceSpecification! (click for full image)

The second part of the gr:Offer focuses on the product details including UPC (EAN), SKU, product image, and product type, etc.

justin bieber album details

Justin Bieber's album details (click for full image)

So let’s talk deeper product details data next. Music releases have evolved from focusing on the entire album to diving into track details. Of course, any serious Bieber fan will want to consume every last second of every track, but the less infatuated fans purchase individual tracks instead. The Music Ontology provides a perfect foundation to list and highlight track details:

justin bieber track listing

Justin Bieber track listing in data (click for full image)

There’s no question that Bieber fans will be praising his work and want to share on Facebook. Since OpenGraph has become the rage in the semantic world (second only to Bieber himself), we’ve added og: meta information in the head of our HTML:

justin bieber hits the opengraph

Justin Bieber hits the opengraph (click for full image)

If you run J.B. through the official Facebook URL Linter product details are extracted:

running JB through the Facebook URL Linter

Running Justing Bieber through the Facebook URL Linter (click for full image)

This product page is an object on the OpenGraph!

I encourage you to run your parsers against any one of our music product detail pages and share the results. Work is ongoing to provide an XML sitemap just for music products — stay tuned for more information. In the meantime, I’m waiting for internet sensation Rebecca Black to muster up some more hits to throw on a full album so we can mark it up in RDFa. :-)

* Disclaimer: the opinions of Justin Bieber and related artists is that of the author and not that of Best Buy, Co., Inc., it’s subsidiaries or employees.

Released! Human Product Discovery Via Machines, RDFa and “Shop URLs”

Posted in: Business, Data Portability, GoodRelations, HTML, Linked Data, RDF, RDFa, Semantic Web, Theory, Uncategorized

Well folks, we’re at it again. The month by month the journey continued Monday into Tuesday night to semantify the hallowed templates of bestbuy.com. One of April’s goals: to enhance machine understanding of Best Buy’s considerable product offerings while retaining human searchability and readability. After long wait, we have deployed code to the search templates to establish a human-readable and machine-parseable front-end API.

Many moons ago (even before all this RDFa goodness), we established a URI scheme we call “shop URLs”. Basically it’s an easy way to pass a search term in a URI and get a visual list of up 50 products our search appliance considers relevant. However, when you have a catalog of 400K+ products, simple visual results may not be the best or most efficient way to sort through the cruft and get at what you’re looking for. Enter stage left our friendly machine helpers: Search Engines, Parsers and Aggregators — this deployment activity is focused on feeding you! We’ve deployed step one of enabling a solution to product visibility and discovery issue by unleashing the result data in RDFa (with GoodRelations, Dublin Core, FOAF, Google Ratings vocabs) for maximum machine parseability.

After all this grandeur and hype, I’m hoping you’re still interested in how it works. You may point your eyes and parsers here:

http://www.bestbuy.com/shop/search+term*

* Please note, due to marketing and business considerations, some of the more popular terms may redirect you to a dataless “category page”. To get a RDFa-enabled result, simply append a * to your search term, e.g., http://www.bestbuy.com/shop/ipods* (how dare those marketing people stand in the way of good data!)

Let’s dive deeper with a quick example. So I’m a bit eclectic and looking for a thermometer online. I would like to see results of the “thermometers” from bestbuy.com, plus pass the data to my machine friend, an application I am building to help me make the right product choice.

First I type access my human-friendly representation using a “shop URL” directly in the browser:

http://www.bestbuy.com/shop/thermometers

Which results in a human-readable web page:

example of human-readable search page

human-readable shop url

Looks like I have 15 product offers that match and are available via bestbuy.com or in store. Excellent.

I’m going to take that same URI and pass it on to my machine helper who just wants the data, no fluff. Let’s say we’re working with RDF/XML…on the surface, the 15 product offers may appear like this:

rdf extract from shop url

rdf extract from shop url

Expanding an individual offer yields the following data-rich result:

expanded data extract of shop url

expanded data extract of shop url

So endeth the second phase of sematification. Make sure and leave your API keys at home, this search data is all open! Tune in for more later this week, I will be discussing another one of April’s goals, expanding RDFa markup to Best Buy’s product detail pages.

Smarter Check Availability Page on Bestbuy.com with RDFa

Posted in: Uncategorized

Checking availability on Best Buy listingRecently we’ve set off on a project journey something we’ve thoughtfully nicknamed “browse widening”. The basic aim of the browse widening project is, well, to make the site wider (groundbreaking, I know). Because this activity (in theory) should take a dev about 5 minutes to accomplish, we have decided to stick a couple of extra nuggets into the “requirements” of the project. As part of my ongoing passion to turn bestbuy.com into the most data-rich website on the planet, I am augmenting the site’s HTML with RDFa and vocabularies like GoodRelations, vCard, and Google’s review vocab, integrating rich product and store data directly into the front-end user experience to maximize the machine extractability and readability while preserving the visual user experience as it stands today.

In late February, we deployed the first iteration of RDFa-enhanced browse widening to the Check Availability page. In it’s current state, users who navigate through the bestbuy.com browse experience and check availability on products are taken to the check product availability page. If unrecognized or unauthenticated, the user is required to enter a zip code which shows the availability of said product at the closet store locations. If authenticated, the user is taken to the product availability page with their preferred stores availability status.

On the surface, this seems like your run-of-the-mill product availability page. But if you peel back a layer and view source on the HTML code, you’ll find a literal treasure trove of rich product and store data. Let’s take a peek at an example of a product that has varying store availability to show the data we’re making available to machines, and, most importantly, the RELATIONSHIPS we’re establishing between the product and where it “lives”.

First we’ll start with a code snippet highlighting the offering and product details:

<div class="csc-large-column csc-last-column" style="margin-top: 0; padding-top: 0;" xmlns:gr="http://purl.org/goodrelations/v1#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:vcard="http://www.w3.org/2006/vcard/ns#">
<div id="checkavail-prodlisting" typeof="gr:Offering" about="#Offering_0885909435340">
	<span rel="gr:availableAtOrFrom" resource="#BestBuy_281"></span>
	<span rel="gr:availableAtOrFrom" resource="#BestBuy_1000"></span>
	<span rel="gr:availableAtOrFrom" resource="#BestBuy_1935"></span>
	<span rel="gr:availableAtOrFrom" resource="#BestBuy_611"></span>
	<div rel="gr:includesObject">
		<div typeof="gr:TypeAndQuantityNode" about="#TypeAndQuantityNode_0885909435340">
			<span property="gr:amountOfThisGood" datatype="xsd:float" content="1.0"></span>
			<span property="gr:hasUnitOfMeasurement" datatype="xsd:string" content="C62"></span>
			<div rel="gr:typeOfGood">
				<div typeof="gr:ProductOrServicesSomeInstancesPlaceholder" about="#ProductOrServicesSomeInstancesPlaceholder_0885909435340">
					<div class="checkavail-product-image" rel="rdfs:seeAlso foaf:depiction"><a href="/site/olspage.jsp?skuId=9845572&type=product&id=1218183952317" rel="product" class="uri"><img src="http://images.bestbuy.com:80/BestBuy_US/images/products/9845/9845572_sc.jpg" alt="9845572 Front Detail" height="56.0" width="105.0"></a></div>
						<span class="checkavail-productlisting-link"><a href="/site/olspage.jsp?skuId=9845572&type=product&id=1218183952317" rel="product" class="uri">Apple® - iPod shuffle® 2GB* MP3 Player (4th Generation - Latest Model) - Orange</a><span property="rdfs:label" content="Apple® - iPod shuffle® 2GB* MP3 Player (4th Generation - Latest Model) - Orange"></span></span>
						<br />
						<span class="checkavail-productlisting-model-sku"><strong>Model: </strong><span property="gr:hasMPN">MC752LL/A</span> | <strong>SKU:</strong><span property="gr:hasStockKeepingUnit">9845572</span><span property="gr:hasEAN_UCC-13" content="0885909435340"></span></span>
					</div>
				</div>
			<div class="clear"></div>
		</div>
	</div>
</div>


To the average HTML layperson, this markup might not equate to much. But let me assure you, there is some very interesting and powerful data modeling at work here, delivered directly through the HTML markup. Let’s take a granular look at what is going on in the code by highlighting individual parts of this snippet. To start, we declare the namespaces of the vocabularies we’re using in our solution. For the Check Product Availability page we combine GoodRelations, FOAF and vCard vocabularies to model the complete solution.

<div class="csc-large-column csc-last-column" xmlns:gr="http://purl.org/goodrelations/v1#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:vcard="http://www.w3.org/2006/vcard/ns#">


Next, we identify a unique identifier for each product offer, using the gr:Offering class. My naming convention includes the UPC of the product with a zero prepended to it, which essentially is the standard format for EAN — a world-wide identifier used everywhere except the US (take that world — you, your EANs and crazy metric system can take a hike!). I’m a big fan of the UPC as product identifier — of course the “u” stands for “universal” which I dream could serve as a sort of “primary key” for product searching on the web.

<div id="checkavail-prodlisting" typeof="gr:Offering" about="#Offering_0885909435340">



The next section of code is where we start to see relationships forming. For this example, we see four span tags with gr:availableAtOrFrom and unique hash identifiers. The importance of this code as a relationship builder (the “cupid” of the GoodRelations solution?) will become more apparent later in the example. For now, let’s just recognize that it’s key to informing machines where the product offer can be procured, ie, where the product is in stock.

<span rel="gr:availableAtOrFrom" resource="#BestBuy_281"></span>
<span rel="gr:availableAtOrFrom" resource="#BestBuy_1000"></span>
<span rel="gr:availableAtOrFrom" resource="#BestBuy_1935"></span>
<span rel="gr:availableAtOrFrom" resource="#BestBuy_6"></span>



Our next sub-snippet encapsulates the product offered in the offering (via gr:includesObject), product type and quantity (via gr:TypeAndQuantityNode) and the description and attributes of the product (via gr:typeOfGood and it’s child methods). As you can see, we utilize GoodRelations as the “base” vocabulary, but also include FOAF and RDFS vocabs to provide a full solution. This is also where we dive deeper into product details, far beyond surface level attributes like price. foaf:depiction for a product image, gr:hasMPN for manufacturer product number, gr:hasStockKeepingUnit for SKU, and gr:hasEAN_UCC-13 for EAN (UPC) give machines access to important product attributes that are beneficial to allow machines to make sense of the massive amount of unstructured product data out on the web today.

<div rel="gr:includesObject">
	<div typeof="gr:TypeAndQuantityNode" about="#TypeAndQuantityNode_0885909435340">
		<span property="gr:amountOfThisGood" datatype="xsd:float" content="1.0"></span>
		<span property="gr:hasUnitOfMeasurement" datatype="xsd:string" content="C62"></span>
		<div rel="gr:typeOfGood">
			<div typeof="gr:ProductOrServicesSomeInstancesPlaceholder" about="#ProductOrServicesSomeInstancesPlaceholder_0885909435340">
				<div class="checkavail-product-image" rel="rdfs:seeAlso foaf:depiction">
					<a href="/site/olspage.jsp?skuId=9845572&type=product&id=1218183952317" rel="product" class="uri"><img src="http://images.bestbuy.com:80/BestBuy_US/images/products/9845/9845572_sc.jpg" alt="9845572 Front Detail" height="56.0" width="105.0"></a></div>
					<span class="checkavail-productlisting-link">
						<a href="/site/olspage.jsp?skuId=9845572&type=product&id=1218183952317" rel="product" class="uri">Apple® - iPod shuffle® 2GB* MP3 Player (4th Generation - Latest Model) - Orange</a><span property="rdfs:label" content="Apple® - iPod shuffle® 2GB* MP3 Player (4th Generation - Latest Model) - Orange"></span>
					</span>
					<br />
					<span class="checkavail-productlisting-model-sku">
						<strong>Model: </strong><span property="gr:hasMPN">MC752LL/A</span> | <strong>SKU:</strong><span property="gr:hasStockKeepingUnit">9845572</span>
						<span property="gr:hasEAN_UCC-13" content="0885909435340"></span>
					</span>
			</div>
	</div>
</div>



Finally, we produce the list of stores with their location data and product availability status. Luckily for machines, we are coding these results using vCard RDFa — providing a rich consumable result (truncated to two store results for readability):

<tr>
	<td class="checkavail-locationtable-locncolumn" typeof="gr:LocationOfSalesOrServiceProvisioning" about="#BestBuy_281">
		<span class="location-name" property="rdfs:label">RICHFIELD MN</span>
		<div class="street-address" rel="vcard:adr">
			<span property="vcard:street-address">1000 WEST 78TH ST</span>,
			<span property="vcard:locality">RICHFIELD</span>,
			<span property="vcard:region">MN</span>
			<span property="vcard:postal-code">55423</span>
		</div>
		<div class="addl">
			<a href="Javascript:mapanddirection('281', 'cat12091')">Map & Directions</a>
		</div>
	</td>
	<td class="checkavail-locationtable-availstatuscolumn">
		<span class="checkavail-availablenow">
			<span class="available-now">Available now</span>
		</span>
		<br />
		Pick up 03/15/2011
	</td>
	<td class="checkavail-locationtable-btncolumn"> 
		<input type="image" src="http://images.bestbuy.com:80/BestBuy_US/en_US/images/global/buttons/btn_addtocart_pdp.gif" border="0" alt="Add To Cart" name="281addtocart">
	</td>
</tr>
<tr>
	<td class="checkavail-locationtable-locncolumn" typeof="gr:LocationOfSalesOrServiceProvisioning" about="#BestBuy_5">
		<span class="location-name">EDINA MN</span>
		<div class="street-address" rel="vcard:adr">
			<span property="vcard:street-address">3200 SOUTHDALE CIR</span>,
			<span property="vcard:locality">EDINA</span>,
			<span property="vcard:region">MN</span>
			<span property="vcard:postal-code">55435</span>
		</div>
		<div class="addl">
			<a href="Javascript:mapanddirection('5', 'cat12091')">Map & Directions</a>
		</div>
	</td>
	<td class="checkavail-locationtable-availstatuscolumn">
		<div>
			<span class="shiptostore">
				<span class="ship-to-store">Ship to store</span>
			</span>
		</div>
		Usually <strong>ships to store</strong> in 3 to 5 days
	</td>
	<td class="checkavail-locationtable-btncolumn"> 
		<input type="image" onclick="Javascript:checkCallType('5')" src="http://images.bestbuy.com:80/BestBuy_US/en_US/images/global/buttons/btn_addtocart_pdp.gif" border="0" alt="Add To Cart" name="5addtocart">
	</td>
</tr>



Now that we have rich markup for our offering, the product and the store locations, it’s time to examine the magic that is inherent in this solution. Remember gr:availableAtOrFrom? It’s part of the key to establish RELATIONSHIPS, which is part of the sweet, sweet goodness of Semantic Web and Linked Data. In our HTML result, stores that have the product on hand are represented by a gr:availableAtOrFrom node which contains a resource attribute. This provides an inter-document pointer to the rich store location details (marked by the “about” attribute), essentially linking a product offer object to a store object and it’s location attributes. Stores where the product is not physically available do not return a gr:availableAtOrFrom. From this RDFa model, a machine can assert product availability – and extract rich location details about the stores where the product is in stock. A truncated code example may help here:

<!-- gr:availableAtOrFrom resources -->
<span rel="gr:availableAtOrFrom" resource="#BestBuy_281"></span>
<span rel="gr:availableAtOrFrom" resource="#BestBuy_1000"></span>

[code]...

<!-- html/data representation of the store, note "about" attribute -->
<td class="checkavail-locationtable-locncolumn" typeof="gr:LocationOfSalesOrServiceProvisioning" about="#BestBuy_281">
<span class="location-name" property="rdfs:label">RICHFIELD MN</span>

[etc]...


Planning Phase 2 -- Matrix URIs

The examples you see here represent the first phase in a two-phased approach to deliver the smartest Check Product Availability page on the web today (at least for the next couple of months until everyone else catches on). As I mentioned in the intro, the code featured here is available through the human browse experience, so physical clicks of the mouse are necessary to resolve to the RDFa-enriched data endpoints. For phase 2, I would like to introduce a direct RDFa endpoint via Matrix URIs to complete the machine-readable portion of the solution.

If you're unfamiliar with the concepts and structure of a Matrix URI, there is an interesting write-up from Tim Berners-Lee back in 1996 that outlines the design. Imagine this Matrix URI being the canonical URI to access very rich data about a product and it's availability:

Using these methods enables the unleashing of important data on the web that provides machines with a greater visibility and understanding of the products, goods and services we offer -- all without an API key. We're not stopping at Product Availability -- keep you're eyes open for our next iteration where we will enable the smartest search results pages using RDFa and friendly "shop" URLs. Don't miss it!

Search

You are currently browsing the archives for the Uncategorized category.

Archives

RSS

Contact

Jay Myers
Minneapolis, Minnesota US (CDT)
45.032742, -93.360229