AmberDB
view release on metacpan or search on metacpan
lib/AmberDB/Index.pm view on Meta::CPAN
return 0 unless defined $a && $a ne '' && defined $b && $b ne '';
my $num = ( $a =~ /^-?[0-9.]+$/ && $b =~ /^-?[0-9.]+$/ );
if ( $op eq 'eq' || $op eq '==' ) { return $num ? $a == $b : $a eq $b }
elsif ( $op eq 'ne' || $op eq '!=' ) { return $num ? $a != $b : $a ne $b }
elsif ( $op eq '>' ) { return $a > $b }
elsif ( $op eq '>=' ) { return $a >= $b }
elsif ( $op eq '<' ) { return $a < $b }
elsif ( $op eq '<=' ) { return $a <= $b }
return 0;
}
# $adb->_resolve_field_value($table_info, \@record, $field_spec);
# ------------------------------------------------
sub _resolve_field_value {
my ( $self, $table_info, $record, $field_spec ) = @_;
return '' unless defined $field_spec && defined $record && ref($record) eq 'ARRAY';
# 1. Relational RDBM or Nested Array: "2->14"
if ( $field_spec =~ /^(\d+)->(\d+)$/ ) {
my ( $b1, $b2 ) = ( $1, $2 );
my $blk_info = $table_info->{blocks}->[$b1] if ref($table_info) eq 'HASH' && $table_info->{blocks};
# RDBM relational resolution
if ( $blk_info && $blk_info->{rdbm} ) {
my $ref_table;
if ( ref( $blk_info->{rdbm} ) eq 'HASH' ) {
$ref_table = $blk_info->{rdbm}->{table};
}
elsif ( $blk_info->{rdbm} =~ /^([\w\-]+)[;:,]/ ) {
$ref_table = $1;
}
my $ref_id = $record->[$b1];
if ( $ref_table && defined $ref_id && $ref_id ne '' ) {
my @ref_rec = $self->read_id( $ref_table, $ref_id );
return $ref_rec[$b2] // '';
}
return '';
}
# Nested Array Reference
elsif ( ref( $record->[$b1] ) eq 'ARRAY' ) {
return $record->[$b1]->[$b2] // '';
}
# Tab/comma separated string
else {
my $raw = $record->[$b1] // '';
my @parts = split /[\t,]/, $raw;
return $parts[$b2] // '';
}
}
# 2. Direct Field: 5
return $record->[$field_spec] // '';
}
# my @vals = $adb->field_to_list($value, $mode, $table_path, $table_info, $blk);
# Converts ARRAY ref, comma/semicolon delimited string or single value to a normalized list.
# In 'write' mode, registers text strings into .unq with auto-incrementing lastid, or validates rdbm.
# In 'read' mode, resolves existing string IDs from .unq without creating new entries.
# ------------------------------------------------
sub field_to_list {
my ( $self, $value, $mode, $table_path, $table_info, $blk ) = @_;
return () unless defined $value && $value ne '';
# 1. Normalize input using trim_space with flatten mode (1)
my @raw;
if ( ref $value eq 'ARRAY' ) {
@raw = @{$value};
}
else {
my $str = $self->trim_space( "$value", 1 );
return () unless defined $str && $str ne '';
if ( $str =~ /[,;]/ ) {
@raw = split /[,;]/, $str;
}
else {
@raw = ($str);
}
}
# Clean and normalize each element with trim_space(..., 1)
my @list;
foreach my $item (@raw) {
next unless defined $item;
if ( ref $item eq 'ARRAY' ) {
push @list, $self->field_to_list($item);
next;
}
my $s = $self->trim_space( "$item", 1 );
push @list, $s if defined $s && length($s);
}
return () unless @list;
# If no mode or context provided, return the normalized elements
return @list unless $mode && $table_path && defined $blk;
# 2. Mode-specific processing ('write' or 'read')
$mode = lc($mode);
my ( $target_table, $target_blk ) = $self->rdbm_target( $table_info, $blk );
if ($target_table) {
my @ids;
foreach my $item (@list) {
if ( $item =~ /^\d+$/ ) {
push @ids, $item;
}
elsif ( defined $target_blk ) {
my $target_table_path = $self->table_path($target_table);
my $target_unq = "${target_table_path}_${target_blk}.unq";
my $nid;
if ( -e $target_unq || $self->{_db}->{$target_unq} ) {
($nid) = $self->index_get( $target_unq, "s:$item", 'raw' );
}
if ( !defined $nid || $nid eq '' ) {
lib/AmberDB/Index.pm view on Meta::CPAN
return () unless ref($recs_ref) eq 'ARRAY' && @$recs_ref;
return $self->db_sortid( $tableid, @$recs_ref ) unless $s_opt;
my $norm = $self->normalize_sort_opt($s_opt);
my $blk = $norm->{blk} // 0;
my $dir = $norm->{dir} // 'desc';
my $table_info = $tableid ? $self->table_info($tableid) : undef;
my $type;
if ( $table_info && exists $table_info->{sort_block} ) {
foreach my $cfg ( @{ $table_info->{sort_block} } ) {
if ( ref($cfg) eq 'HASH' && $cfg->{blk} == $blk ) {
$type = $cfg->{type};
last;
}
}
}
$type //= ( $blk == 0 ) ? ( $table_info->{id_type} // 'num' ) : 'auto';
return $self->array_sort( $type, $dir, $blk, @$recs_ref );
}
1;
__END__
=head1 NAME
AmberDB::Index - Inverted search, exact field match, binary sort, and URL slug rewrite indexing engine
=head1 SYNOPSIS
# Indexing methods are called directly on the AmberDB instance ($adb):
# 1. URL Slug generation and reverse lookup (.slg)
my $slug = $adb->set_slug("catalog_product", $record_ref, 1);
my $slug_map = $adb->get_slug("catalog_product", 0, 101, 102);
# 2. Normalization of array or delimited values into clean lists and .unq IDs
my @unq_ids = $adb->field_to_list($raw_val, 'write', $table_path, $table_info, $blk);
# 3. Monotonic sort key generation for fixed-width sorting (.srt)
my $key = $adb->normalize_sort_key("1250.50", "num");
=head1 DESCRIPTION
C<AmberDB::Index> manages flat-file inverted search indexes (C<.src>), exact field matching indexes (C<.fld>), primary key lists (C<.inx>), binary pre-sorted record indexes (C<.srt>), and bidirectional URL slug rewrite dictionaries (C<.slg>).
Facet forward indexing (C<.fac>) is handled by C<AmberDB::Index::Facet>, and dual-tier cold record indexing (C<.jinx>, C<.jfld>, C<.jsrc>) is managed by C<AmberDB::Index::Junk>.
B<Inheritance Note:> C<AmberDB> inherits from C<AmberDB::Index> via C<use parent>. All indexing methods can be called directly on C<$adb>.
=head1 METHODS
=head2 field_to_list($value, [$mode], [$table_path], [$table_info], [$blk])
Converts ARRAY references, comma/semicolon-delimited strings, or single scalars into a normalized list of trimmed values.
=over 4
=item * In C<'write'> mode: Registers text values into the per-block unique/dictionary index (C<_${blk}.unq>) with auto-incrementing numeric IDs (or validates foreign key IDs for RDBM fields).
=item * In C<'read'> mode: Resolves existing string IDs from C<_${blk}.unq> without creating new dictionary entries.
=back
my @ids = $adb->field_to_list("Red, Blue, Green", 'write', $path, $info, 3);
=head2 normalize_sort_key($value, $type, [$length])
Normalizes an input value into a fixed-width byte key for fast monotonic sorting in binary C<.srt> files:
=over 4
=item * B<num / decimal>: Adds a C<1e12> (1,000,000,000,000) offset for signed float/integer monotonic sorting (C<%020.6f> format).
=item * B<string / ascii>: Converts to ASCII, removes punctuation, truncates to 8 bytes (or C<$length>), and pads with spaces.
=item * B<date>: Converts date expressions to 14-character C<YYYYMMDDHHMMSS> timestamps.
=back
my $sort_key = $adb->normalize_sort_key("249.90", "num");
=head2 set_slug($table_id, $record, [$write_mode])
Generates a URL-friendly ASCII slug from designated schema title blocks (C<slug_block>) and registers bidirectional mapping in C<_0.slg> (Record ID -E<gt> Slug) and C<_1.slg> (Slug -E<gt> Record ID).
my $slug = $adb->set_slug("catalog_product", \@record, 1);
# => "kablosuz-bluetooth-kulaklik"
=head2 get_slug($table_id, [$type], @record_or_slug_ids)
Resolves URL slugs or reverse-maps slugs back to record IDs.
=over 4
=item * C<$type = 0>: Returns C<{ record_id =E<gt> slug }> (default, reads C<_0.slg>).
=item * C<$type = 1>: Returns C<{ slug =E<gt> record_id }> (reads C<_1.slg>).
=back
my $slugs = $adb->get_slug("catalog_product", 0, 101, 102);
# => { 101 => "kablosuz-kulaklik", 102 => "akilli-saat" }
=head2 rdbm_target($table_info, $blk)
Returns C<($target_table, $target_blk)> if the specified schema block is configured as a relational foreign key (RDBM), or empty list / undef otherwise.
my ($target_table, $target_blk) = $adb->rdbm_target($schema, 2);
=head2 repeat_fields($table_info, @record)
Consolidates dynamic repeat columns (defined in C<$table_info-E<gt>{repeat_ids}> and C<repeat_start>) into a single comma-separated value.
=head2 Low-Level Index Maintenance Methods
These methods are called automatically by AmberDB during CRUD operations (C<insert_id>, C<modify_id>, C<delete_id>):
=over 4
=item * C<records_add / records_del> â Updates C<keys>, C<count>, and C<lastid> in primary C<.inx> files.
=item * C<match_add / match_modify / match_del> â Manages exact-match inverted index files (C<_${blk}.fld>).
=item * C<search_add / search_modify / search_del> â Manages full-text keyword search index files (C<_${blk}.src>).
=item * C<sort_add / sort_modify / sort_del> â Manages binary pre-sorted record index files (C<_${blk}.srt>).
=back
( run in 1.167 second using v1.01-cache-2.11-cpan-d01c6094234 )