A complete example ontology for the Acme Shop e-commerce platform, demonstrating all features of the Magnowlia ontology framework.
owl: → <http://www.w3.org/2002/07/owl#>rdf: → <http://www.w3.org/1999/02/22-rdf-syntax-ns#>rdfs: → <http://www.w3.org/2000/01/rdf-schema#>xsd: → <http://www.w3.org/2001/XMLSchema#>skos: → <http://www.w3.org/2004/02/skos/core#>dct: → <http://purl.org/dc/terms/>b: → <https://acmeshop.example.com/ontology/business#>bv: → <https://magnowlia.com/ontology/business-vocabulary#>m: → <https://magnowlia.com/ontology/mapping#>t: → <https://acmeshop.example.com/ontology/tech#>t:order_summary_view
m:Table
t:customers
m:Table
t:products
m:Table
t:service_items
m:Table
t:customer_loyalty
m:Table
t:shipments
m:Table
t:carriers
m:Table
t:customer_product_favorites
m:Table
t:order_summary_view.order_id
m:Column
t:order_summary_view.order_date
m:Column
t:order_summary_view.order_total
m:Column
t:order_summary_view.order_status
m:Column
t:order_summary_view.customer_country
m:Column
t:order_summary_view.product_category
m:Column
t:order_summary_view.payment_method
m:Column
t:order_summary_view.currency
m:Column
t:order_summary_view.customer_type
m:Column
t:order_summary_view.is_test_order
m:Column
t:order_summary_view.customer_id
m:Column
t:order_summary_view.quantity
m:Column
t:order_summary_view.product_id
m:Column
t:customers.customer_id
m:Column
t:customers.customer_segment
m:Column
t:customers.customer_name
m:Column
t:customers.customer_email
m:Column
t:customers.customer_country
m:Column
t:customers.customer_type
m:Column
t:customers.created_at
m:Column
t:customer_loyalty.customer_id
m:Column
t:customer_loyalty.tier
m:Column
t:customer_loyalty.points_balance
m:Column
t:products.product_id
m:Column
t:products.product_name
m:Column
t:products.product_category
m:Column
t:products.product_price
m:Column
t:service_items.service_id
m:Column
t:service_items.service_name
m:Column
t:service_items.service_category
m:Column
t:service_items.service_price
m:Column
t:order_summary_view.service_id
m:Column
t:shipments.order_id
m:Column
t:shipments.carrier_code
m:Column
t:carriers.carrier_code
m:Column
t:carriers.carrier_name
m:Column
t:customer_product_favorites.customer_id
m:Column
t:customer_product_favorites.product_id
m:Column
b:Order
owl:Class
b:Customer
owl:Class
b:LoyaltyMember
owl:Class m:BusinessView
b:loyaltyMemberTier
owl:DatatypeProperty
b:loyaltyMemberPoints
owl:DatatypeProperty
b:loyaltyMemberName
owl:DatatypeProperty
b:Purchasable
owl:Class
b:Product
owl:Class
b:ServiceItem
owl:Class
b:WholesaleCustomer
owl:Class m:BusinessView
b:InternalCustomer
owl:Class m:BusinessView
b:PremiumWholesaleCustomer
owl:Class m:BusinessView
b:ReturnedOrder
owl:Class m:BusinessView
b:CancelledOrder
owl:Class m:BusinessView
b:Carrier
owl:Class
b:customerHasOrder
owl:ObjectProperty
b:orderForCustomer
owl:ObjectProperty
b:orderHasProduct
owl:ObjectProperty
b:productInOrder
owl:ObjectProperty
b:orderHasPurchasable
owl:ObjectProperty
b:orderHasServiceItem
owl:ObjectProperty
b:serviceItemInOrder
owl:ObjectProperty
b:orderShippedByCarrier
owl:ObjectProperty
b:carrierShippedOrder
owl:ObjectProperty
b:customerFavoritedProduct
owl:ObjectProperty
b:productFavoritedByCustomer
owl:ObjectProperty
b:orderId
owl:DatatypeProperty
b:orderDate
owl:DatatypeProperty
b:orderTotal
owl:DatatypeProperty
b:orderStatus
owl:DatatypeProperty
b:customerCountry
owl:DatatypeProperty
b:productCategory
owl:DatatypeProperty
b:paymentMethod
owl:DatatypeProperty
b:orderCurrency
owl:DatatypeProperty
b:customerType
owl:DatatypeProperty
b:customerId
owl:DatatypeProperty
b:orderQuantity
owl:DatatypeProperty
b:isTestOrder
owl:DatatypeProperty
b:productId
owl:DatatypeProperty
b:productName
owl:DatatypeProperty
b:productCategoryMaster
owl:DatatypeProperty
b:productPrice
owl:DatatypeProperty
b:serviceId
owl:DatatypeProperty
b:serviceName
owl:DatatypeProperty
b:serviceCategory
owl:DatatypeProperty
b:servicePrice
owl:DatatypeProperty
b:customerSegment
owl:DatatypeProperty
b:customerName
owl:DatatypeProperty
b:customerEmail
owl:DatatypeProperty
b:customerCountryMaster
owl:DatatypeProperty
b:customerTypeMaster
owl:DatatypeProperty
b:customerCreatedAt
owl:DatatypeProperty
b:carrierName
owl:DatatypeProperty
b:gbpToEurExchangeRate
bv:BusinessConstant
b:revenueAccessPolicy
bv:AccessPolicy
b:customerCountryRowFilter
bv:RowFilter
b:totalRevenueMetric
bv:Metric
b:orderCountMetric
bv:Metric
b:averageOrderValueMetric
bv:Metric
b:returnRateMetric
bv:Metric
b:uniqueCustomersMetric
bv:Metric
b:totalItemsSoldMetric
bv:Metric
b:revenuePerCustomerSegmentMetric
bv:Metric
b:newCustomerRegistrationsMetric
bv:Metric
b:newRegistrations_correlates_returnRate
bv:MetricCorrelation
b:returnRate_drives_averageOrderValue
bv:MetricCorrelation
| 1 | @prefix owl: <http://www.w3.org/2002/07/owl#> . |
| 2 | @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . |
| 3 | @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . |
| 4 | @prefix xsd: <http://www.w3.org/2001/XMLSchema#> . |
| 5 | @prefix skos: <http://www.w3.org/2004/02/skos/core#> . |
| 6 | @prefix dct: <http://purl.org/dc/terms/> . |
| 7 | @prefix b: <https://acmeshop.example.com/ontology/business#> . |
| 8 | @prefix bv: <https://magnowlia.com/ontology/business-vocabulary#> . |
| 9 | @prefix m: <https://magnowlia.com/ontology/mapping#> . |
| 10 | @prefix t: <https://acmeshop.example.com/ontology/tech#> . |
| 11 | |
| 12 | # =========================================================================== |
| 13 | # ONTOLOGY HEADER |
| 14 | # =========================================================================== |
| 15 | |
| 16 | "ttl-uri"><https://acmeshop.example.com/ontology> a owl:Ontology ; |
| 17 | dct:title "Acme Shop Optimized Business Ontology" ; |
| 18 | dct:description "Business-facing ontology for Acme Shop e-commerce platform, optimized for clarity and mapped to the analytics data warehouse." ; |
| 19 | dct:created "2026-03-03"^^xsd:date ; |
| 20 | dct:modified "2026-03-03"^^xsd:date ; |
| 21 | dct:contributor "Magnowlia Platform Team" ; |
| 22 | owl:versionInfo "0.2.0"^^xsd:string ; |
| 23 | rdfs:comment "Design decisions: (1) order_summary_view is the primary fact table - a denormalized view joining orders, customers and products. Properties like customerCountry and productCategory have domain b:Order because they are only available through this view. (2) Constants are inlined in metric expressions; bv:BusinessConstant declarations serve as documentation. (3) Metrics use structured properties (metricExpression/sourceEntity) for single-table queries and metricSql for multi-table joins." . |
| 24 | |
| 25 | # =========================================================================== |
| 26 | # TECHNICAL LAYER - TABLES |
| 27 | # =========================================================================== |
| 28 | |
| 29 | t:order_summary_view a m:Table ; |
| 30 | rdfs:label "Order Summary View" ; |
| 31 | rdfs:comment "Denormalized view joining orders, customers, and products. Primary fact table for order analytics." . |
| 32 | |
| 33 | t:customers a m:Table ; |
| 34 | rdfs:label "Customers" ; |
| 35 | rdfs:comment "Customer master data including segmentation and demographics." . |
| 36 | |
| 37 | t:products a m:Table ; |
| 38 | rdfs:label "Products" ; |
| 39 | rdfs:comment "Product catalog with categories and pricing." . |
| 40 | |
| 41 | t:service_items a m:Table ; |
| 42 | rdfs:label "Service Items" ; |
| 43 | rdfs:comment "Service offerings such as warranties, installations, and subscriptions." . |
| 44 | |
| 45 | t:customer_loyalty a m:Table ; |
| 46 | rdfs:label "Customer Loyalty" ; |
| 47 | rdfs:comment "Loyalty-program data for customers — tier and points balance. One row per enrolled customer." . |
| 48 | |
| 49 | # --- Tables backing the bridge-join + junction relationship examples --- |
| 50 | t:shipments a m:Table ; |
| 51 | rdfs:label "Shipments" ; |
| 52 | rdfs:comment "One row per shipment, linking an order to the carrier that delivered it." . |
| 53 | |
| 54 | t:carriers a m:Table ; |
| 55 | rdfs:label "Carriers" ; |
| 56 | rdfs:comment "Shipping carrier master data." . |
| 57 | |
| 58 | t:customer_product_favorites a m:Table ; |
| 59 | rdfs:label "Customer Product Favorites" ; |
| 60 | rdfs:comment "Junction table linking customers to the products they have favorited (many-to-many)." . |
| 61 | |
| 62 | # =========================================================================== |
| 63 | # TECHNICAL LAYER - COLUMNS |
| 64 | # =========================================================================== |
| 65 | |
| 66 | t:order_summary_view.order_id a m:Column ; |
| 67 | rdfs:label "Order ID" ; |
| 68 | rdfs:comment "Unique order identifier (PK)." ; |
| 69 | m:belongsToTable t:order_summary_view . |
| 70 | |
| 71 | t:order_summary_view.order_date a m:Column ; |
| 72 | rdfs:label "Order Date" ; |
| 73 | rdfs:comment "Date the order was placed. Used as the primary time dimension for order metrics." ; |
| 74 | m:belongsToTable t:order_summary_view . |
| 75 | |
| 76 | t:order_summary_view.order_total a m:Column ; |
| 77 | rdfs:label "Order Total" ; |
| 78 | rdfs:comment "Total order amount. Currency is indicated by the currency column; may be EUR or GBP." ; |
| 79 | m:belongsToTable t:order_summary_view . |
| 80 | |
| 81 | t:order_summary_view.order_status a m:Column ; |
| 82 | rdfs:label "Order Status" ; |
| 83 | rdfs:comment "Current order fulfillment status: pending, confirmed, shipped, delivered, returned, cancelled." ; |
| 84 | m:belongsToTable t:order_summary_view . |
| 85 | |
| 86 | t:order_summary_view.customer_country a m:Column ; |
| 87 | rdfs:label "Customer Country" ; |
| 88 | rdfs:comment "ISO country code of the ordering customer. Denormalized from customer master." ; |
| 89 | m:belongsToTable t:order_summary_view . |
| 90 | |
| 91 | t:order_summary_view.product_category a m:Column ; |
| 92 | rdfs:label "Product Category" ; |
| 93 | rdfs:comment "Category of the ordered product. Denormalized from product catalog." ; |
| 94 | m:belongsToTable t:order_summary_view . |
| 95 | |
| 96 | t:order_summary_view.payment_method a m:Column ; |
| 97 | rdfs:label "Payment Method" ; |
| 98 | rdfs:comment "Payment method used for the order: credit_card, paypal, bank_transfer, apple_pay." ; |
| 99 | m:belongsToTable t:order_summary_view . |
| 100 | |
| 101 | t:order_summary_view.currency a m:Column ; |
| 102 | rdfs:label "Currency" ; |
| 103 | rdfs:comment "ISO currency code for the order amount (EUR, GBP)." ; |
| 104 | m:belongsToTable t:order_summary_view . |
| 105 | |
| 106 | t:order_summary_view.customer_type a m:Column ; |
| 107 | rdfs:label "Customer Type" ; |
| 108 | rdfs:comment "Customer classification: regular, internal (staff), wholesale." ; |
| 109 | m:belongsToTable t:order_summary_view . |
| 110 | |
| 111 | t:order_summary_view.is_test_order a m:Column ; |
| 112 | rdfs:label "Is Test Order" ; |
| 113 | rdfs:comment "Boolean flag indicating whether this is a test order that should be excluded from reporting." ; |
| 114 | m:belongsToTable t:order_summary_view . |
| 115 | |
| 116 | t:order_summary_view.customer_id a m:Column ; |
| 117 | rdfs:label "Customer ID" ; |
| 118 | rdfs:comment "FK to customers table. Denormalized into the view for join-free access." ; |
| 119 | m:belongsToTable t:order_summary_view . |
| 120 | |
| 121 | t:order_summary_view.quantity a m:Column ; |
| 122 | rdfs:label "Quantity" ; |
| 123 | rdfs:comment "Number of items in the order line." ; |
| 124 | m:belongsToTable t:order_summary_view . |
| 125 | |
| 126 | t:order_summary_view.product_id a m:Column ; |
| 127 | rdfs:label "Product ID" ; |
| 128 | rdfs:comment "FK to products table. Identifies the product in the order." ; |
| 129 | m:belongsToTable t:order_summary_view . |
| 130 | |
| 131 | t:customers.customer_id a m:Column ; |
| 132 | rdfs:label "Customer ID" ; |
| 133 | rdfs:comment "Customer unique identifier (PK)." ; |
| 134 | m:belongsToTable t:customers . |
| 135 | |
| 136 | t:customers.customer_segment a m:Column ; |
| 137 | rdfs:label "Customer Segment" ; |
| 138 | rdfs:comment "Customer segment classification: Premium, Standard, New, Churned." ; |
| 139 | m:belongsToTable t:customers . |
| 140 | |
| 141 | t:customers.customer_name a m:Column ; |
| 142 | rdfs:label "Customer Name" ; |
| 143 | rdfs:comment "Full name of the customer." ; |
| 144 | m:belongsToTable t:customers . |
| 145 | |
| 146 | t:customers.customer_email a m:Column ; |
| 147 | rdfs:label "Customer Email" ; |
| 148 | rdfs:comment "Primary email address of the customer." ; |
| 149 | m:belongsToTable t:customers . |
| 150 | |
| 151 | t:customers.customer_country a m:Column ; |
| 152 | rdfs:label "Customer Country" ; |
| 153 | rdfs:comment "ISO country code of the customer. Master data source for the denormalized order_summary_view.customer_country." ; |
| 154 | m:belongsToTable t:customers . |
| 155 | |
| 156 | t:customers.customer_type a m:Column ; |
| 157 | rdfs:label "Customer Type" ; |
| 158 | rdfs:comment "Customer classification: regular, internal (staff), wholesale. Master data source for the denormalized order_summary_view.customer_type." ; |
| 159 | m:belongsToTable t:customers . |
| 160 | |
| 161 | t:customers.created_at a m:Column ; |
| 162 | rdfs:label "Created At" ; |
| 163 | rdfs:comment "Timestamp when the customer record was created (registration date)." ; |
| 164 | m:belongsToTable t:customers . |
| 165 | |
| 166 | # --- customer_loyalty columns --- |
| 167 | |
| 168 | t:customer_loyalty.customer_id a m:Column ; |
| 169 | rdfs:label "Loyalty Customer ID" ; |
| 170 | rdfs:comment "Reference to the customer this loyalty record belongs to." ; |
| 171 | m:belongsToTable t:customer_loyalty . |
| 172 | |
| 173 | t:customer_loyalty.tier a m:Column ; |
| 174 | rdfs:label "Loyalty Tier" ; |
| 175 | rdfs:comment "Loyalty program tier: bronze, silver, gold, platinum." ; |
| 176 | m:belongsToTable t:customer_loyalty . |
| 177 | |
| 178 | t:customer_loyalty.points_balance a m:Column ; |
| 179 | rdfs:label "Loyalty Points Balance" ; |
| 180 | rdfs:comment "Current loyalty point balance for the customer." ; |
| 181 | m:belongsToTable t:customer_loyalty . |
| 182 | |
| 183 | t:products.product_id a m:Column ; |
| 184 | rdfs:label "Product ID" ; |
| 185 | rdfs:comment "Product unique identifier (PK)." ; |
| 186 | m:belongsToTable t:products . |
| 187 | |
| 188 | t:products.product_name a m:Column ; |
| 189 | rdfs:label "Product Name" ; |
| 190 | rdfs:comment "Display name of the product." ; |
| 191 | m:belongsToTable t:products . |
| 192 | |
| 193 | t:products.product_category a m:Column ; |
| 194 | rdfs:label "Product Category" ; |
| 195 | rdfs:comment "Category the product belongs to: Electronics, Clothing, Home & Garden, Sports, Books." ; |
| 196 | m:belongsToTable t:products . |
| 197 | |
| 198 | t:products.product_price a m:Column ; |
| 199 | rdfs:label "Product Price" ; |
| 200 | rdfs:comment "Current list price of the product in EUR." ; |
| 201 | m:belongsToTable t:products . |
| 202 | |
| 203 | # --- service_items columns --- |
| 204 | |
| 205 | t:service_items.service_id a m:Column ; |
| 206 | rdfs:label "Service ID" ; |
| 207 | rdfs:comment "Unique service item identifier (PK)." ; |
| 208 | m:belongsToTable t:service_items . |
| 209 | |
| 210 | t:service_items.service_name a m:Column ; |
| 211 | rdfs:label "Service Name" ; |
| 212 | rdfs:comment "Display name of the service item." ; |
| 213 | m:belongsToTable t:service_items . |
| 214 | |
| 215 | t:service_items.service_category a m:Column ; |
| 216 | rdfs:label "Service Category" ; |
| 217 | rdfs:comment "Category the service belongs to: Warranty, Installation, Subscription, Support." ; |
| 218 | m:belongsToTable t:service_items . |
| 219 | |
| 220 | t:service_items.service_price a m:Column ; |
| 221 | rdfs:label "Service Price" ; |
| 222 | rdfs:comment "Current price of the service item in EUR." ; |
| 223 | m:belongsToTable t:service_items . |
| 224 | |
| 225 | t:order_summary_view.service_id a m:Column ; |
| 226 | rdfs:label "Service ID" ; |
| 227 | rdfs:comment "FK to service_items table. Identifies the service in the order. Null when the order line is for a product." ; |
| 228 | m:belongsToTable t:order_summary_view . |
| 229 | |
| 230 | # --- shipments columns --- |
| 231 | t:shipments.order_id a m:Column ; |
| 232 | rdfs:label "Order ID" ; |
| 233 | rdfs:comment "Order this shipment fulfills (FK to order_summary_view.order_id)." ; |
| 234 | m:belongsToTable t:shipments . |
| 235 | |
| 236 | t:shipments.carrier_code a m:Column ; |
| 237 | rdfs:label "Carrier Code" ; |
| 238 | rdfs:comment "Carrier that handled this shipment (FK to carriers.carrier_code)." ; |
| 239 | m:belongsToTable t:shipments . |
| 240 | |
| 241 | # --- carriers columns --- |
| 242 | t:carriers.carrier_code a m:Column ; |
| 243 | rdfs:label "Carrier Code" ; |
| 244 | rdfs:comment "Unique carrier identifier (PK)." ; |
| 245 | m:belongsToTable t:carriers . |
| 246 | |
| 247 | t:carriers.carrier_name a m:Column ; |
| 248 | rdfs:label "Carrier Name" ; |
| 249 | rdfs:comment "Human-readable carrier name." ; |
| 250 | m:belongsToTable t:carriers . |
| 251 | |
| 252 | # --- customer_product_favorites columns (pure junction: two FKs, no payload) --- |
| 253 | t:customer_product_favorites.customer_id a m:Column ; |
| 254 | rdfs:label "Customer ID" ; |
| 255 | rdfs:comment "Favoriting customer (FK to customers.customer_id)." ; |
| 256 | m:belongsToTable t:customer_product_favorites . |
| 257 | |
| 258 | t:customer_product_favorites.product_id a m:Column ; |
| 259 | rdfs:label "Product ID" ; |
| 260 | rdfs:comment "Favorited product (FK to products.product_id)." ; |
| 261 | m:belongsToTable t:customer_product_favorites . |
| 262 | |
| 263 | # =========================================================================== |
| 264 | # BUSINESS LAYER - CLASSES |
| 265 | # =========================================================================== |
| 266 | |
| 267 | b:Order a owl:Class ; |
| 268 | rdfs:label "Order" ; |
| 269 | rdfs:comment "A customer order placed on the Acme Shop platform. Backed by the order_summary_view which is a denormalized view joining orders, customers, and products." ; |
| 270 | rdfs:seeAlso "ttl-uri"><https://schema.org/Order> ; |
| 271 | skos:example "ORD-2025-001234, ORD-2025-005678" ; |
| 272 | m:mapsToTable t:order_summary_view . |
| 273 | |
| 274 | b:Customer a owl:Class ; |
| 275 | rdfs:label "Customer" ; |
| 276 | skos:altLabel "Buyer", "Account" ; |
| 277 | rdfs:comment "A registered customer of Acme Shop." ; |
| 278 | rdfs:seeAlso "ttl-uri"><https://schema.org/Person> ; |
| 279 | skos:example "Jane Smith, Acme Corporation" ; |
| 280 | m:mapsToTable t:customers . |
| 281 | |
| 282 | b:LoyaltyMember a owl:Class, m:BusinessView ; |
| 283 | rdfs:label "Loyalty Member" ; |
| 284 | rdfs:comment "An enrolled loyalty-program customer, with tier and points alongside the customer's name and contact details." ; |
| 285 | rdfs:seeAlso b:Customer ; |
| 286 | skos:example "Jane Smith (Gold, 12 480 pts), Acme Corporation (Platinum, 87 200 pts)" ; |
| 287 | m:mapsToTable t:customers, t:customer_loyalty ; |
| 288 | m:joinCondition "customers.customer_id = customer_loyalty.customer_id"^^xsd:string . |
| 289 | |
| 290 | b:loyaltyMemberTier a owl:DatatypeProperty ; |
| 291 | rdfs:label "Loyalty Tier" ; |
| 292 | rdfs:domain b:LoyaltyMember ; |
| 293 | rdfs:range xsd:string ; |
| 294 | rdfs:comment "Loyalty program tier — bronze, silver, gold, platinum." ; |
| 295 | m:mapsToColumn t:customer_loyalty.tier ; |
| 296 | bv:enumType "dynamic" . |
| 297 | |
| 298 | b:loyaltyMemberPoints a owl:DatatypeProperty ; |
| 299 | rdfs:label "Loyalty Points Balance" ; |
| 300 | rdfs:domain b:LoyaltyMember ; |
| 301 | rdfs:range xsd:integer ; |
| 302 | rdfs:comment "Current point balance for the member." ; |
| 303 | m:mapsToColumn t:customer_loyalty.points_balance . |
| 304 | |
| 305 | b:loyaltyMemberName a owl:DatatypeProperty ; |
| 306 | rdfs:label "Loyalty Member Name" ; |
| 307 | rdfs:domain b:LoyaltyMember ; |
| 308 | rdfs:range xsd:string ; |
| 309 | rdfs:comment "Full name of the loyalty member." ; |
| 310 | m:mapsToColumn t:customers.customer_name . |
| 311 | |
| 312 | # --- Polymorphic relationship: Purchasable (Product or ServiceItem) --- |
| 313 | # An Order can reference either a Product or a ServiceItem via the common |
| 314 | # superclass Purchasable. This models a polymorphic association in OWL: |
| 315 | # orderHasPurchasable has range b:Purchasable, and both Product and ServiceItem |
| 316 | # are subclasses, so the relationship accepts either type. |
| 317 | |
| 318 | b:Purchasable a owl:Class ; |
| 319 | rdfs:label "Purchasable" ; |
| 320 | rdfs:comment "Abstract superclass for anything that can appear as a line item in an order. Subclassed by Product (physical goods) and ServiceItem (services, warranties, subscriptions). This class has no direct table mapping — use the concrete subclasses Product (t:products) or ServiceItem (t:service_items) for queries." . |
| 321 | |
| 322 | b:Product a owl:Class ; |
| 323 | rdfs:subClassOf b:Purchasable ; |
| 324 | rdfs:label "Product" ; |
| 325 | skos:altLabel "Item", "SKU" ; |
| 326 | rdfs:comment "A physical product available in the Acme Shop catalog." ; |
| 327 | rdfs:seeAlso "ttl-uri"><https://schema.org/Product> ; |
| 328 | skos:example "Wireless Mouse Pro, Organic Cotton T-Shirt, Standing Desk" ; |
| 329 | owl:disjointWith b:ServiceItem ; |
| 330 | m:mapsToTable t:products . |
| 331 | |
| 332 | b:ServiceItem a owl:Class ; |
| 333 | rdfs:subClassOf b:Purchasable ; |
| 334 | rdfs:label "Service Item" ; |
| 335 | skos:altLabel "Service", "Add-on" ; |
| 336 | rdfs:comment "A service offering such as a warranty, installation, or subscription that can be purchased alongside or independently of a product." ; |
| 337 | rdfs:seeAlso "ttl-uri"><https://schema.org/Service> ; |
| 338 | skos:example "2-Year Extended Warranty, Professional Installation, Monthly Support Plan" ; |
| 339 | owl:disjointWith b:Product ; |
| 340 | m:mapsToTable t:service_items . |
| 341 | |
| 342 | # --- Subclasses with filters --- |
| 343 | |
| 344 | b:WholesaleCustomer a owl:Class, m:BusinessView ; |
| 345 | rdfs:subClassOf b:Customer ; |
| 346 | rdfs:label "Wholesale Customer" ; |
| 347 | rdfs:comment "Customer purchasing in bulk at wholesale prices. Identified by customer_type = 'wholesale' in the order view." ; |
| 348 | owl:disjointWith b:InternalCustomer ; |
| 349 | m:mapsToTable t:order_summary_view ; |
| 350 | m:joinCondition "order_summary_view.customer_type = 'wholesale'"^^xsd:string . |
| 351 | |
| 352 | b:InternalCustomer a owl:Class, m:BusinessView ; |
| 353 | rdfs:subClassOf b:Customer ; |
| 354 | rdfs:label "Internal Customer" ; |
| 355 | rdfs:comment "Internal staff purchases. Typically excluded from revenue reporting. Identified by customer_type = 'internal'." ; |
| 356 | m:mapsToTable t:order_summary_view ; |
| 357 | m:joinCondition "order_summary_view.customer_type = 'internal'"^^xsd:string . |
| 358 | |
| 359 | b:PremiumWholesaleCustomer a owl:Class, m:BusinessView ; |
| 360 | rdfs:subClassOf b:WholesaleCustomer ; |
| 361 | rdfs:label "Premium Wholesale Customer" ; |
| 362 | rdfs:comment "High-value wholesale customers with premium pricing agreements. Identified by customer_type = 'wholesale' AND customer_segment = 'Premium'." ; |
| 363 | m:mapsToTable t:order_summary_view ; |
| 364 | m:joinCondition "order_summary_view.customer_type = 'wholesale' AND customers.customer_segment = 'Premium'"^^xsd:string . |
| 365 | |
| 366 | b:ReturnedOrder a owl:Class, m:BusinessView ; |
| 367 | rdfs:subClassOf b:Order ; |
| 368 | rdfs:label "Returned Order" ; |
| 369 | rdfs:comment "Orders that were returned or refunded by the customer." ; |
| 370 | owl:disjointWith b:CancelledOrder ; |
| 371 | m:mapsToTable t:order_summary_view ; |
| 372 | m:joinCondition "order_summary_view.order_status = 'returned'"^^xsd:string . |
| 373 | |
| 374 | b:CancelledOrder a owl:Class, m:BusinessView ; |
| 375 | rdfs:subClassOf b:Order ; |
| 376 | rdfs:label "Cancelled Order" ; |
| 377 | rdfs:comment "Orders that were cancelled before fulfillment. NOTE: Mutually exclusive with ReturnedOrder - an order is either cancelled or returned, not both." ; |
| 378 | rdfs:seeAlso b:ReturnedOrder ; |
| 379 | m:mapsToTable t:order_summary_view ; |
| 380 | m:joinCondition "order_summary_view.order_status = 'cancelled'"^^xsd:string . |
| 381 | |
| 382 | # Carrier (range of the bridge-join example). Note: the favorites junction |
| 383 | # table has NO class — a pure junction is modeled as a relationship, not an entity. |
| 384 | b:Carrier a owl:Class ; |
| 385 | rdfs:label "Carrier" ; |
| 386 | rdfs:comment "A shipping carrier that delivers orders." ; |
| 387 | m:mapsToTable t:carriers . |
| 388 | |
| 389 | # =========================================================================== |
| 390 | # BUSINESS LAYER - RELATIONSHIPS |
| 391 | # =========================================================================== |
| 392 | |
| 393 | b:customerHasOrder a owl:ObjectProperty ; |
| 394 | rdfs:domain b:Customer ; |
| 395 | rdfs:range b:Order ; |
| 396 | rdfs:label "customer has order" ; |
| 397 | rdfs:comment "Links a customer to all orders they have placed. Joined via customer_id." ; |
| 398 | owl:inverseOf b:orderForCustomer ; |
| 399 | m:realizedBy "customers.customer_id = order_summary_view.customer_id"^^xsd:string . |
| 400 | |
| 401 | b:orderForCustomer a owl:ObjectProperty ; |
| 402 | rdfs:domain b:Order ; |
| 403 | rdfs:range b:Customer ; |
| 404 | rdfs:label "order for customer" ; |
| 405 | rdfs:comment "Links an order back to the customer who placed it. Inverse of customerHasOrder." ; |
| 406 | owl:inverseOf b:customerHasOrder ; |
| 407 | m:realizedBy "order_summary_view.customer_id = customers.customer_id"^^xsd:string . |
| 408 | |
| 409 | b:orderHasProduct a owl:ObjectProperty ; |
| 410 | rdfs:subPropertyOf b:orderHasPurchasable ; |
| 411 | rdfs:domain b:Order ; |
| 412 | rdfs:range b:Product ; |
| 413 | rdfs:label "order has product" ; |
| 414 | rdfs:comment "Links an order to the physical product purchased. Joined via product_id." ; |
| 415 | owl:inverseOf b:productInOrder ; |
| 416 | m:realizedBy "order_summary_view.product_id = products.product_id"^^xsd:string . |
| 417 | |
| 418 | b:productInOrder a owl:ObjectProperty ; |
| 419 | rdfs:domain b:Product ; |
| 420 | rdfs:range b:Order ; |
| 421 | rdfs:label "product in order" ; |
| 422 | rdfs:comment "Links a product to all orders in which it appears. Inverse of orderHasProduct." ; |
| 423 | owl:inverseOf b:orderHasProduct ; |
| 424 | m:realizedBy "products.product_id = order_summary_view.product_id"^^xsd:string . |
| 425 | |
| 426 | # --- Polymorphic purchasable relationship --- |
| 427 | # orderHasPurchasable accepts any Purchasable subclass (Product or ServiceItem). |
| 428 | # The specific subtype relationships (orderHasProduct, orderHasServiceItem) are |
| 429 | # declared as sub-properties, so navigating orderHasPurchasable returns both. |
| 430 | |
| 431 | b:orderHasPurchasable a owl:ObjectProperty ; |
| 432 | rdfs:domain b:Order ; |
| 433 | rdfs:range b:Purchasable ; |
| 434 | rdfs:label "order has purchasable" ; |
| 435 | rdfs:comment "Links an order to any purchasable item (product or service). This is the polymorphic relationship — use orderHasProduct or orderHasServiceItem for type-specific access. Realized via product_id or service_id depending on the subtype." ; |
| 436 | m:realizedBy "order_summary_view.product_id = products.product_id OR order_summary_view.service_id = service_items.service_id"^^xsd:string . |
| 437 | |
| 438 | b:orderHasServiceItem a owl:ObjectProperty ; |
| 439 | rdfs:subPropertyOf b:orderHasPurchasable ; |
| 440 | rdfs:domain b:Order ; |
| 441 | rdfs:range b:ServiceItem ; |
| 442 | rdfs:label "order has service item" ; |
| 443 | rdfs:comment "Links an order to the service item purchased. Joined via service_id." ; |
| 444 | owl:inverseOf b:serviceItemInOrder ; |
| 445 | m:realizedBy "order_summary_view.service_id = service_items.service_id"^^xsd:string . |
| 446 | |
| 447 | b:serviceItemInOrder a owl:ObjectProperty ; |
| 448 | rdfs:domain b:ServiceItem ; |
| 449 | rdfs:range b:Order ; |
| 450 | rdfs:label "service item in order" ; |
| 451 | rdfs:comment "Links a service item to all orders in which it appears. Inverse of orderHasServiceItem." ; |
| 452 | owl:inverseOf b:orderHasServiceItem ; |
| 453 | m:realizedBy "service_items.service_id = order_summary_view.service_id"^^xsd:string . |
| 454 | |
| 455 | # --- Bridge-table join (ADR-069): Order <-> Carrier through the `shipments` |
| 456 | # table. The m:realizedBy is a single linear equality chain from the domain |
| 457 | # table (order_summary_view) to the range table (carriers); the engine |
| 458 | # auto-joins the intermediate "bridge" table. |
| 459 | b:orderShippedByCarrier a owl:ObjectProperty ; |
| 460 | rdfs:label "shipped by carrier" ; |
| 461 | rdfs:comment "Links an order to the carrier that shipped it, via the shipments bridge table." ; |
| 462 | rdfs:domain b:Order ; |
| 463 | rdfs:range b:Carrier ; |
| 464 | owl:inverseOf b:carrierShippedOrder ; |
| 465 | m:relationshipRole "Transactional" ; |
| 466 | m:realizedBy "order_summary_view.order_id = shipments.order_id AND shipments.carrier_code = carriers.carrier_code"^^xsd:string . |
| 467 | |
| 468 | b:carrierShippedOrder a owl:ObjectProperty ; |
| 469 | rdfs:label "shipped order" ; |
| 470 | rdfs:comment "Links a carrier to the orders it shipped, via the shipments bridge table." ; |
| 471 | rdfs:domain b:Carrier ; |
| 472 | rdfs:range b:Order ; |
| 473 | owl:inverseOf b:orderShippedByCarrier ; |
| 474 | m:relationshipRole "Transactional" ; |
| 475 | m:realizedBy "carriers.carrier_code = shipments.carrier_code AND shipments.order_id = order_summary_view.order_id"^^xsd:string . |
| 476 | |
| 477 | # --- Many-to-many via a pure junction: Customer <-> Product through |
| 478 | # `customer_product_favorites`. The junction carries no payload, so it is |
| 479 | # modeled as a single through-junction Membership relationship (no business |
| 480 | # class for the junction); the engine auto-joins it as a bridge. |
| 481 | b:customerFavoritedProduct a owl:ObjectProperty ; |
| 482 | rdfs:label "favorited product" ; |
| 483 | rdfs:comment "A product the customer has favorited, via the favorites junction." ; |
| 484 | rdfs:domain b:Customer ; |
| 485 | rdfs:range b:Product ; |
| 486 | owl:inverseOf b:productFavoritedByCustomer ; |
| 487 | m:relationshipRole "Membership" ; |
| 488 | m:realizedBy "customers.customer_id = customer_product_favorites.customer_id AND customer_product_favorites.product_id = products.product_id"^^xsd:string . |
| 489 | |
| 490 | b:productFavoritedByCustomer a owl:ObjectProperty ; |
| 491 | rdfs:label "favorited by customer" ; |
| 492 | rdfs:comment "A customer who has favorited the product, via the favorites junction." ; |
| 493 | rdfs:domain b:Product ; |
| 494 | rdfs:range b:Customer ; |
| 495 | owl:inverseOf b:customerFavoritedProduct ; |
| 496 | m:relationshipRole "Membership" ; |
| 497 | m:realizedBy "products.product_id = customer_product_favorites.product_id AND customer_product_favorites.customer_id = customers.customer_id"^^xsd:string . |
| 498 | |
| 499 | # =========================================================================== |
| 500 | # BUSINESS LAYER - PROPERTIES (Dimensions) |
| 501 | # =========================================================================== |
| 502 | |
| 503 | b:orderId a owl:DatatypeProperty ; |
| 504 | rdfs:label "Order ID" ; |
| 505 | rdfs:domain b:Order ; |
| 506 | rdfs:range xsd:string ; |
| 507 | rdfs:comment "Unique order identifier." ; |
| 508 | m:mapsToColumn t:order_summary_view.order_id . |
| 509 | |
| 510 | b:orderDate a owl:DatatypeProperty ; |
| 511 | rdfs:label "Order Date" ; |
| 512 | rdfs:domain b:Order ; |
| 513 | rdfs:range xsd:date ; |
| 514 | rdfs:comment "Date when the order was placed. Primary time dimension for all order-related metrics." ; |
| 515 | m:mapsToColumn t:order_summary_view.order_date . |
| 516 | |
| 517 | b:orderTotal a owl:DatatypeProperty ; |
| 518 | rdfs:label "Order Total" ; |
| 519 | rdfs:domain b:Order ; |
| 520 | rdfs:range xsd:decimal ; |
| 521 | rdfs:comment "Total monetary value of the order. Currency varies (see orderCurrency)." ; |
| 522 | m:mapsToColumn t:order_summary_view.order_total . |
| 523 | |
| 524 | b:orderStatus a owl:DatatypeProperty ; |
| 525 | rdfs:label "Order Status" ; |
| 526 | rdfs:domain b:Order ; |
| 527 | rdfs:range xsd:string ; |
| 528 | rdfs:comment "Current fulfillment status of the order." ; |
| 529 | m:mapsToColumn t:order_summary_view.order_status ; |
| 530 | bv:enumType "dynamic" . |
| 531 | |
| 532 | b:customerCountry a owl:DatatypeProperty ; |
| 533 | rdfs:label "Customer Country" ; |
| 534 | rdfs:domain b:Order ; |
| 535 | rdfs:range xsd:string ; |
| 536 | rdfs:comment "ISO country code of the ordering customer. Only available for customers who have placed orders. See b:customerCountryMaster for the master data version." ; |
| 537 | bv:masterProperty b:customerCountryMaster ; |
| 538 | rdfs:seeAlso b:customerCountryMaster ; |
| 539 | m:mapsToColumn t:order_summary_view.customer_country ; |
| 540 | bv:enumType "country" . |
| 541 | |
| 542 | b:productCategory a owl:DatatypeProperty ; |
| 543 | rdfs:label "Product Category" ; |
| 544 | rdfs:domain b:Order ; |
| 545 | rdfs:range xsd:string ; |
| 546 | rdfs:comment "Category of the ordered product. Only available for products that appear in orders. See b:productCategoryMaster for the master data version." ; |
| 547 | bv:masterProperty b:productCategoryMaster ; |
| 548 | rdfs:seeAlso b:productCategoryMaster ; |
| 549 | m:mapsToColumn t:order_summary_view.product_category ; |
| 550 | bv:enumType "dynamic" . |
| 551 | |
| 552 | b:paymentMethod a owl:DatatypeProperty ; |
| 553 | rdfs:label "Payment Method" ; |
| 554 | rdfs:domain b:Order ; |
| 555 | rdfs:range xsd:string ; |
| 556 | rdfs:comment "Method of payment used for the order." ; |
| 557 | m:mapsToColumn t:order_summary_view.payment_method ; |
| 558 | bv:enumType "dynamic" . |
| 559 | |
| 560 | b:orderCurrency a owl:DatatypeProperty ; |
| 561 | rdfs:label "Order Currency" ; |
| 562 | rdfs:domain b:Order ; |
| 563 | rdfs:range xsd:string ; |
| 564 | rdfs:comment "ISO currency code for the order amount." ; |
| 565 | m:mapsToColumn t:order_summary_view.currency ; |
| 566 | bv:enumType "currency" . |
| 567 | |
| 568 | b:customerType a owl:DatatypeProperty ; |
| 569 | rdfs:label "Customer Type" ; |
| 570 | rdfs:domain b:Order ; |
| 571 | rdfs:range xsd:string ; |
| 572 | rdfs:comment "Classification of the customer. Determines eligibility for reporting (internal/test orders are typically excluded). See b:customerTypeMaster for the master data version." ; |
| 573 | bv:masterProperty b:customerTypeMaster ; |
| 574 | rdfs:seeAlso b:customerTypeMaster ; |
| 575 | m:mapsToColumn t:order_summary_view.customer_type ; |
| 576 | bv:enumType "dynamic" . |
| 577 | |
| 578 | b:customerId a owl:DatatypeProperty ; |
| 579 | rdfs:label "Customer ID" ; |
| 580 | rdfs:domain b:Order ; |
| 581 | rdfs:range xsd:string ; |
| 582 | rdfs:comment "Customer identifier. FK to customers table, available directly in order_summary_view." ; |
| 583 | rdfs:seeAlso b:orderForCustomer ; |
| 584 | m:mapsToColumn t:order_summary_view.customer_id . |
| 585 | |
| 586 | b:orderQuantity a owl:DatatypeProperty ; |
| 587 | rdfs:label "Order Quantity" ; |
| 588 | rdfs:domain b:Order ; |
| 589 | rdfs:range xsd:integer ; |
| 590 | rdfs:comment "Number of items in the order." ; |
| 591 | m:mapsToColumn t:order_summary_view.quantity . |
| 592 | |
| 593 | b:isTestOrder a owl:DatatypeProperty ; |
| 594 | rdfs:label "Is Test Order" ; |
| 595 | rdfs:domain b:Order ; |
| 596 | rdfs:range xsd:boolean ; |
| 597 | rdfs:comment "Boolean flag indicating whether this is a test order. Test orders are excluded from revenue and performance metrics." ; |
| 598 | m:mapsToColumn t:order_summary_view.is_test_order ; |
| 599 | bv:enumType "boolean" . |
| 600 | |
| 601 | b:productId a owl:DatatypeProperty ; |
| 602 | rdfs:label "Product ID" ; |
| 603 | rdfs:domain b:Order ; |
| 604 | rdfs:range xsd:string ; |
| 605 | rdfs:comment "Product identifier. FK to products table, available directly in order_summary_view." ; |
| 606 | rdfs:seeAlso b:orderHasProduct ; |
| 607 | m:mapsToColumn t:order_summary_view.product_id . |
| 608 | |
| 609 | b:productName a owl:DatatypeProperty ; |
| 610 | rdfs:label "Product Name" ; |
| 611 | rdfs:domain b:Product ; |
| 612 | rdfs:range xsd:string ; |
| 613 | rdfs:comment "Display name of the product." ; |
| 614 | m:mapsToColumn t:products.product_name . |
| 615 | |
| 616 | b:productCategoryMaster a owl:DatatypeProperty ; |
| 617 | rdfs:label "Product Category (Master)" ; |
| 618 | rdfs:domain b:Product ; |
| 619 | rdfs:range xsd:string ; |
| 620 | rdfs:comment "Category from the product catalog master table. See also b:productCategory on b:Order for the denormalized version available in order analytics." ; |
| 621 | rdfs:seeAlso b:productCategory ; |
| 622 | m:mapsToColumn t:products.product_category ; |
| 623 | bv:enumType "dynamic" . |
| 624 | |
| 625 | b:productPrice a owl:DatatypeProperty ; |
| 626 | rdfs:label "Product Price" ; |
| 627 | rdfs:domain b:Product ; |
| 628 | rdfs:range xsd:decimal ; |
| 629 | rdfs:comment "Current list price of the product in EUR." ; |
| 630 | m:mapsToColumn t:products.product_price . |
| 631 | |
| 632 | # --- ServiceItem properties --- |
| 633 | |
| 634 | b:serviceId a owl:DatatypeProperty ; |
| 635 | rdfs:label "Service ID" ; |
| 636 | rdfs:domain b:ServiceItem ; |
| 637 | rdfs:range xsd:string ; |
| 638 | rdfs:comment "Unique service item identifier." ; |
| 639 | m:mapsToColumn t:service_items.service_id . |
| 640 | |
| 641 | b:serviceName a owl:DatatypeProperty ; |
| 642 | rdfs:label "Service Name" ; |
| 643 | rdfs:domain b:ServiceItem ; |
| 644 | rdfs:range xsd:string ; |
| 645 | rdfs:comment "Display name of the service item." ; |
| 646 | m:mapsToColumn t:service_items.service_name . |
| 647 | |
| 648 | b:serviceCategory a owl:DatatypeProperty ; |
| 649 | rdfs:label "Service Category" ; |
| 650 | rdfs:domain b:ServiceItem ; |
| 651 | rdfs:range xsd:string ; |
| 652 | rdfs:comment "Category of the service item." ; |
| 653 | m:mapsToColumn t:service_items.service_category ; |
| 654 | bv:enumType "dynamic" . |
| 655 | |
| 656 | b:servicePrice a owl:DatatypeProperty ; |
| 657 | rdfs:label "Service Price" ; |
| 658 | rdfs:domain b:ServiceItem ; |
| 659 | rdfs:range xsd:decimal ; |
| 660 | rdfs:comment "Current price of the service item in EUR." ; |
| 661 | m:mapsToColumn t:service_items.service_price . |
| 662 | |
| 663 | b:customerSegment a owl:DatatypeProperty ; |
| 664 | rdfs:label "Customer Segment" ; |
| 665 | rdfs:domain b:Customer ; |
| 666 | rdfs:range xsd:string ; |
| 667 | rdfs:comment "Customer segment classification from the customer master table." ; |
| 668 | m:mapsToColumn t:customers.customer_segment ; |
| 669 | bv:enumType "dynamic" . |
| 670 | |
| 671 | b:customerName a owl:DatatypeProperty ; |
| 672 | rdfs:label "Customer Name" ; |
| 673 | rdfs:domain b:Customer ; |
| 674 | rdfs:range xsd:string ; |
| 675 | rdfs:comment "Full name of the customer." ; |
| 676 | m:mapsToColumn t:customers.customer_name . |
| 677 | |
| 678 | b:customerEmail a owl:DatatypeProperty ; |
| 679 | rdfs:label "Customer Email" ; |
| 680 | rdfs:domain b:Customer ; |
| 681 | rdfs:range xsd:string ; |
| 682 | rdfs:comment "Primary email address of the customer. Contains PII — access is restricted." ; |
| 683 | bv:sensitivityLevel "Confidential" ; |
| 684 | bv:complianceCategory "PII" ; |
| 685 | bv:maskingRule "redact" ; |
| 686 | m:mapsToColumn t:customers.customer_email . |
| 687 | |
| 688 | b:customerCountryMaster a owl:DatatypeProperty ; |
| 689 | rdfs:label "Customer Country (Master)" ; |
| 690 | rdfs:domain b:Customer ; |
| 691 | rdfs:range xsd:string ; |
| 692 | rdfs:comment "ISO country code from the customer master table. See also b:customerCountry on b:Order for the denormalized version available in order analytics." ; |
| 693 | rdfs:seeAlso b:customerCountry ; |
| 694 | m:mapsToColumn t:customers.customer_country ; |
| 695 | bv:enumType "dynamic" . |
| 696 | |
| 697 | b:customerTypeMaster a owl:DatatypeProperty ; |
| 698 | rdfs:label "Customer Type (Master)" ; |
| 699 | rdfs:domain b:Customer ; |
| 700 | rdfs:range xsd:string ; |
| 701 | rdfs:comment "Customer classification from the master table. See also b:customerType on b:Order for the denormalized version available in order analytics." ; |
| 702 | rdfs:seeAlso b:customerType ; |
| 703 | m:mapsToColumn t:customers.customer_type ; |
| 704 | bv:enumType "dynamic" . |
| 705 | |
| 706 | b:customerCreatedAt a owl:DatatypeProperty ; |
| 707 | rdfs:label "Customer Registration Date" ; |
| 708 | rdfs:domain b:Customer ; |
| 709 | rdfs:range xsd:dateTime ; |
| 710 | rdfs:comment "Timestamp when the customer registered." ; |
| 711 | m:mapsToColumn t:customers.created_at . |
| 712 | |
| 713 | # --- Carrier properties --- |
| 714 | b:carrierName a owl:DatatypeProperty ; |
| 715 | rdfs:label "Carrier Name" ; |
| 716 | rdfs:domain b:Carrier ; |
| 717 | rdfs:range xsd:string ; |
| 718 | rdfs:comment "The carrier's display name." ; |
| 719 | m:mapsToColumn t:carriers.carrier_name . |
| 720 | |
| 721 | # =========================================================================== |
| 722 | # BUSINESS LAYER - CONSTANTS |
| 723 | # =========================================================================== |
| 724 | |
| 725 | b:gbpToEurExchangeRate a bv:BusinessConstant ; |
| 726 | rdfs:label "GBP to EUR Exchange Rate" ; |
| 727 | rdfs:comment "Fixed exchange rate for converting GBP to EUR. Rate: 1 GBP = 0.85 EUR. To convert GBP amounts to EUR, divide by 0.85. This value is inlined in metric expressions." ; |
| 728 | bv:constantValue "0.85"^^xsd:decimal . |
| 729 | |
| 730 | # =========================================================================== |
| 731 | # ACCESS CONTROL |
| 732 | # =========================================================================== |
| 733 | |
| 734 | # Restricts revenue-bearing metrics to admin role and finance group. |
| 735 | # Applied below to b:totalRevenueMetric and b:revenuePerCustomerSegmentMetric. |
| 736 | b:revenueAccessPolicy a bv:AccessPolicy ; |
| 737 | rdfs:label "Revenue access policy" ; |
| 738 | rdfs:comment "Only admins and the finance group may query revenue-bearing metrics." ; |
| 739 | bv:allowRole "admin" ; |
| 740 | bv:allowGroup "finance" . |
| 741 | |
| 742 | # Row-level filter on order_summary_view: a non-admin user only sees |
| 743 | # orders whose customer_country is in their {{user.region}} attribute. |
| 744 | # Admin role is exempt. Applied to all order-sourced metrics below. |
| 745 | b:customerCountryRowFilter a bv:RowFilter ; |
| 746 | rdfs:label "Customer country row filter" ; |
| 747 | rdfs:comment "Per-user region restriction on Order rows. Non-admin callers only see orders whose customer_country is in their {{user.region}} attribute." ; |
| 748 | bv:filterExpression "customer_country IN ({{user.region}})" ; |
| 749 | bv:exemptRole "admin" . |
| 750 | |
| 751 | # =========================================================================== |
| 752 | # BUSINESS LAYER - METRICS (with structured properties) |
| 753 | # =========================================================================== |
| 754 | |
| 755 | b:totalRevenueMetric a bv:Metric ; |
| 756 | rdfs:label "Total Revenue" ; |
| 757 | skos:altLabel "Gross Revenue", "Total Sales" ; |
| 758 | rdfs:comment "Total order revenue with automatic GBP to EUR conversion. Includes ALL order types and statuses. For order count see b:orderCountMetric; for average value see b:averageOrderValueMetric." ; |
| 759 | rdfs:seeAlso b:orderCountMetric, b:averageOrderValueMetric ; |
| 760 | bv:metricExpression "SUM(CASE WHEN b:orderCurrency = 'GBP' THEN b:orderTotal / {{gbpToEurExchangeRate}} ELSE b:orderTotal END)" ; |
| 761 | bv:sourceEntity b:Order ; |
| 762 | bv:timeDimension b:orderDate ; |
| 763 | bv:dependsOnProperty b:orderTotal, b:orderCurrency ; |
| 764 | bv:dependsOnConstant b:gbpToEurExchangeRate ; |
| 765 | bv:metricCategory "Revenue" ; |
| 766 | bv:accessPolicy b:revenueAccessPolicy ; |
| 767 | bv:rowFilter b:customerCountryRowFilter . |
| 768 | |
| 769 | b:orderCountMetric a bv:Metric ; |
| 770 | rdfs:label "Order Count" ; |
| 771 | skos:altLabel "Number of Orders", "Total Orders" ; |
| 772 | rdfs:comment "Total number of orders placed. Counts all orders regardless of status. For revenue see b:totalRevenueMetric." ; |
| 773 | rdfs:seeAlso b:totalRevenueMetric ; |
| 774 | bv:metricExpression "COUNT(*)" ; |
| 775 | bv:sourceEntity b:Order ; |
| 776 | bv:timeDimension b:orderDate ; |
| 777 | bv:dependsOnProperty b:orderId ; |
| 778 | bv:metricCategory "Orders" ; |
| 779 | bv:rowFilter b:customerCountryRowFilter . |
| 780 | |
| 781 | b:averageOrderValueMetric a bv:Metric ; |
| 782 | rdfs:label "Average Order Value" ; |
| 783 | skos:altLabel "AOV", "Avg Order Value" ; |
| 784 | rdfs:comment "Average revenue per order. Equivalent to total_revenue / order_count." ; |
| 785 | rdfs:seeAlso b:totalRevenueMetric, b:orderCountMetric ; |
| 786 | bv:metricExpression "AVG(b:orderTotal)" ; |
| 787 | bv:sourceEntity b:Order ; |
| 788 | bv:timeDimension b:orderDate ; |
| 789 | bv:dependsOnProperty b:orderTotal ; |
| 790 | bv:metricCategory "Revenue" ; |
| 791 | bv:rowFilter b:customerCountryRowFilter . |
| 792 | |
| 793 | b:returnRateMetric a bv:Metric ; |
| 794 | rdfs:label "Return Rate" ; |
| 795 | skos:altLabel "Return %", "Refund Rate" ; |
| 796 | rdfs:comment "Percentage of orders that were returned or refunded, excluding test orders and internal staff purchases. Formula: returned_orders / total_orders * 100. A value of 5% means one in twenty orders was returned." ; |
| 797 | bv:metricExpression """100.0 * SUM(CASE WHEN b:orderStatus = 'returned' THEN 1 ELSE 0 END) |
| 798 | / NULLIF(COUNT(*), 0)""" ; |
| 799 | bv:sourceEntity b:Order ; |
| 800 | bv:metricPreFilter "b:customerType <> 'internal' AND b:isTestOrder = false" ; |
| 801 | bv:timeDimension b:orderDate ; |
| 802 | bv:dependsOnProperty b:orderStatus, b:customerType, b:isTestOrder ; |
| 803 | bv:metricCategory "Orders" ; |
| 804 | bv:rowFilter b:customerCountryRowFilter . |
| 805 | |
| 806 | b:uniqueCustomersMetric a bv:Metric ; |
| 807 | rdfs:label "Unique Customers" ; |
| 808 | skos:altLabel "Distinct Customers", "Active Buyers" ; |
| 809 | rdfs:comment "Number of distinct customers who placed orders in the period. NOTE: This is an ACTIVITY-BASED count - only customers with at least one order are counted. This is NOT a master data count of all registered customers." ; |
| 810 | bv:metricExpression "COUNT(DISTINCT b:customerId)" ; |
| 811 | bv:sourceEntity b:Order ; |
| 812 | bv:timeDimension b:orderDate ; |
| 813 | bv:dependsOnProperty b:customerId ; |
| 814 | bv:metricCategory "Customers" ; |
| 815 | bv:rowFilter b:customerCountryRowFilter . |
| 816 | |
| 817 | b:totalItemsSoldMetric a bv:Metric ; |
| 818 | rdfs:label "Total Items Sold" ; |
| 819 | skos:altLabel "Units Sold", "Total Quantity" ; |
| 820 | rdfs:comment "Total number of items sold across all orders." ; |
| 821 | bv:metricExpression "SUM(b:orderQuantity)" ; |
| 822 | bv:sourceEntity b:Order ; |
| 823 | bv:timeDimension b:orderDate ; |
| 824 | bv:dependsOnProperty b:orderQuantity ; |
| 825 | bv:metricCategory "Orders" ; |
| 826 | bv:rowFilter b:customerCountryRowFilter . |
| 827 | |
| 828 | # Multi-table metric using SQL template escape hatch |
| 829 | b:revenuePerCustomerSegmentMetric a bv:Metric ; |
| 830 | rdfs:label "Revenue Per Customer Segment" ; |
| 831 | rdfs:comment "Average revenue per customer, broken down by customer segment. Requires joining orders with customer master data. For total revenue without segmentation, see b:totalRevenueMetric." ; |
| 832 | rdfs:seeAlso b:totalRevenueMetric ; |
| 833 | bv:metricSql """ |
| 834 | SELECT DATE_TRUNC('{{granularity}}', order_summary_view.order_date) AS time_dimension |
| 835 | {{select_dimensions}}, |
| 836 | CASE WHEN COUNT(DISTINCT customers.customer_id) > 0 |
| 837 | THEN SUM(order_summary_view.order_total) / COUNT(DISTINCT customers.customer_id) |
| 838 | ELSE 0 END AS metric_value |
| 839 | FROM order_summary_view |
| 840 | INNER JOIN customers ON order_summary_view.customer_id = customers.customer_id |
| 841 | {{join_clauses}} |
| 842 | WHERE order_summary_view.order_date >= '{{start_date}}' AND order_summary_view.order_date < '{{end_date}}' |
| 843 | {{where_filters}} |
| 844 | GROUP BY 1 {{group_by_dimensions}} |
| 845 | ORDER BY 1 |
| 846 | """ ; |
| 847 | bv:sourceEntity b:Order ; |
| 848 | bv:timeDimension b:orderDate ; |
| 849 | bv:dependsOnProperty b:orderTotal, b:customerId, b:customerSegment ; |
| 850 | bv:dependsOnRelationship b:customerHasOrder ; |
| 851 | bv:metricCategory "Revenue" ; |
| 852 | bv:accessPolicy b:revenueAccessPolicy ; |
| 853 | bv:rowFilter b:customerCountryRowFilter . |
| 854 | |
| 855 | b:newCustomerRegistrationsMetric a bv:Metric ; |
| 856 | rdfs:label "New Customer Registrations" ; |
| 857 | skos:altLabel "New Signups", "Customer Registrations" ; |
| 858 | rdfs:comment "Number of new customer registrations per period. Excludes internal (staff) customers. For activity-based customer counts (customers who placed orders), see b:uniqueCustomersMetric." ; |
| 859 | rdfs:seeAlso b:uniqueCustomersMetric ; |
| 860 | bv:metricExpression "COUNT(*)" ; |
| 861 | bv:sourceEntity b:Customer ; |
| 862 | bv:metricPreFilter "b:customerTypeMaster <> 'internal'" ; |
| 863 | bv:timeDimension b:customerCreatedAt ; |
| 864 | bv:dependsOnProperty b:customerTypeMaster ; |
| 865 | bv:metricCategory "Customers" . |
| 866 | |
| 867 | # ================================================================ |
| 868 | # CORRELATIONS — How metrics relate to each other |
| 869 | # ================================================================ |
| 870 | |
| 871 | # Statistical: new customer registrations correlate with return rate |
| 872 | b:newRegistrations_correlates_returnRate a bv:MetricCorrelation ; |
| 873 | rdfs:label "New Registrations ↔ Return Rate" ; |
| 874 | rdfs:comment "New customers return items at higher rates than repeat buyers — unfamiliar with sizing and product quality. The effect flattens at high registration volumes." ; |
| 875 | bv:sourceMetric b:newCustomerRegistrationsMetric ; |
| 876 | bv:targetMetric b:returnRateMetric ; |
| 877 | bv:correlationType "statistical" ; |
| 878 | bv:correlationStrength "0.62" ; |
| 879 | bv:correlationCurve "logarithmic" . |
| 880 | |
| 881 | # Causal: high return rates drive down average order value |
| 882 | b:returnRate_drives_averageOrderValue a bv:MetricCorrelation ; |
| 883 | rdfs:label "Return Rate → AOV" ; |
| 884 | rdfs:comment "Higher return rates tend to reduce effective AOV as returned items deflate averages." ; |
| 885 | bv:sourceMetric b:returnRateMetric ; |
| 886 | bv:targetMetric b:averageOrderValueMetric ; |
| 887 | bv:correlationType "causal" ; |
| 888 | bv:correlationStrength "moderate" ; |
| 889 | bv:correlationCurve "inverse" . |
| 890 |
Connect your data, define metrics, and start asking questions in plain English.
Get Started Free