# Model A - Single Product A key factor that you need to consider when deciding how to configure your product data model is to define how your product will be displayed on the Product detail, Product listing, and Search results pages. These decisions will impact the Product Type you create along with the Products and Product Variants. We will explore this process as part of this module on advanced product data modeling, and explain various solutions depending on different business requirements. We will use the iPhone 16 Product as a case study to learn about these decisions in more detail. In addition, we will analyze various iPhone 16 models to understand how to configure advanced product data modeling in commercetools for different use cases. The iPhone has a hierarchical structure of variations, starting with the base model and progressing through increasingly specific attributes: - Base model: iPhone 16 - Model variant: Base (6.1 inch) - colors (5) - storage (3) - Model variant: Plus (6.7 inch) - colors (5) - storage (3) - Model variant: Pro (6.3 inch) - colors (4) - storage (4) - Model variant: Max (6.9 inch) - colors (4) - storage (3) Now let’s explore how we can model the iPhone 16 Product in commercetools. ## Model A: Single product listing with full variant display Our first product data model needs to meet the following requirements: - **Product listing page**: display as a single Product. ![Model A product listing page.](https://docs.commercetools.com/learning-model-your-product-catalog/images/advanced-product-data-modeling/model-a-product-listing-page.png) - **Search result**: display as a single Product. ![Model A search results page.](https://docs.commercetools.com/learning-model-your-product-catalog/images/advanced-product-data-modeling/model-a-search-results-page.png) - **Product detail page**: display all Product combinations. ![Model A product detail page.](https://docs.commercetools.com/learning-model-your-product-catalog/images/advanced-product-data-modeling/model-a-product-detail-page.png) ## Set up Product data model in commercetools To fulfill the requirements of setting up our product data model in commercetools, we need to configure the following settings and understand the resulting impact: - **Number of Products**: 1 - **Number of URLs**: 1 - **Number of Product Variants per Product**: 58 - **Price display**: We will need to use a price range on the Product card (showing the lowest and highest price across all variants $1,399.00 - 2,849.00 AUD). We can meet the above requirements by creating a Product Type with the following listed Attributes: ### Product Type and Attributes summary | Attribute | Label | Type | Required | Searchable | Possible Values | | :--- | :--- | :--- | :--- | :--- | :--- | | color | color | enum | True | True | Ultramarine, Teal, Pink, White, Black, Black Titanium, White Titanium, Natural Titanium, Desert Titanium | | storage | storage | number | True | True | 128000, 256000, 512000, 1000000 | | display | Display Size | enum | True | True | 6.1, 6.3, 6.7, 6.9 | | display-type | Display Type | enum | False | False | Super Retina XDR | | promotion | ProMotion technology | boolean | False | True | | | always-on-display | Always-On Display | boolean | False | True | | | processor | Processor | enum | False | False | A18, A18 Pro | | gpu-cores | GPU Cores | number | False | False | | | camera-control | Camera Control | boolean | False | False | | | camera-system | Camera System | enum | False | False | Advanced dual, Pro | | main-camera | Main Camera | enum | False | False | 48MP Fusion | | telephoto | Telephoto | number | False | False | | | megapixels | Megapixels | number | False | False | | | emergency-sos | Emergency SOS | boolean | False | False | | | emergency-satellite-sos | Emergency Satellite SOS | boolean | False | False | | | video-playback | Video Playback | text | False | False | | | usb-version | USB Version | enum | False | False | 2, 3 | | cellular | Cellular | enum | False | False | 5G | | model | Model | enum | False | False | Base, Plus, Pro, Max | ### Product Type JSON The Product Type JSON object is shown here. You can use this JSON object in your own Project to create the same Product Type. ```json { "id": "7bf93cac-94ed-44ad-b8c0-91fb67103f42", "version": 1, "createdAt": "2024-10-30T02:18:49.579Z", "lastModifiedAt": "2024-10-30T02:18:49.579Z", "lastModifiedBy": { "clientId": "ViXgKzquNw-cXDYLmXagUH3Q", "isPlatformClient": false }, "createdBy": { "clientId": "ViXgKzquNw-cXDYLmXagUH3Q", "isPlatformClient": false }, "name": "iphone-a", "description": "iphone-a", "classifier": "Complex", "attributes": [ { "name": "color", "label": { "en-AU": "color", "en-NZ": "", "zh": "" }, "inputTip": { "en-AU": "", "en-NZ": "", "zh": "" }, "isRequired": true, "type": { "name": "enum", "values": [ { "key": "ultramarine", "label": "Ultramarine" }, { "key": "teal", "label": "Teal" }, { "key": "pink", "label": "Pink" }, { "key": "white", "label": "White" }, { "key": "black", "label": "Black" }, { "key": "black-titanium", "label": "Black Titanium" }, { "key": "white-titanium", "label": "White Titanium" }, { "key": "natural-titanium", "label": "Natural Titanium" }, { "key": "desert-titanium", "label": "Desert Titanium" } ] }, "attributeConstraint": "CombinationUnique", "isSearchable": true, "inputHint": "SingleLine", "displayGroup": "Other" }, { "name": "storage", "label": { "en-AU": "storage", "en-NZ": "", "zh": "" }, "inputTip": { "en-AU": "", "en-NZ": "", "zh": "" }, "isRequired": true, "type": { "name": "number" }, "attributeConstraint": "CombinationUnique", "isSearchable": true, "inputHint": "SingleLine", "displayGroup": "Other" }, { "name": "display", "label": { "en-AU": "Display Size", "en-NZ": "", "zh": "" }, "inputTip": { "en-AU": "", "en-NZ": "", "zh": "" }, "isRequired": true, "type": { "name": "enum", "values": [ { "key": "6.1", "label": "6.1" }, { "key": "6.3", "label": "6.3" }, { "key": "6.7", "label": "6.7" }, { "key": "6.9", "label": "6.9" } ] }, "attributeConstraint": "CombinationUnique", "isSearchable": true, "inputHint": "SingleLine", "displayGroup": "Other" }, { "name": "display-type", "label": { "en-AU": "Display Type", "en-NZ": "", "zh": "" }, "inputTip": { "en-AU": "", "en-NZ": "", "zh": "" }, "isRequired": false, "type": { "name": "enum", "values": [ { "key": "super-retina-xdr", "label": "Super Retina XDR" } ] }, "attributeConstraint": "None", "isSearchable": false, "inputHint": "SingleLine", "displayGroup": "Other" }, { "name": "promotion", "label": { "en-AU": "ProMotion technology", "en-NZ": "", "zh": "" }, "inputTip": { "en-AU": "", "en-NZ": "", "zh": "" }, "isRequired": false, "type": { "name": "boolean" }, "attributeConstraint": "None", "isSearchable": true, "inputHint": "SingleLine", "displayGroup": "Other" }, { "name": "always-on-display", "label": { "en-AU": "Always-On Display", "en-NZ": "", "zh": "" }, "inputTip": { "en-AU": "", "en-NZ": "", "zh": "" }, "isRequired": false, "type": { "name": "boolean" }, "attributeConstraint": "None", "isSearchable": true, "inputHint": "SingleLine", "displayGroup": "Other" }, { "name": "processor", "label": { "en-AU": "Processor", "en-NZ": "", "zh": "" }, "inputTip": { "en-AU": "", "en-NZ": "", "zh": "" }, "isRequired": false, "type": { "name": "enum", "values": [ { "key": "a18", "label": "A18" }, { "key": "a18-pro", "label": "A18 Pro" } ] }, "attributeConstraint": "None", "isSearchable": false, "inputHint": "SingleLine", "displayGroup": "Other" }, { "name": "gpu-cores", "label": { "en-AU": "GPU Cores", "en-NZ": "", "zh": "" }, "inputTip": { "en-AU": "", "en-NZ": "", "zh": "" }, "isRequired": false, "type": { "name": "number" }, "attributeConstraint": "None", "isSearchable": false, "inputHint": "SingleLine", "displayGroup": "Other" }, { "name": "camera-control", "label": { "en-AU": "Camera Control", "en-NZ": "", "zh": "" }, "inputTip": { "en-AU": "", "en-NZ": "", "zh": "" }, "isRequired": false, "type": { "name": "boolean" }, "attributeConstraint": "None", "isSearchable": false, "inputHint": "SingleLine", "displayGroup": "Other" }, { "name": "camera-system", "label": { "en-AU": "Camera System", "en-NZ": "", "zh": "" }, "inputTip": { "en-AU": "", "en-NZ": "", "zh": "" }, "isRequired": false, "type": { "name": "enum", "values": [ { "key": "advanced-dual", "label": "Advanced dual" }, { "key": "pro", "label": "Pro" } ] }, "attributeConstraint": "None", "isSearchable": false, "inputHint": "SingleLine", "displayGroup": "Other" }, { "name": "main-camera", "label": { "en-AU": "Main Camera", "en-NZ": "", "zh": "" }, "inputTip": { "en-AU": "", "en-NZ": "", "zh": "" }, "isRequired": false, "type": { "name": "enum", "values": [ { "key": "48mp-fusion", "label": "48MP Fusion" } ] }, "attributeConstraint": "None", "isSearchable": false, "inputHint": "SingleLine", "displayGroup": "Other" }, { "name": "telephoto", "label": { "en-AU": "Telephoto", "en-NZ": "", "zh": "" }, "inputTip": { "en-AU": "", "en-NZ": "", "zh": "" }, "isRequired": false, "type": { "name": "number" }, "attributeConstraint": "None", "isSearchable": false, "inputHint": "SingleLine", "displayGroup": "Other" }, { "name": "megapixels", "label": { "en-AU": "Megapixels", "en-NZ": "", "zh": "" }, "inputTip": { "en-AU": "", "en-NZ": "", "zh": "" }, "isRequired": false, "type": { "name": "number" }, "attributeConstraint": "None", "isSearchable": false, "inputHint": "SingleLine", "displayGroup": "Other" }, { "name": "emergency-sos", "label": { "en-AU": "Emergency SOS", "en-NZ": "", "zh": "" }, "inputTip": { "en-AU": "", "en-NZ": "", "zh": "" }, "isRequired": false, "type": { "name": "boolean" }, "attributeConstraint": "None", "isSearchable": false, "inputHint": "SingleLine", "displayGroup": "Other" }, { "name": "emergency-satellite-sos", "label": { "en-AU": "Emergency Satellite SOS", "en-NZ": "", "zh": "" }, "inputTip": { "en-AU": "", "en-NZ": "", "zh": "" }, "isRequired": false, "type": { "name": "boolean" }, "attributeConstraint": "None", "isSearchable": false, "inputHint": "SingleLine", "displayGroup": "Other" }, { "name": "video-playback", "label": { "en-AU": "Video Playback", "en-NZ": "", "zh": "" }, "inputTip": { "en-AU": "", "en-NZ": "", "zh": "" }, "isRequired": false, "type": { "name": "text" }, "attributeConstraint": "None", "isSearchable": false, "inputHint": "SingleLine", "displayGroup": "Other" }, { "name": "usb-version", "label": { "en-AU": "USB Version", "en-NZ": "", "zh": "" }, "inputTip": { "en-AU": "", "en-NZ": "", "zh": "" }, "isRequired": false, "type": { "name": "enum", "values": [ { "key": "2", "label": "2" }, { "key": "3", "label": "3" } ] }, "attributeConstraint": "None", "isSearchable": false, "inputHint": "SingleLine", "displayGroup": "Other" }, { "name": "cellular", "label": { "en-AU": "Cellular", "en-NZ": "", "zh": "" }, "inputTip": { "en-AU": "", "en-NZ": "", "zh": "" }, "isRequired": false, "type": { "name": "enum", "values": [ { "key": "5g", "label": "5G" } ] }, "attributeConstraint": "None", "isSearchable": false, "inputHint": "SingleLine", "displayGroup": "Other" }, { "name": "model", "label": { "en-AU": "Model", "en-NZ": "", "zh": "" }, "inputTip": { "en-AU": "", "en-NZ": "", "zh": "" }, "isRequired": false, "type": { "name": "enum", "values": [ { "key": "base", "label": "Base" }, { "key": "plus", "label": "Plus" }, { "key": "pro", "label": "Pro" }, { "key": "max", "label": "Max" } ] }, "attributeConstraint": "None", "isSearchable": false, "inputHint": "SingleLine", "displayGroup": "Other" } ], "key": "iphone-a" } ``` ### iPhone 16 Product JSON example Here's the JSON of the Product that we need to create in our module: [iPhone 16 Product JSON](/learning-model-your-product-catalog/downloads/product-iphone-a.json). - **Total**: 252 KB - **Per Projection (each Staged and Current)**: 126 KB each - **Variants**: 124 KB - **Attributes**: 48.7 KB - **Assets**: 50.4 KB - **Prices**: 20.0 KB ### Attribute decisions In this use case, we have chosen the `storage` capacity to be modeled by using a `number` attribute with a consistent unit of measure, specifically megabytes (MB). While iPhones offer storage options such as 128 GB, 256 GB, 512 GB, and 1 TB, storing these values as their megabyte equivalents (for example, 128000 MB, 256000 MB, 512000 MB, and 1024000 MB) provides us greater flexibility in filter and query operations. This approach enables range-based searches, which help to filter products on Product listing pages and search results. For example, a customer could search for devices with `storage greater than 200000 MB` (effectively 200 GB), which wouldn't be possible with mixed units like GB and TB. To ensure accurate comparisons and to simplify queries, all storage values in this project are measured in megabytes (MB) - the smallest common unit. While the underlying data uses MB, you can easily and dynamically convert the displayed values for customers to more user-friendly units like GB or TB at the time of presentation. This provides a seamless customer experience while maintaining the backend's query capabilities. You can manage this conversion either in the frontend application logic, BFF, or some other layer. Alternatively, you could use a separate attribute to determine the displayed value, while still using storage for query operations. We have used a similar strategy for `megapixels`, `telephoto`, and `gpu-cores`; as a result, they are of type `number`. `usb-version` is of type `enum` because neither does the usb standard follow a strict numerical naming convention nor does it follow a clear naming convention. For example, USB 2.0, USB 3.0, USB 3.1 Gen 2, USB4, and USB4 Version 2.0 are all valid versions. This field could also be of type `text`, but the benefit of using `enum` is that we are ensuring that only valid values are used for the product. This is useful if Product data is updated by personnel across the Merchant Center. We have configured `cellular` and `display` in the same manner for similar reasons. We use enum values for `display-type`, `model`, `color`, `processor`, `main-camera`, and `camera-system` to ensure data integrity. Although `display` represents screen size in inches, it is also an enum. This helps include foldable phones with two screen sizes, storing both within a single attribute for simplified display. If filtering by screen size becomes necessary, we can refactor the display into separate attributes for standard and foldable phones. `promotion`, `always-on-display`, `camera-control`, `emergency-sos`, and `emergency-satellite-sos` are all `boolean` values. Note that `display` is an enum field that stores number values. In this use case, it is not required to query this field with the same level of flexibility as is necessary for `storage`. We can meet these requirements by using either type `number` or `enum`. `video-playback` is of type `text` because there can be large variations in the string values. For example, up to 33 hours, 16 hours of video playback, and 20 hours of high power mode. ### Attribute constraints A specific Product Variant is a combination of `color`, `storage`, and `display`. These are the important differentiating attributes for Product Variants. As a result, each Product Variant contains the `CombinationUnique` constraint. ### Attribute setting: Searchable You might have noticed in the earlier sections that we have made only a few of the Attributes searchable. This is to ensure: **Searchable attribute optimization**: marking only essential attributes as [`isSearchable`](/api/projects/productTypes.md#attributedefinition) improves the speed of Product Search indexing. The `isSearchable` setting only affects the [Product Search](/api/projects/product-search.md) and [Product Projection Search](/api/projects/product-projection-search.md) endpoints, not the Products or Product Projections endpoints. Note: For optimal performance with large or low-latency Product or Product Projection queries, refer to our [indexes and query optimization guide](/learning-developer-essentials/api-queries-query-predicates/improve-query-performance.md). ### External Search vs. Product Search External search engines can provide more control and flexibility over data management and querying as compared with in-built product search solutions. This advantage is due to the ability of external search engines to customize how your data is structured and indexed. For instance, a single iPhone 16 Product representing all variations can be broken down into searchable entities by model (Pro, Pro Max), color (Space Black, Silver), storage (128 GB, 256 GB), or any other combination. This granular approach enables more precise control over the Product cards that are displayed on the search and category pages. Instead of a single iPhone 16 search result, users can view distinct entries for each model (such as iPhone 16 Pro and iPhone 16 Pro Max) on the Search results and Product listing pages. Conversely, you can aggregate disparate iPhone 16 product data in an external search engine. Instead of indexing 18 separate products (one for each model-storage combination), you could consolidate them into a single record or four model-specific records. In addition, you can control the data synced to an external search engine to minimize the number of synchronized fields and reduce the query payload size, thereby improving the search performance. ## Related pages - [Area overview page with navigation](/learning-model-your-product-catalog.md) - [Previous page: Overview](/learning-model-your-product-catalog/advanced-product-data-modeling/overview.md) - [Next page: Model B - Product displayed by model](/learning-model-your-product-catalog/advanced-product-data-modeling/model-b.md)