File Coverage

blib/lib/Search/Typesense.pm
Criterion Covered Total %
statement 46 53 86.7
branch 1 4 25.0
condition n/a
subroutine 15 18 83.3
pod 2 3 66.6
total 64 78 82.0


line stmt bran cond sub pod time code
1             package Search::Typesense;
2              
3 4     4   67810 use v5.16.0;
  4         15  
4              
5 4     4   581 use Moo;
  4         7145  
  4         24  
6             with 'Search::Typesense::Role::Request';
7              
8 4     4   3460 use Mojo::JSON qw(decode_json encode_json);
  4         203850  
  4         290  
9 4     4   707 use Mojo::UserAgent;
  4         232451  
  4         38  
10 4     4   194 use Mojo::URL;
  4         11  
  4         21  
11 4     4   140 use Carp qw(croak);
  4         7  
  4         196  
12              
13 4     4   1855 use Search::Typesense::Document ();
  4         12  
  4         134  
14 4     4   2025 use Search::Typesense::Collection;
  4         12  
  4         138  
15 4     4   2000 use Search::Typesense::Version;
  4         14  
  4         156  
16 4         27 use Search::Typesense::Types qw(
17             ArrayRef
18             Bool
19             Enum
20             HashRef
21             InstanceOf
22             NonEmptyStr
23             PositiveInt
24             compile
25 4     4   32 );
  4         7  
26              
27             =head1 NAME
28              
29             Search::Typesense - Perl interface to Typesense search engine.
30              
31             =head1 SYNOPSIS
32              
33             my $typesense = Search::Typesense->new(
34             host => $host, # required
35             api_key => $key, # required
36             port => $port, # defaults to 8108
37             use_https => $bool, # defaults to true
38             );
39            
40             my $results = $typesense->search(
41             $collection_name,
42             { q => 'Search String' },
43             );
44             if ( $results->{found} ) {
45             foreach my $hit ( @{ $results->{hits} } ) {
46             ...;
47             }
48             }
49              
50             L<Check here for a comparison to ElasticSearch and similar technologies|https://typesense.org/typesense-vs-algolia-vs-elasticsearch-vs-meilisearch/>.
51              
52             =head1 DESCRIPTION
53              
54             B<ALPHA CODE>. The interface can and will change without warning.
55              
56             This is an interface to the L<Typesense|https://typesense.org/> search
57             engine. Most methods will do one of three things:
58              
59             =over 4
60              
61             =item * Return results as defined in the Typesense documentation (listed per section)
62              
63             =item * Return nothing if Typesense returns a 404.
64              
65             =item * C<croak> if Typesense returns an error.
66              
67             =back
68              
69             =cut
70              
71             our $VERSION = '0.07';
72              
73             has collections => (
74             is => 'lazy',
75             isa => InstanceOf ['Search::Typesense::Collection'],
76             init_arg => undef,
77             handles => [qw/search/],
78             builder => sub {
79 0     0   0 my $self = shift;
80 0         0 return Search::Typesense::Collection->new(
81             user_agent => $self->_ua,
82             url => $self->_url_base,
83             );
84             },
85             );
86              
87             has documents => (
88             is => 'lazy',
89             isa => InstanceOf ['Search::Typesense::Document'],
90             init_arg => undef,
91             builder => sub {
92 0     0   0 my $self = shift;
93 0         0 return Search::Typesense::Document->new(
94             user_agent => $self->_ua,
95             url => $self->_url_base,
96             );
97             },
98             );
99              
100             # this sub without a body is called a "forward declaration" and it allows the
101             # requires() in Search::Typesense::Role::Request to realize that we really do
102             # provide these methods. In this case, it also prevents the following error
103             # from being generated by the roles consumed:
104             #
105             # Error: You cannot overwrite a locally defined method (_ua) with a reader
106             # at .../lib/Search/Typesense.pm line 121.
107              
108             sub _ua;
109             has _ua => (
110             is => 'lazy',
111             isa => InstanceOf ['Mojo::UserAgent'],
112             builder => sub {
113 3     3   55 my $self = shift;
114 3         36 my $ua = Mojo::UserAgent->new;
115 3         27 my $key = $self->api_key;
116             $ua->on(
117             start => sub {
118 3     3   1468 my ( $ua, $tx ) = @_;
119 3         26 $tx->req->headers->header(
120             'Content-Type' => 'application/json' )
121             ->header( 'X-TYPESENSE-API-KEY' => $key );
122             }
123 3         39 );
124 3         78 return $ua;
125             },
126             );
127              
128             sub _url_base;
129             has _url_base => (
130             is => 'lazy',
131             isa => InstanceOf ['Mojo::URL'],
132             builder => sub {
133 3     3   50 my $self = shift;
134 3         32 my $url = Mojo::URL->new;
135 3 50       86 $url->scheme( $self->use_https ? 'https' : 'http' );
136 3         141 $url->host( $self->host );
137 3         33 $url->port( $self->port );
138 3         60 return $url;
139             },
140             );
141              
142             has use_https => (
143             is => 'ro',
144             isa => Bool,
145             required => 1,
146             default => 1,
147             );
148              
149             has api_key => (
150             is => 'ro',
151             isa => NonEmptyStr,
152             required => 1,
153             );
154              
155             has host => (
156             is => 'ro',
157             isa => NonEmptyStr,
158             required => 1,
159             );
160              
161             has port => (
162             is => 'ro',
163             isa => PositiveInt,
164             default => 8108,
165             );
166              
167             sub BUILD {
168 3     3 0 23950 my $self = shift;
169 3         23 $self->assert_is_running;
170             }
171              
172             =head1 CONSTRUCTOR
173              
174             The constructor takes a list (or hashref) of key/value pairs.
175              
176             my $typesense = Search::Typesense->new(
177             host => $host, # required
178             api_key => $key, # required
179             port => $port, # defaults to 8108
180             use_https => $bool, # defaults to true
181             );
182              
183             =head2 C<api_key>
184              
185             The api key to which will be sent as the C<X-TYPESENSE-API-KEY> header.
186              
187             =head2 C<host>
188              
189             The hostname to connect to.
190              
191             =head2 C<port>
192              
193             Optional port number to connect to. Defaults to 8108 if not supplied.
194              
195             =head2 C<use_https>
196              
197             Optional boolean. Whether or not to connect to Typesense over https. Default true.
198              
199             =head1 METHODS
200              
201             For CRUD operations on collections and documents, see the documentation for
202             C<collections> (L<Search::Typesense::Collection>) and C<documents>
203             (L<Search::Typesense::Document>).
204              
205             =head2 C<collections>
206              
207             my $collections = $typesense->collections;
208             my $collection = $collections->get($collection_name);
209             my $results = $collections->search($collection_name, {q => 'London'});
210              
211             Returns an instance of L<Search::Typesense::Collection> for managing Typesense collections.
212              
213             =head2 C<search>
214              
215             my $results = $typesense->search($collection_name, {q => 'London'});
216              
217             Shorthand that delegated to C<< $typesense->collections->search(...) >>.
218              
219             We provide this on the top-level C<$typesense> object because this is the
220             common case.
221              
222             =head2 C<documents>
223              
224             my $documents = $typesense->documents;
225             my $document = $documents->delete($collection_name, $document_id);
226              
227             Returns an instance of L<Search::Typesense::Document> for managing Typesense documents.
228              
229             =head2 C<assert_is_running>
230              
231             $typesense->assert_is_running;
232              
233             This does nothing if we can connect to Typesense. Otherwise, this method will
234             C<croak> with a message explaining the error.
235              
236             =cut
237              
238             sub assert_is_running {
239 3     3 1 7 my $self = shift;
240 3         25 $self->_GET( path => ['health'] );
241             }
242              
243             =head2 C<typesense_version>
244              
245             my $version = $typesense->typesense_version;
246              
247             Returns an instance of L<Search::Typesense::Version>.
248              
249             If your version of Typesense is older than C<0.8.0>, this method will return
250             nothing.
251              
252             =cut
253              
254             sub typesense_version {
255 0     0 1   my $self = shift;
256 0 0         my $result = $self->_GET( path => ['debug'] ) or return;
257             return Search::Typesense::Version->new(
258 0           version_string => $result->{version} );
259             }
260              
261             1;
262              
263             __END__
264              
265             =head1 INTERNATIONALIZATION (I18N)
266              
267             Currently Typesense supports languages that use spaces as a word separator. In
268             the future, a new tokenizer will be added to support languages such as Chinese
269             or Japanese. I do not know the timeframe for this.
270              
271             =head1 AUTHOR
272              
273             Curtis "Ovid" Poe, C<< <ovid at allaroundtheworld.fr> >>
274              
275             =head1 BUGS
276              
277             Please report any bugs or feature requests to
278             C<https://github.com/Ovid/Search-Typesense/issues>. I will be notified, and
279             then you'll automatically be notified of progress on your bug as I make
280             changes.
281              
282             =head1 SUPPORT
283              
284             You can find documentation for this module with the perldoc command.
285              
286             perldoc Search::Typesense
287              
288             You can also look for information at:
289              
290             =over 4
291              
292             =item * Github Repo
293              
294             L<https://github.com/Ovid/Search-Typesense/>
295              
296             =item * Issue Tracker
297              
298             L<https://github.com/Ovid/Search-Typesense/issues>
299              
300             =item * Search CPAN
301              
302             L<https://metacpan.org/release/Search-Typesense>
303              
304             =back
305              
306             =head1 ACKNOWLEDGEMENTS
307              
308             Thanks for Sebastian Reidel and Matt Trout for feedback.
309              
310             =head1 LICENSE AND COPYRIGHT
311              
312             This software is Copyright (c) 2021 by Curtis "Ovid" Poe.
313              
314             This is free software, licensed under:
315              
316             The Artistic License 2.0 (GPL Compatible)