Subject: | Wrong (?) beahavour of search using KiokuDB::Backend::BDB::GIN |
perl version: 5.8.8
os version: Linux fireball 2.6.18-6-686 #1 SMP Tue Jun 17 21:31:27 UTC
2008 i686 GNU/Linux
KiokuDB version: 0.27
KiokiDB::Backend::BDB version: 0.14
BerkeleyDB (perl package) version: 0.38
BerkeleyDB version: 4.7.25
I found some issues using manual GIN search with
KiojuDB::Backend::DBD::GIN :
1. Warns like 'Reference is already weak at
/home/self/lib/KiokuDB/LiveObjects.pm line 324'
2. OR-like result set while make search with multi-keyed query (waitng
AND-like result set)
3. Duplicated objects in result set while make search with multy-keyed
query
4. Re-deserializing entries, that are already exists in live objects
How to reproduce: execute attached script, look at warnigns and test.
Patch (attached):
Simplest way i found to fix this bug is to rewrite search method in
KiokuDB::Backend::BDB::GIN. Main idea is to use BerkeleyDB equlity
joins, wich take all heavy work on itself.
Subject: | KiokuDB-Backend-BDB-GIN-patch.pm |
# i'm sory for possible negative-style design of this patch;
# there is my first public patch
# there is only search method redefined
package KiokuDB::Backend::BDB::GIN;
use Moose;
use BerkeleyDB;
sub search
{
my ( $self, $query, @args ) = @_;
my %args = (
distinct => $self->distinct,
@args,
);
my $live_objects = $args{live_objects};
my %spec = $query->extract_values($self);
my @cursors;
# create cursor for each requested field
foreach my $key (@{ $spec{values} }){
my $cur = $self->secondary_db->db_cursor;
my $data = '';
my $ret = $cur->c_get($key, $data, DB_SET);
if ($ret == DB_NOTFOUND){
return new Data::Stream::Bulk::Nil;
}
confess "Can't join tables: $ret" unless $ret == 0;
push @cursors, $cur;
}
# we want retrieve all records if no fields requested
my $cursor = scalar @cursors
? $self->primary_db->db_join(\@cursors)
: $self->secondary_db->db_cursor;
my $flags = scalar @cursors ? 0 : DB_NEXT;
return new Data::Stream::Bulk::Callback callback => sub
{
my ($id, $data) = ('', '');
my $ret = $cursor->c_get($id, $data, $flags);
if ($ret == 0){
# wan't desereilize fetched objects, use exited
my ($entry) = $live_objects->ids_to_entries($id);
unless (defined $entry){
$entry = $self->deserialize($data);
}
return [$entry];
} elsif ($ret == DB_NOTFOUND){
$cursor->c_close;
return;
} else {
throw LGame::Err "Error while fetching db: $ret";
}
}
}
__PACKAGE__->meta->make_immutable;
1;
Subject: | kioku-test_search.pl |
{
package App::Object;
use Moose;
has indexed_field => (
is => 'rw',
isa => 'Int'
);
__PACKAGE__->meta->make_immutable;
1;
}
package main;
use KiokuDB;
use Search::GIN::Extract::Callback;
use Search::GIN::Query::Manual;
use Test::Simple tests => 2;
my $dir = KiokuDB->connect(
"bdb-gin:dir=/home/self/tmp/db/",
create => 1,
extract => Search::GIN::Extract::Callback->new(extract =>
sub {
my ( $object, $extractor, @args) = @_;
my $index = { class => ref $object };
if ( $object->isa('App::Object') ){
$index->{indexed_field} = $object->indexed_field;
}
return $index;
})
);
{
my $scope = $dir->new_scope;
$dir->store(App::Object->new(indexed_field => 123));
$dir->store(App::Object->new(indexed_field => 777));
}
{
my $scope = $dir->new_scope;
my $query = Search::GIN::Query::Manual->new(
values => {
class => 'App::Object',
indexed_field => 123
},
);
# i'm waiting here to recieve 1 object
# that will be an instance of App::Object AND
# have indexed_field == 123
@result = $dir->search($query)->all;
ok @result == 1; # not ok, @result == 3 here, looks good with patch
}