package A2B::Index; use strict; sub new { my $package = shift; my $table = shift; my @field_names = @_; my @fields = @{$table->{fields}}{@field_names}; my $self = bless { field_names => \@field_names, fields => \@fields, data => {}, }, $package; $table->_add_index($self); return $self; } sub name { my $self = shift; my $name = join '-', @{$self->{field_names}}; return $name; } sub add_row { my $self = shift; my $row = shift; my @key = @$row[@{$self->{fields}}]; my $place = $self->{data}; while (@key > 1) { $place = $place->{shift @key} ||= {}; } push @{$place->{$key[0]}}, $row; } sub get { my $self = shift; my @key = @_; my $place = $self->{data}; while (@key && defined $place) { $place = $place->{shift @key}; } my $depth = @{$self->{fields}} - @_; if ($depth > 0 && defined $place) { # we need to delve inside the rest of the index # this is a little ugly, & perhaps slow? my @list = values %$place; while (--$depth > 0) { @list = map { values %$_ } @list; } @list = map { @$_ } @list; return \@list; } return defined $place ? $place : []; } # warning - if you call query with undefined values in the template_row, it may # go into an infinite loop sub query { my $self = shift; my $template_row = shift; my @key = @$template_row[@{$self->{fields}}]; pop @key until defined $key[-1]; for (@key) { ! defined $_ and die "invalid query" } return $self->get(@key); } sub keys { my $self = shift; my $depth = @_ ? shift : @{$self->{fields}}; die "bad depth" if $depth == 0 || $depth > @{$self->{fields}}; return _collect_keys($self->{data}, $depth); } sub _collect_keys { my ($hash, $depth) = @_; if ($depth == 1) { return [CORE::keys(%$hash)]; } return map {[$_ , _collect_keys($hash->{$_}, $depth-1)]} CORE::keys(%$hash); } sub data { my $self = shift; return $self->{data}; } 1