The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
package Ploonga;

use strict;
use warnings;
use Carp;
use Groonga::API;
use Groonga::API::Constants qw/GRN_CTX_USE_QL/;
use JSON::XS ();
no bytes;

use constant DEFAULT_PORT => 10041;

our $VERSION = $Groonga::API::VERSION;

sub new {
  my ($class, %args) = @_;

  Groonga::API::init();

  $args{_ctx} = my $ctx = Groonga::API::ctx_open(GRN_CTX_USE_QL)
    or croak "failed to create a Groonga context";

  if (my $dbfile = $args{dbfile}) {
    if (-f $dbfile) {
      $args{_db} = Groonga::API::db_open($ctx, $dbfile)
        or croak "failed to open a Groonga database";
    }
    elsif (!$args{no_create}) {
      $args{_db} = Groonga::API::db_create($ctx, $dbfile, undef)
        or croak "failed to create a Groonga database";
    }
    else {
      croak "failed to open a Groonga database";
    }
  }
  elsif (my $host = $args{host}) {
    my $port = $args{port} || DEFAULT_PORT;
    my $rc = Groonga::API::ctx_connect($ctx, $host, $port, 0);
    croak "failed to connect to $host:$port ($rc)" if $rc;
  }

  $args{_json} = JSON::XS->new->utf8($args{encoding} ? 1 : 0);

  bless \%args, $class;
}

sub do {
  my ($self, @args) = @_;

  my $ctx = $self->{_ctx} or croak "no Groonga context";
  my $db = $self->{_db} or croak "no Groonga database";

  my $rc;

  for my $arg (@args) {
    my $encoded = $self->{encoding} ? Encode::encode($self->{encoding}, $arg) : $arg;
    $rc = Groonga::API::ctx_send($ctx, $encoded, bytes::length($encoded), 0);
    croak "command failed ($rc)" if $rc;
  }

  $rc = Groonga::API::ctx_recv($ctx, my $res, my $len, my $flags);
  croak "command failed ($rc)" if $rc;
  $res = substr($res, 0, $len);

  if ($self->{encoding}) {
    $res = Encode::decode($self->{encoding}, $res);
  }

  eval { $self->{_json}->decode($res) } || $res;
}

sub ctx { shift->{_ctx} }
sub db  { shift->{_db} }

sub DESTROY {
  my $self = shift;
  if (my $ctx = $self->{_ctx}) {
    if (my $db = $self->{_db}) {
      Groonga::API::obj_unlink($ctx, $db);
    }
    if (Groonga::API::get_major_version() > 1) {
      Groonga::API::ctx_close($ctx);
    } else {
      Groonga::API::ctx_fin($ctx);
    }
  }
  Groonga::API::fin();
}

1;

__END__

=head1 NAME

Ploonga - (yet another) interface to Groonga

=head1 SYNOPSIS

  use Ploonga;

  # Standalone mode
  my $ploonga = Ploonga->new(
    dbfile => 'db/test.db',
    no_create => 0, # set this to true if necessary
  );

  # Client mode
  my $ploonga = Ploonga->new(
    host => 'localhost',
    port => 10041,
  );

  # You can pass whatever builtin Groonga client accepts.
  my $ret = $ploonga->do('table_create --name Site --flags TABLE_HASH_KEY --key_type ShortText');

  # Extra args, instead of passing via stdin
  my $ret = $ploonga->do('load --table Site', <<'JSON');
  [
  {"_key":"http://example.org/","title":"This is test record 1!"},
  {"_key":"http://example.net/","title":"test record 2."},
  {"_key":"http://example.com/","title":"test test record three."},
  {"_key":"http://example.net/afr","title":"test record four."},
  {"_key":"http://example.org/aba","title":"test test test record five."},
  {"_key":"http://example.com/rab","title":"test test test test record six."},
  {"_key":"http://example.net/atv","title":"test test test record seven."},
  {"_key":"http://example.org/gat","title":"test test record eight."},
  {"_key":"http://example.com/vdw","title":"test test record nine."},
  ]
  JSON

=head1 DESCRIPTION

Unless you really want to do some complex stuff, this is the module you want to use to communicate with a Groonga database/server. The interface is almost the same as the builtin Groonga client. You simply don't need to quote commands nor decode json output by yourself.

If you do need, try L<Groonga::API>, which provides raw interface to Groonga C APIs.

=head1 METHODS

=head2 new

Creates a client object. Available options are: dbfile/no_create (for standalone mode), host/port (for client mode).

You can also set C<encoding> option to decode/encode when you send/receive data.

=head2 do

Takes a string expression which must contain a Groonga command (and optional string arguments to send), and returns the result, which may be a scalar, or some complex data structure.

=head2 ctx, db

Accessors to internal objects. Only useful when you use L<Groonga::API>.

=head1 SEE ALSO

L<Groonga::API>

L<http://groonga.org/>

L<https://github.com/yappo/p5-Groonga/>

=head1 AUTHOR

Kenichi Ishigaki, E<lt>ishigaki@cpan.orgE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2013 by Kenichi Ishigaki.

This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.

=cut