/[SWISH-PlusPlus]/trunk/PlusPlus.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /trunk/PlusPlus.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 11 by dpavlin, Sun Dec 5 13:30:57 2004 UTC revision 22 by dpavlin, Tue Dec 7 16:05:43 2004 UTC
# Line 4  use 5.008004; Line 4  use 5.008004;
4  use strict;  use strict;
5  use warnings;  use warnings;
6    
7  our $VERSION = '0.05';  our $VERSION = '0.20';
8    
9  use Carp;  use Carp;
10  use File::Temp qw/ tempdir /;  use File::Temp qw/ tempdir /;
11  #use YAML;  use BerkeleyDB;
12    use Storable qw(store retrieve freeze thaw);
13    use YAML;
14    
15  =head1 NAME  =head1 NAME
16    
17  SWISH::PlusPlus - Perl extension SWISH++  SWISH::PlusPlus - Perl extension for full-text indexer SWISH++ with properties support
18    
19  =head1 SYNOPSIS  =head1 SYNOPSIS
20    
21    use SWISH::PlusPlus;    use SWISH::PlusPlus;
22    blah blah blah  
23      my $i = new SWISH::PlusPlus(
24            index_dir => '/tmp/foo',
25      );
26      $i->add( 42 => 'meaning of life' );
27    
28      print $i->search("meaning");  # returns 42
29    
30  =head1 DESCRIPTION  =head1 DESCRIPTION
31    
32  This is perl module to use SWISH++ indexer by Paul J. Lucas. SWISH++ is  This is perl module to use SWISH++ indexer by Paul J. Lucas. SWISH++ is
33  rewrite of swish-e in C++ which is extremly fast (thank to mmap), but without  rewrite of swish-e in C++ which is extremely fast (due to mmap usage and
34  support for properties (which this module tries to fix).  clever language heuristics), but without support for properties (which this
35    module tries to fix).
36  Implementation of this module is crafted after L<Plucene::Simple> and it  
37  should be easy to replace Plucene with this module for increased  Implementation of API is something in-between C<SWISH::API> and
38  performance. However, this module is not plug-in replacement.  C<Plucene::Simple>. It should be easy to replace Plucene or swish-e with
39    this module for increased performance. However, this module is not plug-in
40    replacement.
41    
42  =head1 METHODS  =head1 METHODS
43    
44  =head2 new  =head2 new
45    
46  Create new indexing object.  Create new instance for index.
47    
48    my $i = SWISH::PlusPlus->new(    my $i = SWISH::PlusPlus->new(
49          index_dir => '/path/to/index',          index_dir => '/path/to/index',
# Line 44  Create new indexing object. Line 54  Create new indexing object.
54          use_stopwords => 1,          use_stopwords => 1,
55    );    );
56    
57  Options to new are following:  Options are described below:
58    
59  =over 5  =over 5
60    
61  =item C<index_dir>  =item C<index_dir>
62    
63  Path to directory in which index will be created.  Path to directory in which index and meta database will be created.
64    
65  =item C<index>  =item C<index>
66    
# Line 70  C<STDERR> prefixed by C<##>. Line 80  C<STDERR> prefixed by C<##>.
80  =item C<meta_in_body>  =item C<meta_in_body>
81    
82  This option (off by default) enables to search content of meta fields  This option (off by default) enables to search content of meta fields
83  without specifing them (like they are in body of document). This will  without specifying them (like they are in body of document). This will
84  somewhat increate index size.  somewhat increase index size.
85    
86  =item C<use_stopwords>  =item C<use_stopwords>
87    
# Line 90  sub new { Line 100  sub new {
100                  croak "need $_" unless $self->{$_};                  croak "need $_" unless $self->{$_};
101          }          }
102    
103          if (! -e $self->{'index_dir'}) {          my $index_dir = $self->{'index_dir'};
104                  mkdir $self->{'index_dir'} || confess "can't create index ",$self->{'index'},": $!";  
105            my $cwd;
106            chomp($cwd = `pwd`);
107            $self->{'cwd'} = $cwd || carp "can't get cwd!";
108            
109            if ($index_dir !~ m#^/#) {
110                    $index_dir = "$cwd/$index_dir";
111                    print STDERR "## full path to index_dir: $index_dir\n" if ($self->{'debug'});
112                    $self->{'index_dir'} = $index_dir;
113            }
114    
115            if (! -e $index_dir) {
116                    mkdir $index_dir || confess "can't create index ",$self->{'index'},": $!";
117          }          }
118    
119          # default executables          # default executables
120          $self->{'index'} ||= 'index';          $self->{'index'} ||= 'index';
121          $self->{'search'} ||= 'search';          $self->{'search'} ||= 'search';
122    
123          print STDERR "## new index_dir: ",$self->{'index_dir'}," index: ",$self->{'index'}, " search: ",$self->{'search'},"\n" if ($self->{'debug'});          print STDERR "## new index_dir: ",$index_dir," index: ",$self->{'index'}, " search: ",$self->{'search'},"\n" if ($self->{'debug'});
124    
125          $self ? return $self : return undef;          $self ? return $self : return undef;
126  }  }
# Line 106  sub new { Line 128  sub new {
128    
129  =head2 check_bin  =head2 check_bin
130    
131  Check if swish++ binaries specified in L<new> are available and verify  Check if SWISH++ binaries specified in L<new> are available and verify
132  version signature.  version signature.
133    
134    if ($i->check_bin) {    if ($i->check_bin) {
# Line 117  It will also setup property Line 139  It will also setup property
139    
140    $i->{'version'}    $i->{'version'}
141    
142  which you can examine to see version.  which you can examined to see numeric version (something like C<6.0.4>).
143    
144  =cut  =cut
145    
# Line 134  sub check_bin { Line 156  sub check_bin {
156          confess $self->{'search'}," binary is not SWISH++" unless ($s =~ m/^SWISH\+\+/);          confess $self->{'search'}," binary is not SWISH++" unless ($s =~ m/^SWISH\+\+/);
157    
158          if ($i eq $s) {          if ($i eq $s) {
159                    $i =~ s/^SWISH\+\+\s+// || confess "can't strip SWISH++ from version";
160                  $self->{'version'} = $i;                  $self->{'version'} = $i;
161                  return 1;                  return 1;
162          } else  {          } else  {
# Line 147  sub check_bin { Line 170  sub check_bin {
170    
171  Quick way to add simple data to index.  Quick way to add simple data to index.
172    
173    $i->index_document($key, $data);    $i->index_document($path, $data);
174    $i->index_document( 42 => 'meaning of life' );    $i->index_document(
175            42 => 'meaning of life',
176            1984 => 'Oh!',
177      );
178    
179    C<$path> value is really path, so you don't want to use directory
180    separators (slashes, /) in it probably.
181    
182  =cut  =cut
183    
# Line 169  sub index_document { Line 198  sub index_document {
198    
199  =head2 add  =head2 add
200    
201  Add document with metadata to index.  Add document with meta-data to index.
202    
203    $i->add(    $i->add(
204          path => 'path/to/document',          path => 'path/to/document',
# Line 189  This is thin wrapper round L<_create_doc Line 218  This is thin wrapper round L<_create_doc
218  sub add {  sub add {
219          my $self = shift;          my $self = shift;
220    
221          $self->_create_doc(@_);          return $self->_create_doc(@_);
222    }
223    
224          return 1;  
225    =head2 delete
226    
227    Delete document from index.
228    
229      $i->delete("document/path");
230    
231    If deletion is succesfull returns revision of deleted document, otherwise
232    undef.
233    
234    =cut
235    
236    sub delete {
237            my $self = shift;
238    
239            my $path = shift || carp "empty path?";
240    
241            print STDERR "## delete: $path\n" if ($self->{'debug'});
242    
243            my $rev = $self->{'meta_db'}->{"R$path"};
244            if ($rev) {
245                    $self->{'_deleted'}->{$path} = $rev;
246                    $self->{'_deleted_counter'}++;
247                    print STDERR "## deleted revision $rev, counter: ",$self->{'_deleted_counter'}++,"\n" if ($self->{'debug'});
248                    return $rev;
249            }
250    
251            return undef;
252  }  }
253    
254    
255  =head2 search  =head2 search
256    
257  Search your index.  Search your index using any valid SWISH++ query.
258    
259    my @results = $i->search("swhish query");    my @results = $i->search("swish query");
260    
261  Returns array with result IDs.  Returns array with elements like this:
262    
263      {
264       rank => 10,                  # rank of result
265       path => 'path to result',    # path to result
266       size => 999,                 # size in bytes
267       title => 'title of result'   # title meta property
268      }
269    
270  =cut  =cut
271    
# Line 208  sub search { Line 274  sub search {
274    
275          my $query = shift || return;          my $query = shift || return;
276    
277          $self->_close_index;          $self->finish_update;
278            $self->_tie_meta_db(DB_RDONLY);
279    
280          my @results;          my @results;
281    
282          # escape double quotes in query for shell          # escape double quotes in query for shell
283          $query =~ s/"/\\"/g;          $query =~ s/"/\\"/g;
284    
285          my $open_cmd = $self->{'search'}." -i ".$self->{'index_dir'}.'/index "'.$query.'" |';          my $open_cmd = $self->{'search'} .
286          print STDERR "## search $open_cmd\n" if ($self->{'debug'});                  ' -i ' . $self->{'index_dir'}.'/index' .
287                    ' "' . $query . '"'.
288                    ' |';
289            print STDERR "## search: $open_cmd\n" if ($self->{'debug'});
290    
291            my %r;
292    
293          open(SEARCH, $open_cmd) || confess "can't start $open_cmd: $!";          open(SEARCH, $open_cmd) || confess "can't start $open_cmd: $!";
294          while(<SEARCH>) {          my $l;
295                  next if (/^#/);          while($l = <SEARCH>) {
296                  chomp;                  next if ($l =~ /^#/);
297                  print STDERR "## $_\n" if ($self->{'debug'});                  chomp($l);
298                  my ($rank,$path,$size,$title) = split(/ /,$_,4);                  print STDERR "## $l\n" if ($self->{'debug'});
299                    my ($rank,$path,$size,$rev,$title) = split(/ /,$l,5);
300                    $path =~ s#^\./##; # strip from path
301    
302                    # get current revision
303                    $r{$path} = $self->{'meta_db'}->{"R$path"};
304    
305                    # skip if old revision
306                    next if ($r{$path} > $rev);
307    
308                    print STDERR "## current revision $rev\n" if ($self->{'debug'});
309    
310                  push @results, {                  push @results, {
311                          rank => $rank,                          rank => $rank,
312                          path => $path,                          path => $path,
313                          size => $size,                          size => $size,
314                          title => $title,                          title => $title,
315                  }                  } unless ($self->{'_deleted'}->{$path} && $self->{'_deleted'}->{$path} <= $rev);
316          }          }
317    
318          close(SEARCH) || confess "can't close search";          close(SEARCH) || confess "can't close search";
# Line 239  sub search { Line 322  sub search {
322          return @results;          return @results;
323  }  }
324    
325    =head2 property
326    
327    Return stored meta property from result or result path.
328    
329      print $i->property('path', 'meta name');
330      print $i->property($res->{'path'}, 'meta name');
331      print $i->property('path');
332      print $i->property($res->{'path'});
333    
334    Returns one meta property (if meta name is specified) or whole hash with
335    all meta properties.
336    
337    =cut
338    
339    sub property {
340            my $self = shift;
341    
342            my $path = shift || return;
343            my $meta = shift;
344    
345            if ($path =~ m/^HASH/) {
346                    $path = $path->{'path'} || confess "can't find path in input data";
347            }
348    
349            my $val = $self->{'meta_db'}->{"M$path"};
350    
351            # FIXME should we die here like swish-e does?
352            return unless ($val);
353    
354            $val = thaw($val);
355    
356            print STDERR "## property $path $meta: ",(Dump($val) || 'undef'),"\n" if ($self->{'debug'});
357    
358            return $val->{$meta} if ($meta);
359    
360            return $val;
361    }
362    
363    =head2 finish_update
364    
365    This method will close index binary and enable search. Searching is not
366    available while indexing is in process.
367    
368      $i->finish_update;
369    
370    Usually, you don't need to call this method directly. It will be called on
371    DESTROY when $i goes out of scope or when you first call search in session
372    if indexing was started.
373    
374    =cut
375    
376    sub finish_update {
377            my $self = shift;
378    
379            print STDERR "## finish_update\n" if ($self->{'debug'});
380    
381            $self->_close_index && $self->_untie_meta_db;
382    }
383    
384    sub DESTROY {
385            my $self = shift;
386            $self->finish_update;
387    }
388    
389  =head1 PRIVATE METHODS  =head1 PRIVATE METHODS
390    
391  Private methods implement internals for creating temporary file needed for  Private methods implement internals for creating temporary files needed for
392  swish++. You should have no need to call them directly, and they are here  SWISH++. You should have no need to call them directly, and they are here
393  just to have documentation.  just to have documentation.
394    
395  =head2 _init_indexer  =head2 _init_indexer
# Line 259  It will also create empty file C<_stopwo Line 406  It will also create empty file C<_stopwo
406  sub _init_indexer {  sub _init_indexer {
407          my $self = shift;          my $self = shift;
408    
409          $self->{'tmp_dir'} = tempdir( CLEANUP => 1 ) || confess "can't create temporary directory: $!";          return if ($self->{'_index_fh'});
410    
411            my $tmp_dir = tempdir( CLEANUP => 1 ) || confess "can't create temporary directory: $!";
412            $self->{'tmp_dir'} = $tmp_dir;
413    
414            chdir $tmp_dir || confess "can't chdir to ".$tmp_dir.": $!";
415    
416            print STDERR "## tmp_dir: $tmp_dir\n" if ($self->{'debug'});
417    
418          chdir $self->{'tmp_dir'} || confess "can't chdir to ".$self->{'tmp_dir'}.": $!";          my $opt = "-v " . ($self->{'debug'} || '0');
419    
420          my $opt = "-v 4";          my $index_dir = $self->{'index_dir'} || confess "no index_dir?";
421            my $index_file = $index_dir . '/index';
422    
423            if (-e $index_file && ! -z $index_file) {
424                    $opt .= ' -I ';
425                    $self->{'_incremental'} = 1;
426                    print STDERR "## using incremental indexing for $index_file\n" if ($self->{'debug'});
427            } else {
428                    $self->{'_incremental'} = 0;
429            }
430    
431          unless ($self->{'use_stopwrods'}) {          unless ($self->{'use_stopwrods'}) {
432                  open(STOP, '>', "_stopwords_") || carp "can't create empty stopword file, skipping\n";                  open(STOP, '>', "_stopwords_") || carp "can't create empty stopword file, skipping\n";
# Line 272  sub _init_indexer { Line 435  sub _init_indexer {
435                  $opt .= " -s _stopwords_";                  $opt .= " -s _stopwords_";
436          }          }
437    
438          my $open_cmd = '| '.$self->{'index'}.' '.$opt.' -e "html:*" -i '.$self->{'index_dir'}.'/index -';          my $open_cmd = '| '.$self->{'index'}.' '.$opt.' -e "html:*" -i '.$index_file.' -';
439    
440            print STDERR "## init_indexer: $open_cmd\n" if ($self->{'debug'});
441    
442            open($self->{'_index_fh'}, $open_cmd) || confess "can't start index with $open_cmd: $!";
443    
444          open($self->{'index_fh'}, $open_cmd) || confess "can't start index with $open_cmd: $!";          chdir $self->{'cwd'} || confess "can't chdir to ".$self->{'cwd'}.": $!";
445    
446            $self->_tie_meta_db(DB_CREATE);
447    
448          return $self->{'index_fh'};          return $self->{'_index_fh'};
449  }  }
450    
451  =head2 _create_doc  =head2 _create_doc
452    
453  Create temporary file and pass it's name to swish++  Create temporary file and pass it's name to SWISH++
454    
455    $i->_create_doc(    $i->_create_doc(
456          path => 'path/to/store/in/index',          path => 'path/to/store/in/index',
# Line 295  Create temporary file and pass it's name Line 462  Create temporary file and pass it's name
462          }          }
463    );    );
464    
 To delete document, just omit body and meta data.  
   
465  =cut  =cut
466    
467  sub _create_doc {  sub _create_doc {
# Line 305  sub _create_doc { Line 470  sub _create_doc {
470          my $arg = {@_};          my $arg = {@_};
471    
472          # open indexer if needed          # open indexer if needed
473          $self->{'index_fh'} ||= $self->_init_indexer;          $self->_init_indexer;
474    
475          my $path = $self->{'tmp_dir'} || confess "no tmp_dir?";          my $path = $self->{'tmp_dir'} || confess "no tmp_dir?";
476            my $id = $arg->{'path'} || confess "no path?";
477            $path .= "/$id";
478    
479            my $rev = $self->{'rev'}++;
480    
481          open(TMP, '>', $arg->{'path'}) || die "can't create temp file ".$arg->{'path'}.": $!";          print STDERR "## _create_doc: $path [$rev]\n" if ($self->{'debug'});
482    
483            open(TMP, '>', $path) || die "can't create temp file $path: $!";
484    
485          print TMP '<html><head>';          print TMP '<html><head>';
486    
487          $arg->{'body'} ||= '';          my $body = $arg->{'body'};
488    
489            if (defined($body)) {
490                    $self->{'meta_db'}->{"B$id"} = $body;
491            } else {
492                    $body = '';
493            }
494    
495            my $title = $arg->{'title'};
496    
497          if ($arg->{'meta'}) {          if ($arg->{'meta'}) {
498                  foreach my $name (keys %{$arg->{'meta'}}) {                  foreach my $name (keys %{$arg->{'meta'}}) {
499                          my $content = $arg->{'meta'}->{$name};                          my $content = $arg->{'meta'}->{$name};
500                          print TMP qq{<meta name="$name" content="$content">};                          print TMP qq{<meta name="$name" content="$content">};
501                          $arg->{'body'} .= " $content" if ($self->{'meta_in_body'});                          $body .= " $content" if ($self->{'meta_in_body'});
502                  }                  }
503                    $arg->{'meta'}->{'title'} = $title;
504                    $self->{'meta_db'}->{"M$id"} = freeze($arg->{'meta'});
505          }          }
506    
507          if (defined($arg->{'title'})) {          if (defined($title)) {
508                  print TMP '<title>' . ($arg->{'title'} || '') . '</title>';                  $title = "$rev $title";
509                  $arg->{'body'} .= " ".$arg->{'title'} if ($self->{'meta_in_body'});                  $body .= " $title" if ($self->{'meta_in_body'});
510            } else {
511                    $title = "$rev $id";
512          }          }
513    
514          print TMP '</head><body>' . $arg->{'body'} . '</body></html>';          # dump html
515            print TMP "<title>$title</title></head><body>$body</body></html>";
516                    
517          close(TMP) || confess "can't close tmp file ".$arg->{'path'}.": $!";          close(TMP) || confess "can't close tmp file ".$arg->{'path'}.": $!";
518    
519          print { $self->{'index_fh'} } $arg->{'path'}."\n";          print { $self->{'_index_fh'} } "$id\n" || confess "can't pass document $id to indexer: $!";
520            
521            $self->{'meta_db'}->{"R$id"} = $rev;
522    
523            # FIXME this is probably not the right place to update global
524            # maximum revision, but it keeps database in sane state
525            $self->{'meta_db'}->{"Crev"} = $rev;
526  }  }
527    
528  =head2 _close_index  =head2 _close_index
# Line 348  You have to close index before searching Line 538  You have to close index before searching
538  sub _close_index {  sub _close_index {
539          my $self = shift;          my $self = shift;
540    
541          return unless ($self->{'index_fh'});          $self->_store_deleted;
542    
543            return unless ($self->{'_index_fh'});
544    
545          print STDERR "## close index\n" if ($self->{'debug'});          print STDERR "## close index\n" if ($self->{'debug'});
546    
547          close($self->{'index_fh'});          close($self->{'_index_fh'}) || confess "can't close index: $!";
548          undef $self->{'index_fh'};          undef $self->{'_index_fh'};
549    
550            if ($self->{'_incremental'}) {
551                    print STDERR "## move new index over old\n" if ($self->{'debug'});
552                    rename $self->{'index_dir'}.'/index.new',$self->{'index_dir'}.'/index' || die "can't move new index over old one: $!";
553            }
554    
555            return 1;
556    }
557    
558    =head2 _tie_meta_db
559    
560    Open BerkeleyDB database with meta properties.
561    
562      $i->_tie_meta_db(DB_CREATE);
563      $i->_tie_meta_db(DB_RDONLY);
564    
565    }
566    
567    =cut
568    
569    sub _tie_meta_db  {
570            my $self = shift;
571    
572            my $flags = shift || confess "need DB_CREATE or DB_RDONLY";
573    
574            return if ($self->{'_meta_db_flags'} && $self->{'_meta_db_flags'} == $flags);
575    
576            print STDERR "## _tie_meta_db($flags)\n" if ($self->{'debug'});
577    
578            $self->_untie_meta_db;
579            $self->{'_meta_db_flags'} = $flags;
580    
581            my $file = $self->{'index_dir'}.'/meta.db';
582    
583            tie %{$self->{'meta_db'}}, "BerkeleyDB::Hash",
584                    -Filename => $file,
585                    -Flags    => $flags
586            or confess "cannot open $file: $! $BerkeleyDB::Error\n" ;
587    
588            $self->{'rev'} = $self->{'meta_db'}->{'Crev'} || 0;
589    
590            my $delref = $self->{'meta_db'}->{'Cdeleted'};
591            if ($delref) {
592                    $self->{'_deleted'} = thaw($delref);
593    
594                    print "## deleted ",keys %{$self->{'_deleted'}}," records\n" if ($self->{'debug'});
595            } else {
596                    $self->{'_deleted'} = {};
597            }
598    
599            $self->{'_deleted_counter'} = 0;
600            return 1;
601    }
602    
603    =head2 _untie_meta_db
604    
605    Close BerkeleyDB database with meta properties.
606    
607      $i->_untie_meta_db;
608    
609    =cut
610    
611    sub _untie_meta_db {
612            my $self = shift;
613    
614            return unless ($self->{'meta_db'});
615    
616            print STDERR "## _untie_meta_db\n" if ($self->{'debug'});
617            untie %{$self->{'meta_db'}} || confess "can't untie!";
618            undef $self->{'meta_db'};
619            undef $self->{'_meta_db_flags'};
620    
621            return 1;
622    }
623    
624    
625    =head2 _store_deleted
626    
627    Save hash of deleted files using L<Storable>.
628    
629      $i->_store_deleted;
630    
631    =cut
632    
633    sub _store_deleted {
634            my $self = shift;
635    
636            return if (! $self->{'_deleted_counter'});
637    
638            print STDERR "## save deleted ",Dump($self->{'_deleted'}) if ($self->{'debug'});
639    
640            my $d = freeze($self->{'_deleted'});
641    
642            $self->_tie_meta_db(DB_CREATE);
643    
644            $self->{'meta_db'}->{'Cdeleted'} = $d ||
645                    carp "can't store deleted: $!";
646    
647            # reset counter
648            $self->{'_deleted_counter'} = 0;
649  }  }
650    
651  1;  1;
# Line 367  None by default. Line 659  None by default.
659    
660  =head2 Debian  =head2 Debian
661    
662  Debian version of swish++ is often old (version 5 at moment of this writing  Debian version of SWISH++ is often old (version 5 at moment of this writing
663  while version 6 is available in source code), so this module by default  while version 6 is available in source code), so this module by default
664  uses executable names B<index> and B<search> for self-compiled version  uses executable names B<index> and B<search> for self-compiled version
665  instead of one from Debian package. See L<new> how to specify Debian  instead of one from Debian package. See L<new> how to specify Debian
# Line 375  default binaries B<index++> and B<search Line 667  default binaries B<index++> and B<search
667    
668  =head2 SWISH++  =head2 SWISH++
669    
670  Aside from very good rewrite in C++, SWISH++ is fatster because it has  Aside from very good rewrite in C++, SWISH++ is faster because it uses
671  claver heuristics about which data in input files are words to index and  claver heuristics about which data in input files are words to index and
672  which are not. It's based on English language and might be best choice if  which are not. It's based on English language and might be best choice if
673  you plan to install large amount of long text documents.  you plan to index large amount of long text documents.
674    
675  However, if you plan to index all data from structured storage (e.g. RDBMS)  However, if you plan to index all data from structured storage (e.g. RDBMS)
676  you might want B<all> words from data to end up in index as opposed to just  you might want B<all> words from data to end up in index as opposed to just
# Line 386  those which look like English words. Thi Line 678  those which look like English words. Thi
678  don't plan to index English texts with this module.  don't plan to index English texts with this module.
679    
680  With distribution build versions of SWISH++ you might have problems with  With distribution build versions of SWISH++ you might have problems with
681  disepearing words. To overcome this problem, you will have to compile and  disapearing words. To overcome this problem, you will have to compile and
682  configure SWISH++ yourself (because language characteristics are  configure SWISH++ yourself (because language characteristics are
683  compilation-time option).  compilation-time option).
684    
# Line 402  configuration is needed for B<date test> Line 694  configuration is needed for B<date test>
694  doesn't recognize 2004-12-05 as date. Have in mind that your index size  doesn't recognize 2004-12-05 as date. Have in mind that your index size
695  might explode.  might explode.
696    
697    =head1 BUGS
698    
699    Currently there is no way to specify which meta data will be stored as
700    properties. B<This will be fixed very soon>.
701    
702    There is no garbage collection on temporary files created for SWISH++. This
703    means that one run of indexer will take additional disk space for temporary
704    files, which will be removed at end. There should be some way to remove
705    files after they are indexed by SWISH++. However, at this early stage of
706    development it's just not supported yet. Have plenty of disk space!
707    
708  =head1 SEE ALSO  =head1 SEE ALSO
709    
710  C<swish++> web site L<http://homepage.mac.com/pauljlucas/software/swish/>  SWISH++ web site L<http://homepage.mac.com/pauljlucas/software/swish/>
711    
712  =head1 AUTHOR  =head1 AUTHOR
713    

Legend:
Removed from v.11  
changed lines
  Added in v.22

  ViewVC Help
Powered by ViewVC 1.1.26