view release on metacpan or search on metacpan
bin/setup_windows.ps1 view on Meta::CPAN
$taskName = "AmberDB-Watchdog-$ProjectName"
$daemonScript = Join-Path $scriptDir "amberdb_daemon.pl"
$libDir = Join-Path $appDir "lib"
# Subfolders required by AmberDB
$subdirs = @("tables", "config", "schema", "lock", "pids")
function Check-Admin {
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
Write-Error "[ERROR] Administrator privileges required! Please right-click PowerShell and select 'Run as Administrator'."
exit 1
}
}
function Install-ImDisk {
Write-Host "[SETUP] Checking ImDisk installation..." -ForegroundColor Cyan
$imdiskCmd = Get-Command imdisk -ErrorAction SilentlyContinue
if ($imdiskCmd) {
Write-Host "[OK] ImDisk is already installed on your system ($($imdiskCmd.Source))." -ForegroundColor Green
docs/EN.AmberDB-Locale_User-Guide.md view on Meta::CPAN
| Capability | Description |
|---|---|
| Case conversion | Locale-aware `uc`, `lc`, `ucfirst` |
| Sorting | Unicode Collation Algorithm (UCA) based |
| ASCII transliteration | Convert accented characters to plain ASCII (slugs, IDs) |
| Number â Text | Written-out numbers for invoices/documents |
| Date/time formatting | Locale-specific date formats |
| Number/currency formatting | Grouping separators, decimal separators, symbol placement |
| HTML entity decoding | Named + numeric entity decode |
| Plural rules | CLDR-based plural form selection |
| UTF-8 safe substring | Character-based slicing (not byte-based) |
**Architectural principle:** Engine logic lives in `AmberDB::Locale.pm`, while language data resides in `AmberDB::Locale::Lang::*` packages as **pure data**. Engine and data are completely separated.
---
## 2. Architecture
```
AmberDB::Locale â Main engine (all logic here)
docs/EN.AmberDB-vs-SQL_User-Guide.md view on Meta::CPAN
> This guide is designed for software engineers coming from traditional relational database management systems (RDBMS / SQL) who want to quickly build applications with AmberDB. Rather than focusing on abstract theory or database philosophy, it adopt...
---
## Table of Contents
1. [Essential Practical Notes for Developers (Quick Intro)](#1-essential-practical-notes-for-developers-quick-intro)
2. [Basic CRUD Operations (DML)](#2-basic-crud-operations-dml)
- [2.1 INSERT (Single Record)](#21-insert-single-record)
- [2.2 BULK INSERT (Batch Ingestion)](#22-bulk-insert-batch-ingestion)
- [2.3 SELECT by ID (Primary Key Point Read)](#23-select-by-id-primary-key-point-read)
- [2.4 UPDATE by ID (Single Record Mutation)](#24-update-by-id-single-record-mutation)
- [2.5 BULK UPDATE (Batch Mutation)](#25-bulk-update-batch-mutation)
- [2.6 DELETE (Single Record Deletion & Soft-Delete)](#26-delete-single-record-deletion--soft-delete)
- [2.7 BULK DELETE (Batch Deletion)](#27-bulk-delete-batch-deletion)
- [2.8 COUNT(*) (Table Record Count)](#28-count-table-record-count)
3. [Querying, Filtering, and Search (SELECT, WHERE, LIKE)](#3-querying-filtering-and-search-select-where-like)
- [3.1 Exact Match (WHERE field = value)](#31-exact-match-where-field--value)
- [3.2 Multi-Value IN Lookup (WHERE id IN (...))](#32-multi-value-in-lookup-where-id-in-)
- [3.3 Text Search (WHERE col LIKE '%...%' / FTS)](#33-text-search-where-col-like--fts)
- [3.4 Compound Multi-Field Filtering (WHERE A = x AND B = y)](#34-compound-multi-field-filtering-where-a--x-and-b--y)
- [3.5 Pagination (LIMIT & OFFSET)](#35-pagination-limit--offset)
4. [Sorting (ORDER BY) and Multilingual Collation](#4-sorting-order-by-and-multilingual-collation)
- [4.1 Numeric and Text Sorting](#41-numeric-and-text-sorting)
- [4.2 Multilingual and Turkish Character Collation](#42-multilingual-and-turkish-character-collation)
5. [Relationships and JOINs: The Core Architectural Difference](#5-relationships-and-joins-the-core-architectural-difference)
- [5.1 SQL Normalized Multi-Table + JOIN Model](#51-sql-normalized-multi-table--join-model)
docs/EN.AmberDB_User-Guide.md view on Meta::CPAN
Each block definition inside the `blocks` array supports the following attributes:
#### 9.7.1 Core Block Attributes
| Attribute | Type | Description | Example |
| :--- | :--- | :--- | :--- |
| `id` | `string` | Programmatic field identifier | `id => "email"` |
| `name` | `string` | Display label for UI forms and table headers | `name => "Email Address"` |
| `type` | `string` | Data storage, type validation, and indexing type | `type => "text"` |
| `input` | `string` | HTML/UI Form input component type | `input => "select"` |
| `valid` | `string` | Automated data validation rule | `valid => "not_null;email"` |
| `option` | `string` | Enumerated choice options (`value:label` pairs) | `option => "1:Active,0:Inactive"` |
| `rdbm` | `string / HASH`| Foreign table lookup mapping (`foreign_table;display_block`) | `rdbm => "catalog_category;2"` |
| `extend` | `HASH` | 1:1 vertical table extension | `extend => { table => "catalog_price", join => "id" }` |
#### 9.7.2 Supported 8 Core Field Types (`type`)
AmberDB uses **8 unified core storage types** across serialization (`db_encode`/`db_decode`), indexing, and sorting layers:
| Field Type (`type`) | Description | `enc_validate` (Write Phase) | `dec_validate` (Read Phase) | Indexing & Sorting Behavior |
docs/EN.AmberDB_User-Guide.md view on Meta::CPAN
#### 9.7.3 UI Form Input Components (`input`)
Determines how the field is rendered in UI forms and administration panels:
| Component (`input`) | UI Element | Description |
| :--- | :--- | :--- |
| `text` | Text Input | Standard single-line text field `<input type="text">`. |
| `textarea` | Textarea | Multi-line plain text box `<textarea>`. |
| `summernote` | Summernote | Rich WYSIWYG HTML visual editor for articles/descriptions. |
| `select` | Dropdown Select | Single-selection dropdown list `<select>`. |
| `checkbox` | Checkbox | Multi-selection checkboxes `<input type="checkbox">` (Use `type => "num"` for Boolean). |
| `radio` | Radio Buttons | Single-selection radio options `<input type="radio">`. |
| `file` | File Upload | Attachment or image file uploader `<input type="file">`. |
| `hidden` | Hidden Field | Hidden form element `<input type="hidden">` (for primary IDs). |
| `email` | Email Input | HTML5 email input field `<input type="email">`. |
| `ascii` | ASCII Field | User/code input box constrained to ASCII charset. |
| `number` | Number Input | Numeric stepper `<input type="number">`. |
| `date` | Date Picker | Interactive date calendar selector `<input type="date">`. |
| `password` | Password Field | Obscured security input `<input type="password">`. |
| `repeat` / `repeats` | Repeater Table | Dynamic sub-row table input with add/remove row buttons (Order items, invoice lines). |
| `search_block` | Search Box | Search-assisted dynamic filter input. |
| `selectbyfind` | SelectByFind | Foreign relation selector populated via dynamic search. |
| `selectbylist` | SelectByList | Multi-item picker component from list. |
#### 9.7.4 Repeating Child Row Blocks (`repeat_start` and `repeat_ids`)
AmberDB natively supports dynamic repeating child rows (e.g. order line items, invoice product rows) horizontally across the flat parent record without relational child tables or `JOIN` operations:
- **Horizontal Row Slicing (`@record[15..$#record]`):** Repeating items, each field index beyond fixed blocks (`$record[15]`, `$record[16]`, `$record[17]`, ...) holds an individual repeating record item (e.g. `[ 101, 'Book', 2, '150.00' ]`).
- **`repeat_start`**: Specifies the starting block index where dynamic repeating rows begin (e.g. `repeat_start => 15`). In the schema, block 15 acts as the prototype template for all succeeding indices.
- **`repeat_ids`**: The engine (`repeat_fields`) scans `@record[15..$#record]`, extracts the first element (numeric item ID) of each repeating row, joins them with commas (`"101,102,103"`), and stores the string in `repeat_ids` (e.g. block 12). Inclu...
```perl
docs/EN.AmberDB_User-Guide.md view on Meta::CPAN
# 5. HTML Entity Conversion (html_ascode / code_ashtml / text2html / html2text)
my $encoded_html = $adb->html_ascode('<a href="test">'); # Encodes special characters to HTML entities
my $plain_text = $adb->html2text($html_document);
```
---
## 16. Faceted Search & Category Filters (Facet Engine)
The Facet Engine powers e-commerce sidebar filter menus (Brand, Category, Author, Price Range, Color, etc.), designed for high-performance, low-latency multi-select faceted filtering across large product catalogs.
### 16.1 Key Benefits & Features
* **Low-Latency Columnar Aggregation:** Instead of scanning full records across the entire database on every page view, the engine reads only the targeted columnar forward index files (`.fac`), aggregating filter menus with minimal I/O overhead.
* **Counts In-Stock & Active Items Only:** Discontinued, out-of-stock, or disabled products never inflate filter counts; shoppers see only genuine, purchasable options and accurate item counts.
* **Smart Multi-Select (Disjunctive Counting):** When a shopper selects multiple brands (e.g., both *Apple* and *Samsung*), remaining brand counts stay visible and accurate (OR logic within the group, AND logic across groups).
* **Search-Scoped Filters (`base_ids`):** When a visitor searches for a keyword (e.g., "wireless headphones"), the sidebar filter displays attributes only for the matching search results, rather than the entire store.
* **Automatic Label Resolution:** Numeric IDs and free-text attributes (e.g., Color names) are automatically resolved into human-readable UI labels without requiring manual join queries.
### 16.2 Schema Configuration (`.table`)
Enable the facet engine by adding `use_facet => 1` and your `facet_block` specifications to your table schema:
```perl
# dbstore/schema/catalog_attributes.table
{
docs/EN.AmberDB_User-Guide.md view on Meta::CPAN
],
}
```
### 16.3 Usage & Practical Examples
#### A. Building Category Sidebar Menus
Generate complete filter groups and matching product counts in a single method call:
```perl
# User selections from URL query string: Category 5, Brand 12 or 14 selected
my %selected_filters = ( 1 => "5", 2 => ["12", "14"] );
my $menu = $adb->facet_menu(
"catalog_attributes",
\%selected_filters,
$table_info->{facet_block},
{ limit => 10, sort => "count" } # Display top 10 options per group sorted by product count
);
# $menu structure is ready to pass directly to your template:
# {
# count => 42, # Total matching products
# ids => [ 101, 105, 120, ... ], # IDs of matching products for product grid
# active_counts => { 1 => 1, 2 => 2 }, # Active filters count per block
# groups => [ # Ready-to-render sidebar groups:
docs/EN.AmberDB_User-Guide.md view on Meta::CPAN
#### B. Dynamic Filters on Search Result Pages
Pass the list of search result IDs as `base_ids` so sidebar filters apply strictly to search results:
```perl
# 1. Search catalog for user query (keys_only returns unpaginated ID list)
my @found_ids = $adb->search_table("catalog_product", "sci-fi", keys_only => 1);
# 2. Generate facet menu scoped exclusively to the search results
my $search_facets = $adb->facet_menu(
"catalog_attributes",
\%selected_filters,
$table_info->{facet_block},
{ base_ids => \@found_ids }
);
```
---
## 17. User Audit Trail and Backup
### 17.1 User Action History (`log_owner`)
docs/TR.AmberDB-vs-SQL_Kullanim_Rehberi.md view on Meta::CPAN
> Bu kılavuz, geleneksel iliÅkisel veritabanı (RDBMS / SQL) deneyimi olan geliÅtiricilerin AmberDB'ye hızla adapte olabilmesi için hazırlanmıÅtır. Kuramsal ve felsefi detaylar yerine, **"SQL'de Åu Åekilde yapılan iÅlem AmberDB'de Åu Å...
---
## İçindekiler
1. [GeliÅtiricinin Bilmesi Gereken Temel Notlar (Hızlı GiriÅ)](#1-geliÅtiricinin-bilmesi-gereken-temel-notlar-hızlı-giriÅ)
2. [Temel CRUD İÅlemleri (DML)](#2-temel-crud-iÅlemleri-dml)
- [2.1 INSERT (Tekil Kayıt Ekleme)](#21-insert-tekil-kayıt-ekleme)
- [2.2 BULK INSERT (Toplu Kayıt Ekleme)](#22-bulk-insert-toplu-kayıt-ekleme)
- [2.3 SELECT by ID (Birincil Anahtarla Okuma)](#23-select-by-id-birincil-anahtarla-okuma)
- [2.4 UPDATE by ID (Kayıt Güncelleme)](#24-update-by-id-kayıt-güncelleme)
- [2.5 BULK UPDATE (Toplu Güncelleme)](#25-bulk-update-toplu-güncelleme)
- [2.6 DELETE (Kayıt Silme & Soft-Delete)](#26-delete-kayıt-silme--soft-delete)
- [2.7 BULK DELETE (Toplu Silme)](#27-bulk-delete-toplu-silme)
- [2.8 COUNT(*) (Kayıt Sayısı)](#28-count-kayıt-sayısı)
3. [Sorgulama, Filtreleme ve Arama (SELECT, WHERE, LIKE)](#3-sorgulama-filtreleme-ve-arama-select-where-like)
- [3.1 Tekil DeÄer EÅleÅmesi (WHERE field = value)](#31-tekil-deÄer-eÅleÅmesi-where-field--value)
- [3.2 Ãoklu DeÄer Listesi (WHERE id IN (...))](#32-çoklu-deÄer-listesi-where-id-in-)
- [3.3 Metin Arama (WHERE col LIKE '%...%' / FTS)](#33-metin-arama-where-col-like--fts)
- [3.4 Ãok Kriterli Filtreleme (WHERE A = x AND B = y)](#34-çok-kriterli-filtreleme-where-a--x-and-b--y)
- [3.5 Sayfalama (LIMIT & OFFSET)](#35-sayfalama-limit--offset)
4. [Sıralama (ORDER BY) ve Alfabetik YerelleÅtirme (Collation)](#4-sıralama-order-by-ve-alfabetik-yerelleÅtirme-collation)
- [4.1 Sayısal ve Alfabetik Sıralama](#41-sayısal-ve-alfabetik-sıralama)
- [4.2 Türkçe ve Ãok Dilli Karakter Sıralaması (Collation)](#42-türkçe-ve-çok-dilli-karakter-sıralaması-collation)
5. [İliÅkiler ve JOIN MantıÄı (En Büyük Mimari Fark)](#5-iliÅkiler-ve-join-mantıÄı-en-büyük-mimari-fark)
- [5.1 SQL Normalize Tablo + JOIN Modeli](#51-sql-normalize-tablo--join-modeli)
docs/TR.AmberDB_Veritabani_Sistemi.md view on Meta::CPAN
use_junk => 1,
junk_rules => [ [ 11, "eq", 0 ] ],
use_ramdisk => 1,
ramdisk_ttl => 3600,
keep_deleted => 1,
log_owner => 1,
min_char => 2,
blocks => [
{ id => "id", name => "Ãrün ID", type => "auto_id", input => "hidden" },
{ id => "category_id", name => "Kategori", type => "text", input => "select", rdbm => "catalog_category;2" },
{ id => "brand_id", name => "Marka", type => "text", input => "select", rdbm => "catalog_brand;2" },
{ id => "author_id", name => "Yazar", type => "text", input => "text" },
{ id => "title", name => "Ãrün Adı", type => "text", input => "text", valid => "not_null" },
{ id => "subtitle", name => "Alt BaÅlık", type => "text", input => "text" },
{ id => "supplier", name => "Tedarikçi", type => "text", input => "text" },
{ id => "description", name => "Açıklama", type => "html", input => "textarea" },
{ id => "stock", name => "Stok Adedi", type => "num", input => "text" },
{ id => "barcode", name => "Barkod", type => "text", input => "text" },
{ id => "price", name => "Fiyat", type => "num", input => "text" },
{ id => "status", name => "SatıŠDurumu",type => "option", input => "select", option => "1:SatıÅta,0:Pasif" },
],
}
```
### 9.6 Åema Parametreleri ve Konfigürasyon Referansı (Tablo Düzeyi)
AÅaÄıdaki tablo, bir `.table` dosyasında kullanılabilecek tüm üst düzey parametreleri, veri tiplerini, varsayılan deÄerlerini ve geriye dönük uyumluluk (eski sistem) karÅılıklarını listeler:
| Parametre | Tip | Varsayılan | Eski / Alternatif Adı | Açıklama |
| :--- | :--- | :--- | :--- | :--- |
docs/TR.AmberDB_Veritabani_Sistemi.md view on Meta::CPAN
Åema içindeki `blocks` dizisinde tanımlanan her bir alan bloÄu Åu nitelikleri alabilir:
#### 9.7.1 Temel Blok Nitelikleri
| Nitelik | Tip | Açıklama | Ãrnek |
| :--- | :--- | :--- | :--- |
| `id` | `string` | Alanın programatik anahtar adı | `id => "email"` |
| `name` | `string` | Formlarda ve tablolarda gösterilecek etiket adı | `name => "E-Posta Adresi"` |
| `type` | `string` | Veri depolama, tip doÄrulama ve indeksleme veri tipi | `type => "text"` |
| `input` | `string` | Form giriÅ bileÅeni (UI) tipi | `input => "select"` |
| `valid` | `string` | Otomatik doÄrulama kuralı | `valid => "not_null;email"` |
| `option` | `string` | Seçenek listesi (`deÄer:etiket` çiftleri) | `option => "1:Aktif,0:Pasif"` |
| `rdbm` | `string / HASH`| BaÅka tablodan veri çekme (`hedef_tablo;gösterilecek_blok`) | `rdbm => "catalog_category;2"` |
| `extend` | `HASH` | 1:1 dikey geniÅletme tablosu | `extend => { table => "catalog_price", join => "id" }` |
#### 9.7.2 Desteklenen 8 Ãekirdek Veri Tipi (`type`)
AmberDB motoru, serileÅtirme (`db_encode`/`db_decode`), indeksleme ve sıralama katmanlarında **8 temel çekirdek veri tipi** kullanır:
| Veri Tipi (`type`) | Tanım | `enc_validate` (Yazma Anı) | `dec_validate` (Okuma Anı) | İndeks ve Sıralama DavranıÅı |
docs/TR.AmberDB_Veritabani_Sistemi.md view on Meta::CPAN
#### 9.7.3 Form GiriÅ BileÅenleri (`input`)
UI ve yönetim paneli katmanında form elemanının nasıl görüntüleneceÄini belirler:
| BileÅen (`input`) | UI Elemanı | Açıklama |
| :--- | :--- | :--- |
| `text` | Metin Kutusu | Standart tek satırlık metin alanı `<input type="text">`. |
| `textarea` | Metin Alanı | Ãok satırlı düz metin kutusu `<textarea>`. |
| `summernote` | Summernote | Zengin WYSIWYG görsel HTML editörü. |
| `select` | Açılır Menü | Tekli seçim kutusu `<select>`. |
| `checkbox` | Onay Kutusu | Ãoklu seçim onay kutuları `<input type="checkbox">` (Boolean için `type => "num"`). |
| `radio` | Radyo Butonu | Tekli seçim radyo butonları `<input type="radio">`. |
| `file` | Dosya Yükleme | Dosya veya görsel yükleme bileÅeni `<input type="file">`. |
| `hidden` | Gizli Alan | Gizli form elemanı `<input type="hidden">` (birincil ID için). |
| `email` | E-Posta Kutusu | HTML5 e-posta giriŠalanı `<input type="email">`. |
| `ascii` | ASCII Alanı | Yalnızca ASCII karakterlere izin veren metin kutusu. |
| `number` | Sayı Kutusu | Sayısal giriŠkutusu `<input type="number">`. |
| `date` | Tarih Seçici | EtkileÅimli takvim tarih seçici `<input type="date">`. |
| `password` | Åifre Kutusu | Maskeli Åifre giriÅ alanı `<input type="password">`. |
| `repeat` / `repeats` | Tekrarlayan Tablo | Dinamik alt satır ekleme/çıkarma formu (SipariŠkalemleri, fatura satırları). |
| `search_block` | Arama Kutusu | Arama destekli dinamik filtre giriŠalanı. |
| `selectbyfind` | Arayarak Seç | İliÅkili tablodan dinamik arama ile seçim bileÅeni. |
| `selectbylist` | Listeden Seç | Listeden çoklu seçim bileÅeni. |
#### 9.7.4 Tekrarlayan Alt Satır Blokları (`repeat_start` ve `repeat_ids`)
AmberDB, iliÅkisel alt tablolara (child table) ve `JOIN` sorgularına ihtiyaç duymadan, ana kayıt içerisine gömülü tekrarlayan dinamik alt satırları (örn. sipariÅ kalemleri, fatura ürün satırları) yatay düzende doÄrudan destekler:
- **Yatay Dizi Yapısı (`@record[15..$#record]`):** Tekrarlayan alt satırlar, sabit bloklardan sonra gelen her bir indeks (`$record[15]`, `$record[16]`, `$record[17]`, ...), baÄımsız birer alt satır kaydıdır (örn: `[ 101, 'Kitap', 2, 150.00 ...
- **`repeat_start`**: Tekrarlayan dinamik blokların baÅladıÄı blok indeksini belirtir (örn. `repeat_start => 15`). Åemada 15. blok Åablon olarak tanımlanır ve 15 ve sonraki tüm alanlar bu Åablonun tip kurallarıyla doÄrulanır.
- **`repeat_ids`**: Motor (`repeat_fields`), `@record[15..$#record]` dilimindeki tüm alt satırların birinci elemanını (sayısal ürün ID'si) otomatik olarak toplayıp virgülle birleÅtirir (`"101,102,103"`) ve `repeat_ids` (örn. 12) bloÄuna ...
```perl
lib/AmberDB.pm view on Meta::CPAN
$adb->delete_list("catalog_product", 101, 102, 103);
=head2 read_id($table_id, [$record_id], [\%options])
Reads a single record by primary key ID (or dynamic positional type) in $O(1)$ time.
Options:
=over 4
=item * C<type>: Positional selector (C<'last'>, C<'first'>, C<'rand'>). When specified, a dummy ID (e.g. C<0>) can be passed to preserve standard 3-argument positional signature consistency: C<< $adb->read_id("products", 0, { type => "last" }) >> (o...
=item * C<sort>: Optional sort block (numeric index, schema block name like C<"price">, C<"price desc">, or hashref C<< { block => "price", dir => "asc" } >>). Used in combination with C<< type => 'first' >> or C<'last'> to retrieve the first or last...
=item * C<range>: Optional numerical/chronological range filter hashref C<< { block => 4, min => 1000, max => 2000 } >> to constrain candidate records before positional selection.
=item * C<inflate>: Boolean (C<1> or string C<"inflate">) to inflate record fields into a named HASH reference based on table schema blocks.
=item * C<counter> / C<use_counter>: Explicit boolean (C<1> or C<0>) or string C<"counter"> to force or disable incrementing the read counter (C<.cnt>).
=item * C<no_counter>: Explicit boolean (C<1>) or string C<"no_counter"> to suppress incrementing the read counter.
=item * C<deleted> / C<force>: Boolean (C<1>) or string C<"deleted"> / C<"force"> to read from soft-deleted archive (C<.del>) if missing from active table.
=item * C<links> / C<alias>: Boolean (C<1>) or string C<"links"> / C<"alias"> to resolve a deleted/merged record ID from alias link index (C<.lnk>) to its canonical record.
lib/AmberDB.pm view on Meta::CPAN
# Tiered Junk query mode
my @active = $adb->field_fetch("products", 1, "5", { jnktype => 'A' }); # Only Active records
# Numerical / chronological range filtering on an auxiliary block:
my @range_prods = $adb->field_fetch("products", 2, "Smartphones", { range => { block => "price", min => 1000, max => 1500 } });
C<field_fetch> uses the C<match_block> definition in the schema and accesses inverted match index files (C<.fld>), providing $O(1)$ average-time lookup per indexed key (total retrieval cost scales with the number of requested values and matching reco...
=head2 search_table($table_id, $query, [\%options])
It performs searches matching query terms using the full-text C<.src> index (or a sorted table scan backup method if unindexed). C<search_table> uses the C<AmberDB::Locale> module. It features advanced language normalization according to the selected...
B<IMPORTANT (Return Signature Convention):>
When C<$limit> is passed and C<E<gt> 0> (paginated), C<search_table> returns C<($total_count, @records)> where the first scalar is the total matching count integer. When C<$limit> is omitted or C<0> (unpaginated), it returns C<@records> directly. Unp...
# 1. Unpaginated (returns array of record arrayrefs directly)
my @records = $adb->search_table("catalog_product", "wireless headphones");
my @sorted_records = $adb->search_table("catalog_product", "headphones", { sort => -5 });
# 2. Paginated (first element is total matching count integer)
my ($total_count, @search) = $adb->search_table("catalog_product", "headphones", { offset => 0, limit => 20 });
lib/AmberDB.pm view on Meta::CPAN
jnktype => 'AB',
}
);
# Return only scalar record IDs
my @all_ids = $adb->search_table("catalog_product", "headphones", { keys_only => 1 });
my ($total_count, @ids) = $adb->search_table("catalog_product", "headphones", { offset => 0, limit => 50, keys_only => 1 });
=head2 field_filter($table_id, \%filter_options)
Performs multi-block filtered queries (AND / OR) with support for multi-value filters, tier mode selection (C<jnktype>), numerical/chronological range filtering (C<range>), sorting, and pagination:
my $res = $adb->field_filter("catalog_product", {
type => "and",
filter => { 1 => "5", 6 => ["12", "14"] },
range => { block => "price", min => 1000, max => 2500 },
sort => { blk => 5, reverse => 1 },
jnktype => "AB",
offset => 0,
limit => 20,
});
lib/AmberDB/Array.pm view on Meta::CPAN
Calculates the dimensions of a list of array references (matrix). Returns a 2-element list: C<($max_row_index, $max_column_index)>.
my ($max_row, $max_col) = $adb->array_size( [1, 2, 3], [4, 5] );
# => (1, 2) # 2 rows (0..1), 3 columns (0..2)
=head2 array_pick(\@indexes, @record)
Extracts and returns only the fields at the given 0-based index positions from C<@record>.
my @selected = $adb->array_pick([ 0, 2 ], "ID101", "SecretKey", "PublicTitle");
# => ("ID101", "PublicTitle")
=head2 deep_copy($data)
Recursively clones nested Perl data structures (hash references, array references, and scalar values) to produce an independent copy.
my $copy = $adb->deep_copy({ user => { roles => [ "admin", "editor" ] } });
=head2 hash_diff($hash1, $hash2)
lib/AmberDB/Base/Facet.pm view on Meta::CPAN
$all_counts{$blk} = \%named_map;
}
}
}
return \%all_counts;
}
# Generates schema-driven facet menu structure and performs active filtering.
# my $result = $adb->facet_menu($tableid, [\%options]);
# options: selected => \%selected, facet_defs => \@facet_defs, offset => 0, limit => 20, base_ids => \@base_scope
# Legacy: my $result = $adb->facet_menu($tableid, \%selected, \@facet_defs, \%opts);
# ------------------------------------------------
sub facet_menu {
my ( $self, $tableid, @args ) = @_;
$tableid or return ( wantarray ? () : {} );
my $table_info = $self->table_info($tableid);
return ( wantarray ? () : {} ) unless $table_info && $table_info->{use_facet};
my ( $selected, $facet_defs, $opts );
if ( @args == 1 && ref( $args[0] ) eq 'HASH' ) {
my $arg = $args[0];
if ( exists $arg->{selected}
|| exists $arg->{facet_defs}
|| exists $arg->{offset}
|| exists $arg->{start}
|| exists $arg->{limit}
|| exists $arg->{base_ids}
|| exists $arg->{scope_ids}
|| exists $arg->{blocks}
|| exists $arg->{filter}
|| exists $arg->{where}
|| exists $arg->{match}
|| exists $arg->{range} )
{
$opts = $arg;
$selected = $arg->{selected} || $arg->{filter} || $arg->{where} || $arg->{match} || {};
$facet_defs = $arg->{facet_defs} || $arg->{blocks} || $table_info->{facet_block} || [];
}
else {
$selected = $arg;
$facet_defs = $table_info->{facet_block} || [];
$opts = {};
}
}
else {
( $selected, $facet_defs, $opts ) = @args;
}
$selected ||= {};
$facet_defs ||= $table_info->{facet_block} || [];
$opts ||= {};
my $table_path = $self->table_path($tableid);
my $offset = $opts->{offset} // $opts->{start} // 0;
my $limit = $opts->{limit} // 0;
my $base_scope = $opts->{base_ids} || $opts->{scope_ids} || undef;
if ( $opts && $opts->{range} ) {
if ( my $ranges = $self->normalize_range_opts( $tableid, $opts ) ) {
if ( $base_scope && @$base_scope ) {
lib/AmberDB/Base/Facet.pm view on Meta::CPAN
my $inx_path = "$table_path.inx";
( undef, @all_active ) = $self->index_get( $inx_path, "keys" ) if -e $inx_path;
}
@all_active = $self->table_keys($tableid) unless @all_active;
my @scoped = $self->filter_ids_by_range( $tableid, \@all_active, $ranges );
$base_scope = \@scoped;
}
}
}
# Normalize active selections into %active_filter
my %active_filter;
for my $raw_k ( keys %$selected ) {
my $blk = $raw_k;
$blk =~ s/^f//; # Strip leading 'f' prefix if passed as f1, f2...
my $v = $selected->{$raw_k};
if ( defined $v && $v ne '' ) {
my @vals = ref($v) eq 'ARRAY' ? @$v : split /,/, $v;
@vals = grep { defined $_ && $_ ne '' } @vals;
$active_filter{$blk} = \@vals if @vals;
}
}
# 1. Active Filtering (Filtered IDs)
my ( $filtered_ids, $total_count ) = ( [], 0 );
if (%active_filter) {
lib/AmberDB/Base/Facet.pm view on Meta::CPAN
my $opt_str = $table_info->{blocks}->[$blk]->{option} // '';
if ($opt_str) {
for my $pair ( split /,/, $opt_str ) {
my ( $v, $l ) = split /:/, $pair, 2;
$name_map{$v} //= $l // $v;
}
}
}
# Active status for this block
my %selected_vals = map { $_ => 1 } @{ $active_filter{$blk} // [] };
my $active_cnt = scalar keys %selected_vals;
$active_counts{$blk} = $active_cnt;
my @items;
for my $val (@vals) {
push @items, {
uid => "fc_${blk}_${val}",
param => "f$blk",
val => $val,
label => ( $name_map{$val} // $val ),
count => ( $counts->{$val} // 0 ),
checked => ( $selected_vals{$val} ? "1" : "" ),
};
}
my $group_data = {
blk => $blk,
name => $label,
active => ( $active_cnt ? "1" : "" ),
active_count => $active_cnt,
records => \@items,
};
lib/AmberDB/Base/Facet.pm view on Meta::CPAN
AmberDB::Index::Facet - Column-oriented facet indexing, disjunctive counting, and navigation menu generator
=head1 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,
});
lib/AmberDB/Base/Facet.pm view on Meta::CPAN
=over 4
=item * B<1. Columnar Unified Storage (C<$table_path.fac>):> Facet data is stored in a unified columnar forward index file (C<$table_path.fac>). Each record's block values are keyed as C<$blk:$rid> mapping to packed value IDs, enabling fast single-co...
=item * B<2. Active-Only Storage Guarantee:> Facet index files store B<only currently active records>. Inactive, discontinued, or out-of-stock records violating C<facet_rules> / C<junk_rules> are excluded during indexing, eliminating the overhead of ...
=item * B<3. Bidirectional String Dictionary (C<.unq>):> Text facets (e.g. colors, specifications) map transparently between string labels and compact numeric dictionary IDs.
=item * B<4. Dynamic Scoping (C<base_ids>):> When computing facet counts within search results or subcategories, passing C<base_ids =E<gt> \@ids> bounds the aggregation strictly to matching records.
=item * B<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.
=back
=head1 METHODS
=head2 facet_menu($tableid, [\%options])
High-level faceted navigation menu generator.
Options:
=over 4
=item * C<selected>: Hash of currently active filter selections: C<{ block_idx =E<gt> $val_or_arr_ref }>. (Aliases: C<filter>, C<where>, C<match>).
=item * C<facet_defs>: Array of facet block definitions (or reads directly from table schema C<facet_block> if omitted).
=item * C<offset>: Pagination start offset (default: 0).
=item * C<limit>: Page size limit (default: 0 = unpaginated).
=item * C<base_ids>: (Alias: C<scope_ids>) Array reference of record IDs to scope calculation (e.g. search result IDs).
=item * C<sort>: C<'count'> (default, descending count) or C<'label'> / C<'name'> (alphabetical).
=item * C<top>: Limit maximum items returned per facet group (e.g. 10).
=item * C<min_count>: Minimum count required to include an item (default: 1).
=item * C<range>: Numerical / chronological range filtering C<{ block => 4, min => 1000, max => 2000 }>.
=back
Returns a comprehensive result hash:
C<{ count => $total, ids => \@filtered_ids, groups => \@groups, active_counts => \%counts, counts => \%all_counts }>.
my $menu = $adb->facet_menu("catalog_product", {
selected => { 1 => "5" },
range => { block => "price", min => 1000, max => 2000 },
offset => 0,
limit => 20,
});
Legacy invocation C<$adb->facet_menu($tableid, \%selected, \@facet_defs, \%options)> remains fully supported.
=head2 field_fltkeys($tableid, \%opts)
Calculates facet key counts for a target block directly from active C<.fac>. Automatically resolves dictionary string labels.
Options:
=over 4
=item * C<target_block>: (Required) Attribute block index to aggregate facet counts for.
=item * C<filter>: (Optional, aliases: C<where>, C<match>) Active filter conditions on other blocks C<{ block_idx => $value }>.
=item * C<base_ids>: (Optional, alias: C<scope_ids>) Array reference of record IDs to scope calculation to.
lib/AmberDB/Base/Schema.pm view on Meta::CPAN
)
};
$schema->{use_simple} = 1;
$schema->{no_backup} = 1;
$schema->{no_transact} = 1;
$schema->{ramdisk_ttl} = 300 unless defined $schema->{ramdisk_ttl} && $schema->{ramdisk_ttl} > 0;
}
# ------------------------------------------------------------------------
# Pipeline Step 2: Simple Mode Stripping
# In simple key-value mode, selectively strip columnar, indexing, relational,
# and caching definitions to enforce lightweight operation.
# ------------------------------------------------------------------------
if ( $schema->{use_simple} && ( $schema->{use_ramdisk} // 0 ) != 3 ) {
delete @{$schema}{
qw(
blocks match_block search_block view_block facet_block filter_block
slug_block sort_block sort_fields use_facet facet_rules use_junk junk_rules
record_index repeat_start repeat_ids field_rules
use_ramdisk ramdisk_ttl use_cache cache_ttl
)
lib/AmberDB/Locale.pm view on Meta::CPAN
# Safely evaluate numeric expression isolated from global DIE handlers
my $res = eval {
local $SIG{__DIE__} = sub {};
eval $expr; ## no critic
};
return $res ? 1 : 0;
}
# -------------------------------------------------------
# Evaluate CLDR plural rule and select template.
#
# my $text = $lang->plural(1, { one => "{count} ürün", other => "{count} ürün" });
# my $text = $lang->plural(5, { one => "{count} item", other => "{count} items" });
# -------------------------------------------------------
sub plural {
my ( $self, $count, $forms ) = @_;
return '' unless defined $forms;
$count //= 0;
my $form_key = 'other';
lib/AmberDB/Locale.pm view on Meta::CPAN
Custom formatting overrides:
$tr->format_currency(100, symbol => 'TL', position => 'suffix', space => 1);
# => "100,00 TL"
=head3 ISO 4217 Currency Dictionary
C<AmberDB::Locale> integrates a master dictionary of ISO 4217 currency definitions, numeric codes, currency symbols, and default subunit decimal precision (implemented internally via C<AmberDB::Locale::Currency>).
Direct dictionary lookups, symbol conversions, and select dropdown lists can be accessed via:
use AmberDB::Locale::Currency;
# Symbol and name lookups
my $sym = AmberDB::Locale::Currency->symbol('TRY'); # 'âº'
my $name = AmberDB::Locale::Currency->name('USD'); # 'US Dollar'
my $info = AmberDB::Locale::Currency->by_code('EUR');
# => { num => '978', name => 'Euro', symbol => 'â¬', digits => 2 }
# Dropdown options for UI forms
lib/AmberDB/Locale.pm view on Meta::CPAN
Supported helper methods:
=over 4
=item * C<AmberDB::Locale::Currency-E<gt>by_code($iso_code)> - Returns the currency definition hash reference for the given 3-letter ISO 4217 code (case-insensitive), containing C<num>, C<name>, C<symbol>, and C<digits>.
=item * C<AmberDB::Locale::Currency-E<gt>symbol($iso_code)> - Returns the currency symbol for the given ISO code (e.g. C<'âº'>, C<'$'>, C<'â¬'>, C<'£'>, C<'â½'>, C<'Â¥'>). If the code is unknown, returns the uppercase code itself.
=item * C<AmberDB::Locale::Currency-E<gt>name($iso_code)> - Returns the English currency name for the given ISO code.
=item * C<AmberDB::Locale::Currency-E<gt>all()> - Returns a list of 2-element array references C<[ $code, $name ]> ordered by priority, suitable for rendering HTML C<E<lt>selectE<gt>> form dropdowns.
=item * C<AmberDB::Locale::Currency-E<gt>active_codes()> - Returns the list of active 3-letter ISO 4217 currency codes supported by the dictionary.
=back
=head2 Date & Time Operations
=head3 format_date($time_or_string [, $pattern_or_style])
Formats a Unix epoch timestamp or ISO date string into a localized date/time representation.
lib/AmberDB/Locale/Currency.pm view on Meta::CPAN
# Get currency name by ISO code
# AmberDB::Locale::Currency->name('TRY') -> 'Türk Lirası'
sub name {
my ( $class_or_self, $code ) = @_;
return '' unless defined $code;
my $c = $CURRENCIES{ uc($code) };
return $c ? $c->{name} : uc($code);
}
# Get all currencies as [ [$code, $name], ... ] for form selects/dropdowns
sub all {
return map { [ $_, $CURRENCIES{$_}->{name} ] } @CURRENCY_ORDER;
}
# List active ISO codes
sub active_codes {
return @CURRENCY_ORDER;
}
1;
t/amberdb_ecommerce_facet.t view on Meta::CPAN
);
$adb->insert_list( 'catalog_product', @products );
# ---------------------------------------------------------------------------
subtest '1. Initial Unfiltered Facet Menu & Label Resolution' => sub {
plan tests => 8;
my $menu = $adb->facet_menu(
'catalog_product',
{}, # No filter selected
$adb->table_attr('catalog_product', 'facet_block'),
{ sort => 'count' }
);
ok( $menu, "Facet menu generated successfully" );
is( $menu->{count}, 8, "Total active products count is 8 (junk item excluded)" );
is( scalar( @{ $menu->{groups} } ), 4, "4 facet groups returned" );
# Check Category Group (blk 1)
my ($cat_group) = grep { $_->{blk} == 1 } @{ $menu->{groups} };
t/amberdb_ecommerce_facet.t view on Meta::CPAN
is( $menu->{count}, 2, "Facet menu restricted to 2 search hits" );
my ($cat_group) = grep { $_->{blk} == 1 } @{ $menu->{groups} };
is( scalar( @{ $cat_group->{records} } ), 1, "Only Bilim Kurgu category present in search facets" );
is( $cat_group->{records}->[0]->{label}, 'Bilim Kurgu', "Category is Bilim Kurgu" );
};
# ---------------------------------------------------------------------------
subtest '5. Dynamic Price Range (Min/Max Slider) & Facet Integration' => sub {
plan tests => 5;
# Scenario: User selects Price range [100.00 TL - 200.00 TL]
# In catalog_product, prices are:
# 1: 85.00 (Roman, Can, Kafka) -> OUT (< 100)
# 2: 150.00 (Roman, Can, Kafka) -> IN
# 3: 195.00 (Roman, Is Bankasi, Dostoyevski) -> IN
# 4: 220.00 (Roman, Is Bankasi, Dostoyevski) -> OUT (> 200)
# 5: 250.00 (Tarih, Is Bankasi, Ataturk) -> OUT (> 200)
# 6: 75.00 (Tarih, Is Bankasi, Ataturk) -> OUT (< 100)
# 7: 280.00 (Bilim Kurgu, Ithaki, Herbert) -> OUT (> 200)
# 8: 160.00 (Bilim Kurgu, Ithaki, Herbert) -> IN
t/amberdb_facet_columnar.t view on Meta::CPAN
base_ids => [ 101, 102 ],
}
);
is( $scoped_brands->{'Apple'}, 1, "Scoped facet count: Apple = 1" );
is( $scoped_brands->{'Samsung'}, 1, "Scoped facet count: Samsung = 1" );
ok( !exists $scoped_brands->{'Dell'}, "Dell not in scope" );
};
# ---------------------------------------------------------------------------
subtest '4. facet_menu generation with selected filters' => sub {
plan tests => 3;
my $facet_menu = $adb->facet_menu(
'catalog_product',
{ 1 => 'Telefon' }, # Filter by Telefon
$adb->table_info('catalog_product')->{facet_block}
);
ok( ref($facet_menu) eq 'HASH', "facet_menu returned hash" );
ok( exists $facet_menu->{groups_by_blk}, "groups_by_blk exists" );
t/amberdb_standard_api_args.t view on Meta::CPAN
my $all3 = $adb->field_allfltkeys('products', [ 2, 3 ], [ 1, 2 ]);
is( $all3->{2}{'Smartphones'}, 2, 'Legacy field_allfltkeys with arrayref base_scope' );
};
# ---------------------------------------------------------------------------
subtest '9. facet_menu with standardized single \%options and legacy' => sub {
plan tests => 6;
# Standardized: single unified hashref
my $menu1 = $adb->facet_menu('products', {
selected => { 2 => 'Smartphones' },
sort => 'count'
});
ok( $menu1, 'facet_menu with unified hashref generated successfully' );
is( $menu1->{count}, 2, 'Total matching count is 2' );
is( scalar(@{ $menu1->{groups} }), 2, '2 facet groups in menu' );
# Standardized: with offset & limit
my $menu_paged = $adb->facet_menu('products', {
offset => 0,
limit => 3