Concepts: Composite vs Flat Keys
Relevant source files
The following files were used as context for generating this wiki page:
The @cellarnode/beverage-utils library provides a specialized system for mapping beverage classification identifiers (slugs) to human-readable names. The core architectural challenge addressed is the reuse of subtype IDs across different beverage categories. For example, the ID red might refer to "Red Wine" under the "Still Wine" category, but "Red Sparkling" under the "Sparkling Wine" category.
To resolve these ambiguities, the system generates a LabelMap containing both flat keys (simple IDs) and composite keys (category-prefixed IDs). This ensures that developers can perform global lookups when appropriate while maintaining the ability to perform precise, context-aware lookups when the beverage category is known.
Sources: README.md:27-32, src/label-map.ts
Architecture of the Label Map
The LabelMap is a flat object where keys represent IDs and values represent their corresponding human-readable labels. It is generated from a BeverageClassification object, which contains a nested hierarchy of categories and subtypes.
Key Types in the Map
- Category Keys: The
idof the category itself (e.g.,wine). - Flat Subtype Keys: The
idof a subtype (e.g.,red). In cases of duplicates across categories, the "last-write-wins" strategy applies. - Composite Keys: A string combination of the category ID and subtype ID, separated by a colon (e.g.,
wine:red).
Sources: src/label-map.ts:5-15, README.md:36-42
Logic Flow for Map Generation
The following diagram illustrates how buildLabelMap iterates through the classification data to populate the three key types:
This flow ensures that every subtype is accessible via both its standalone ID and its category-qualified ID.
Sources: src/label-map.ts:6-14
Disambiguation and Conflict Resolution
The primary purpose of composite keys is disambiguation. Because different categories may use identical subtype IDs, a flat key lookup can be unreliable.
| Feature | Logic | Example |
|---|---|---|
| Flat Key | Uses the subtype ID directly. Overwritten if multiple categories use the same ID. | "red" -> "Red Sparkling" (if Sparkling was processed last) |
| Composite Key | Combines category and subtype. Guaranteed to be unique within the classification. | "wine:red" -> "Red Wine" |
| Category Key | Uses the top-level category ID. | "wine" -> "Still Wine" |
Sources: README.md:36-50, tests/label-map.test.ts:25-36
Data Structure Relationship
The relationship between the source BeverageClassification and the resulting LabelMap is defined by the underlying interfaces:
Sources: src/types.ts:1-15, src/label-map.ts:3-5
Implementation in Formatting Utilities
The formatting functions utilize these keys to provide flexible display options.
formatBeverageType Logic
The formatBeverageType function specifically leverages composite keys to ensure the correct subtype label is displayed based on the provided category.
export function formatBeverageType(
category: string,
subtype?: string | null,
map?: LabelMap,
): string {
const cat = formatBeverageLabel(category, map);
if (!subtype) return cat;
const compositeKey = `${category}:${subtype}`;
const sub = map?.[compositeKey]
? map[compositeKey]
: formatBeverageLabel(subtype, map);
return `${cat} / ${sub}`;
}
Sources: src/format.ts:12-23
Resolution Priority
When formatting a beverage type with a map provided, the system follows this priority for the subtype portion:
- Composite Key Match: Checks for
category:subtypein the map. - Flat Key Match: Checks for
subtypein the map. - Humanization Fallback: If no map match is found, it converts the slug (e.g.,
snake_case) into Title Case.
Sources: src/format.ts:4-23, tests/format.test.ts:34-45
Conclusion
The distinction between composite and flat keys allows the CellarNode classification system to maintain a user-friendly API while managing technical overlaps in beverage industry data. By providing category-prefixed keys, the library ensures that subtypes like "Red" or "White" are always correctly identified within their parent category context.
Sources: README.md:27-33, src/label-map.ts:9-12