The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Wiki::Toolkit::Search::DBIxFTS;

use strict;
use DBIx::FullTextSearch;
use Carp "croak";
use base 'Wiki::Toolkit::Search::Base';

use vars qw( @ISA $VERSION );

$VERSION = 0.05;

=head1 NAME

Wiki::Toolkit::Search::DBIxFTS - DBIx::FullTextSearch search plugin for Wiki::Toolkit.

=head1 REQUIRES

DBIx::FullTextSearch

=head1 SYNOPSIS

  my $store = Wiki::Toolkit::Store::MySQL->new(
                                    dbname => "wiki", dbpass=>"wiki" );
  my $search = Wiki::Toolkit::Search::DBIxFTS->new( dbh => $store->dbh );
  my %wombat_nodes = $search->search_nodes("wombat");

Provides search-related methods for Wiki::Toolkit.

See also L<Wiki::Toolkit::Search::Base>, for methods not documented here.

=cut

=head1 METHODS

=over 4

=item B<new>

  my $search = Wiki::Toolkit::Search::DBIxFTS->new( dbh => $dbh );

You must supply a handle to a database that has the
DBIx::FullTextSearch indexes already set up. (Currently though there's
no checking that what you supply is even a database handle at all, let
alone one that is compatible with DBIx::FullTextSearch.)

=cut

sub _init {
    my ($self, %args) = @_;
    croak "Must supply a database handle" unless $args{dbh};
    $self->{_dbh} = $args{dbh};
    return $self;
}

# We can't use the base version, since we're doing the analysis
# differently between searching and indexing
sub search_nodes {
    my ($self, $termstr, $and_or) = @_;

    $and_or = uc($and_or);
    unless ( defined $and_or and $and_or eq "OR" ) {
        $and_or = "AND";
    }

    # Extract individual search terms - first phrases (between double quotes).
    my @terms = ($termstr =~ m/"([^"]+)"/g);
    $termstr =~ s/"[^"]*"//g;
    # And now the phrases are gone, just split on whitespace.
    push @terms, split(/\s+/, $termstr);

    # If this is an AND search, tell DBIx::FTS we want every term.
    @terms = map { "+$_" } @terms if $and_or eq "AND";

    # Open and perform the FTS.
    my $dbh = $self->{_dbh};
    my $fts = DBIx::FullTextSearch->open($dbh, "_content_and_title_fts");
    my @finds = $fts->econtains(@terms);

    # Well, no scoring yet, you see.
    return map { $_ => 1 } @finds;
}

sub index_node {
    my ($self, $node) = @_;

    my $dbh = $self->{_dbh};
    my $fts_all = DBIx::FullTextSearch->open($dbh, "_content_and_title_fts");
    my $fts_titles = DBIx::FullTextSearch->open($dbh, "_title_fts");

    $fts_all->index_document($node);
    $fts_titles->index_document($node);

    delete $fts_all->{db_backend}; # hack around buglet in DBIx::FTS
    delete $fts_titles->{db_backend}; # ditto
}

sub delete_node {
    my ($self, $node) = @_;
    my $dbh = $self->{_dbh};
    my $fts_all    = DBIx::FullTextSearch->open($dbh, "_content_and_title_fts")
        or croak "Can't open _content_and_title_fts";
    my $fts_titles = DBIx::FullTextSearch->open($dbh, "_title_fts")
        or croak "Can't open _title_fts";
    eval { $fts_all->delete_document($node); };
    croak "Couldn't delete from full index: $@" if $@;
    eval { $fts_titles->delete_document($node); };
    croak "Couldn't delete from title-only index: $@" if $@;
    return 1;
}

sub supports_phrase_searches { return 1; }
sub supports_fuzzy_searches  { return 0; }

=back

=head1 SEE ALSO

L<Wiki::Toolkit>,  L<Wiki::Toolkit::Search::Base>.

=cut

1;