Modeling Product Models

Modeling Product Model Data Using the GoodRelations Ontology

Author: Martin Hepp

Abstract:On this page, we explain how the GoodRelations Ontology can be used to specify product models, like "Ford T", "Volkswagen Golf", "Thinkpad T60", and the like.

Prequisites: You need to understand the difference between a product model, a product class, and an actual product instance. Such is explained in the GoodRelations Primer and the GoodRelations Technical Report.
Note: If you want to include product features, you must use or create a products or services ontology like eClassOWL. See here for details.

Step-by-Step Example

Scenario: Assume we want to say that ACME Electronics produces a TV set model ACME Colorvision 123. It has a screensize of 20 centimeters.

Step 1: Define the namespaces and import the GoodRelations ontology and the respective ontology for products and services.

@prefix toy: <> .
@prefix gr: <> .
@prefix xsd: <> .
@prefix default: <> .
@prefix protege: <> .
@prefix rdfs: <> .
@prefix rdf: <> .
@prefix owl: <> .

a owl:Ontology ;
owl:imports <> , <> .

Step 2: Define the Business Entity, i.e. the manufacturer.

  a gr:BusinessEntity ;
  gr:hasDUNS "123456789"^^xsd:string ;
  gr:legalName "ACME Electronics Ltd."^^xsd:string .

Step 3: Define the product model and its features.

   a gr:ProductOrServiceModel , toy:TVSet ;
   gr:description "The ACME Model Colorvision 123"^^xsd:string ;
   gr:hasEAN_UCC-13 "1234567890123"^^xsd:string ;
   gr:hasManufacturer default:ACME_Electronics ;
   toy:hasScreenSize default:QuantitativeValueFloat_1 .

   a gr:QuantitativeValueFloat ;
   gr:hasUnitOfMeasurement "CMT"^^xsd:string ;
   gr:hasValueFloat "20"^^xsd:string .

Step 4: Express that the manufacturer is also selling unknown instances of this model (if needed).

   a gr:Offering ;
   gr:eligibleCustomerTypes gr:Reseller ;
   gr:hasBusinessFunction gr:Sell ;
   gr:includesObject default:TypeAndQuantityNode_3 ;
   gr:validFrom "2008-08-14T16:40:50"^^xsd:dateTime ;
   gr:validThrough "2009-08-14T16:40:54"^^xsd:dateTime .

   a gr:ProductOrServicesSomeInstancesPlaceholder , toy:TVSet ;
   gr:hasMakeAndModel default:Colorvision123 .

   a gr:TypeAndQuantityNode ;
   gr:amountOfThisGood "1.0"^^xsd:float ;
   gr:hasUnitOfMeasurement "C62"^^xsd:string ;
   gr:typeOfGood default:SomeACMETVSets .

Step 5: Make your RDF data known!

Don't forget to make your RDF data known:

Full Example in N3 Notation

@prefix toy: <> .
@prefix gr: <> .
@prefix xsd: <> .
@prefix default: <> .
@prefix protege: <> .
@prefix rdfs: <> .
@prefix rdf: <> .
@prefix owl: <> .

a owl:Ontology ;
owl:imports <> , <> .

   a gr:BusinessEntity ;
   gr:hasDUNS ""^^xsd:string ;
   gr:legalName ""^^xsd:string .

   a gr:ProductOrServiceModel , toy:TVSet ;
   rdfs:comment "The ACME Model Colorvision 123"^^xsd:string ;
   gr:description "The ACME Model Colorvision 123"^^xsd:string ;
   gr:hasEAN_UCC-13 "1234567890123"^^xsd:string ;
   gr:hasManufacturer default:ACME_Electronics ;
   toy:hasScreenSize default:QuantitativeValueFloat_1 .

   a gr:QuantitativeValueFloat ;
   gr:hasUnitOfMeasurement "CMT"^^xsd:string ;
   gr:hasValueFloat "20"^^xsd:string .

   a gr:Offering ;
   gr:eligibleCustomerTypes gr:Reseller ;
   gr:hasBusinessFunction gr:Sell ;
   gr:includesObject default:TypeAndQuantityNode_3 ;
   gr:validFrom "2008-08-14T16:40:50"^^xsd:dateTime ;
   gr:validThrough "2009-08-14T16:40:54"^^xsd:dateTime.

   a gr:ProductOrServicesSomeInstancesPlaceholder , toy:TVSet ;
   gr:hasMakeAndModel default:Colorvision123 .

   a gr:TypeAndQuantityNode ;
   gr:amountOfThisGood "1.0"^^xsd:float ;
   gr:hasUnitOfMeasurement "C62"^^xsd:string ;
   gr:typeOfGood default:SomeACMETVSets .

How to handle property-value pairs that require additional cleansing

Exposing fine-grained product model master data using GoodRelations is a major step towards findability for products on the long tail. Unfortunately, thequality of the raw data available or the lack of a vocabulary for product classes or properties is often a limitation. As for the latter, simply create proprietary classes, and properties for each property in your dataspace and let's try to link them later on. Still, implementing the recipe from above can be very difficult for at least a part of the data you may have, because this step, requires that you can

  1. decide whether a property is qualitative (list of predefined values), quantitative (anything than can be an interval), or datatype (boolean, string, digits that are no numbers,...)
  2. split the unit of measurement from the property or from the value.


Often, values and units are mixed in either the value field or in the property name.

Property Value
Screen size 20 inches
Screen size (inch.) 20

Even if you want to use proprietary properties, you would have to extract the actual value (20) and the UN/CEFACT code for the unit of measurement (INH for inches).

Also, multiple properties are frequently stuffed into a single field:

Property Value
Dimensions (WxDxH) 10x20x50 cm
Dimensions (cm^3) 10x20x50 cm

You would have to extract all three values and the UN/CEFACT codes for each unit of measurement (CMT for centimeters in this example).

  • Width: value: 10, unit: CMT
  • Depth: value: 20, unit: CMT
  • Height: value: 50, unit: CMT

Now, often these three steps are difficult for a part of large data sources, either due to lack of structure, data quality issues, or both.
Luckily, you can still expose your data on the Web using GoodRelations by using the GoodRelation design principle of "incremental enrichment":

While the solution above is the "textbook" or "gold standard" way, you can still publish the data, even if not yet perfect. The Web will do the rest - new tools and people.

Recipe: "Incremental Enrichment"

Here is how to do it:

1. First, create an owl:datatypeProperty, name it "Temporary Property - Properties to be cleansed", set the range to xsd:string, and make it an rdfs:subpropertyOf gr:datatypeProductOrServiceProperty.

foo:P_TemporaryProperty a owl:datatypeProperty;
        rdfs:label "Superproperty of all Temporary Properties";
        rdfs:range  xsd:string;
        rdfs:subpropertyOf gr:datatypeProductOrServiceProperty.

Now, let's assume you have the two entries:

Size: 10x20x50 cm
Screen size: 20 inches

For each property / value pair that cannot be easily turned into the proper representation, do the following:

2. Create an owl:datatypeProperty, name it "P_ (Temporary Property)", set the range to xsd:string, and make it an rdfs:subpropertyOf P_TemporaryProperty.

foo:P_000001 a owl:datatypeProperty;
        rdfs:label "Size (Temporary Property)";
        rdfs:range  xsd:string;
        rdfs:subpropertyOf foo:P_TemporaryProperty .

foo:P_000002 a owl:datatypeProperty;
        rdfs:label "Screen size (Temporary Property)";
        rdfs:range  xsd:string;
        rdfs:subpropertyOf foo:P_TemporaryProperty .

3. Use the original value, no matter what content it actually is, as a literal value of type xsd:string and attach it to the product using the new properties:

foo:TV_Set_Model1233 a gr:ProductOrServiceModel,
       foo:P_000001 "10x20x50 cm";
       foo:P_000002 "20 inches".

And you are done!

Quite clearly, those properties are not yet accessible for intelligent deep comparison, e.g. "find a TV Set that has a screen size of at least 19 inches". But

- you can publish all of your data right away
- other can lift that data using heuristics.

This approach makes the most sense if only a part of your properties are difficult to handle, while others can be lifted to the proper modeling using Regular Expressions etc.

Note that due to introducing P_TemporaryProperty, we can now easily search for such properties using SPARQL and keep them apart from true datatype properties. (Important: Keep in mind that GoodRelations does not use owl:datatypeProperties for typical float or integer values.)

SELECT ?prop ?value
WHERE {?prop rdfs:subpropertyOf foo:P_TemporaryProperty. ?s ?prop ?value.}

You can then combine SPARQL CONSTRUCT + Regular Expressions or external software components to create the cleansed property value pairs