NAME

AmberDB::Index::Facet - Column-oriented facet indexing, disjunctive counting, and navigation menu generator

SYNOPSIS

# Querying from AmberDB instance ($adb inherits AmberDB::Index::Facet):

# 1. Generate full-catalog or filtered facet menu with disjunctive counts:
my $menu_data = $adb->facet_menu(
    "catalog_product",
    { 1 => "5", 2 => [ "12", "14" ] }, # %selected_filters
    \@facet_block_definitions,
    { sort => 'count', top => 10 }      # %options
);

# 2. Dynamic Scoped facet menu (e.g. within search results or category scope):
my $search_facets = $adb->facet_menu(
    "catalog_product",
    \%selected,
    \@facet_defs,
    { base_ids => \@search_result_ids }
);

# 3. Direct facet key counts for a single block:
my $counts = $adb->field_fltkeys("catalog_product", {
    target_block => 2,
    base_ids     => \@active_product_ids,
});

DESCRIPTION

AmberDB::Index::Facet provides a high-performance, column-oriented forward indexing and disjunctive facet aggregation engine designed for low-latency faceted navigation across large-scale catalogs.

Inheritance Note: AmberDB inherits from AmberDB::Index::Facet via use parent. All facet query and menu methods documented below are invoked directly on $adb.

KEY ARCHITECTURAL FEATURES

  • 1. Columnar Per-Block Storage (_${blk}.fac): Facet data is stored in partitioned columnar forward index files (${table_path}_${blk}.fac). Each file maps Record ID to packed value IDs, enabling fast single-column scans.

  • 2. Active-Only Storage Guarantee: Facet index files store only currently active records. Inactive, discontinued, or out-of-stock records violating facet_rules / junk_rules are excluded during indexing, eliminating the overhead of scanning historical records.

  • 3. Bidirectional String Dictionary (_${blk}.unq): Text facets (e.g. colors, specifications) map transparently between string labels and compact numeric dictionary IDs.

  • 4. Dynamic Scoping (base_ids): When computing facet counts within search results or subcategories, passing base_ids => \@ids bounds the aggregation strictly to matching records.

  • 5. Multi-Select Disjunctive Faceting: Supports multi-selection where checking multiple items within the same filter group uses OR logic (showing counts of remaining options), while combining across different filter groups uses AND logic.

METHODS

facet_menu($tableid, \%selected, \@facet_defs, [\%options])

High-level faceted navigation menu generator. =over 4 =item * $tableid: Table name (e.g. catalog_product). =item * \%selected: Hash of currently active filter selections: { block_idx => $val_or_arr_ref }. =item * \@facet_defs: Array of facet block definitions (or reads directly from table schema facet_block if omitted). =item * \%options: Optional parameters: - base_ids: Array reference of record IDs to scope calculation (e.g. search result IDs). - sort: 'count' (default, descending count) or 'label' / 'name' (alphabetical). - top: Limit maximum items returned per facet group (e.g. 10). - min_count: Minimum count required to include an item (default: 1). =back

Returns a comprehensive result hash: { count => $total, ids => \@filtered_ids, groups => \@groups, active_counts => \%counts }.

my $menu = $adb->facet_menu("catalog_product", \%selected, \@facet_defs);

field_fltkeys($tableid, \%opts)

Calculates facet key counts for a target block directly from active _${target_block}.fac. Automatically resolves dictionary string labels.

my $counts = $adb->field_fltkeys("catalog_product", { target_block => 2, base_ids => \@scoped_ids });

field_allfltkeys($tableid, \@blk_list, \@base_scope)

Calculates facet key counts across multiple active block files in a single pass.

facet_rules($table_info, @record)

Evaluates whether a record qualifies for inclusion in facet index files. Automatically integrates with junk_rules.

AUTHOR

Maruf Cetin <marufcetin@gmail.com>

LICENSE AND COPYRIGHT

Copyright (C) 2020-2026 Maruf Cetin.

This library is free software; you can redistribute it and/or modify it under the terms of the Artistic License 2.0.