NAME
Couch::DB - CouchDB database client
INHERITANCE
Couch::DB is extended by
Couch::DB::Mojolicious
SYNOPSIS
use Couch::DB::Mojolicious ();
my $couch = Couch::DB::Mojolicious->new(api => '3.3.3');
my $db = $couch->db('my-db'); # Couch::DB::Database object
my $cluster = $couch->cluster; # Couch::DB::Cluster object
my $client = $couch->createClient(...); # Couch::DB::Client
DESCRIPTION
When this module was written, there were already a large number of CouchDB implementations on CPAN. Still, there was a need for one more. This implementation provides a thick interface: a far higher level of abstraction than the other modules. This should make your work much, much easier.
Also, open https://perl.overmeer.net/couch-db/reference.html in a browser window, as useful cross-reference: parameters for CouchDB are not documented in this Perl documentation! For those, you need to visit https://docs.couchdb.org/en/stable/.
Please read the "DETAILS" section, further down, at least once before you start!
METHODS
Constructors
- Couch::DB->new(%options)
-
Create a relation with a CouchDB server (cluster). You should use totally separated Couch::DB-objects for totally separate database clusters. Note: you can only instantiate extensions of this class.
When you do not specify a
server
-url, but have an environment variablePERL_COUCH_DB_SERVER
, then server url, username, and password are derived from it.-Option --Default api <required> auth 'BASIC' password undef server "http://127.0.0.1:5984" to_json +{ } to_perl +{ } to_query +{ } username undef
- api => $version
-
You MUST specify the version of the server you expect to answer your queries. Couch::DB tries to hide differences between your expectations and the reality of the server version.
The $version can be a string or a version object (see "man version").
- auth => 'BASIC'|'COOKIE'
-
Authentication method to be used by default for each client.
- password => STRING
- server => URL
-
The default server to connect to, by URL. See
etc/local.ini[chttpd]
This server will be named_local
.You can add more servers using addClient(). In such case, you probably do not want this default client to be created as well. To achieve this, explicitly set
server => undef
. - to_json => HASH
-
A table mapping converter name to CODE, to override/add the default PERL to JSON object conversions for sending structures. See toJSON().
- to_perl => HASH
-
A table mapping converter name to CODE, to override/add the default JSON to PERL object conversions for Couch::DB::Result::values(). See toPerl() and listToPerl().
- to_query => HASH
-
A table mapping converter name to CODE, to override/add the default PERL to URL QUERY conversions. Defaults to the json converters. See toQuery().
- username => STRING
-
When a
username
is given, it will be used together withauth
andpassword
to login to any created client.
Accessors
- $obj->api()
-
Returns the interface version you expect the server runs, as a version object. Differences between reality and expectations are mostly automatically resolved.
Interface starting points
- $obj->cluster()
-
Returns a Couch::DB::Cluster-object, which organizes calls to manipulate replication, sharding, and related jobs. This will always return the same object.
- $obj->createClient(%options)
-
Create a client object which handles a server. All options are passed to Couch::DB::Client. The
couch
parameter is added for you. The client will also be added via addClient(), and is then returned.It may be useful to create to clients to the same server: one with admin rights, and one without. Or clients to different nodes, to create fail-over.
- $obj->db($name, %options)
-
Declare a database. The database may not exist yet: calling this method does nothing with the CouchDB server.
my $db = $couch->db('authors'); $db->ping or $db->create(...);
- $obj->node($name)
-
Returns a Couch::DB::Node-object with the $name. If the object does not exist yet, it gets created, otherwise reused.
Ungrouped calls
- $obj->freshUUID(%options)
-
Returns one unique identifier.
- $obj->freshUUIDs($count, %options)
-
Returns a $count number of UUIDs in a LIST. This uses requestUUIDs() to get a bunch at the same time, for efficiency. You may get fewer than you want, but only when the server is not sending them.
-Option--Default bulk 50
- $obj->requestUUIDs($count, %options)
-
[CouchDB API "GET /_uuids", since 2.0]
Returns UUIDs (Universally unique identifiers), when the call was successful. Better use freshUUIDs(). It is faster to use Perl modules to generate UUIDs.
- $obj->searchAnalyze(%options)
-
[CouchDB API "POST /_search_analyze", since 3.0, UNTESTED]
Check what the build-in Lucene tokenizer(s) will do with your text.
-Option --Default analyzer <required> text <required>
Processing
The methods in this section implement the CouchDB API. You should usually not need to use these yourself, as this libary abstracts them.
- $obj->addClient($client)
-
Add a Couch::DB::Client-object to be used to contact the CouchDB cluster. Returned is the couch object, so these calls are stackable.
- $obj->call($method, $path, %options)
-
Call some couchDB server, to get work done. This is the base for any interaction with the server.
Besides the explicitly listed parameters, this
call
method is also responsible for handling the generic parameters which influence the connection to the server (likedelay
,client
, andheaders
) and hook into events (likeon_final
andon_error
).Note: you should probably not use this method yourself: all endpoint of CouchDB are available via a nice, abstract wrapper.
-Option--Default paging {} query undef send undef
- paging => HASH
-
When the endpoint support paging, then its needed configuration data has been collected in here. This enables the use of
succeed
,page
,skip
, and friends. See examples in section "Pagination". - query => HASH
-
Query parameters for the request.
- send => HASH
-
The content to be sent with POST and PUT methods. in those cases, even when there is nothing to pass on, simply to be explicit about that.
- $obj->check($condition, $change, $version, $what)
-
If the $condition it true (usually the existence of some parameter), then check whether api limitiations apply.
Parameter $change is either
removed
,introduced
, ordeprecated
(as strings). Theversion
is taken from the CouchDB API documentation. The $what describes the element, to be used in error or warning messages. - $obj->client($name)
-
Returns the client with the specific $name (which defaults to the server's url).
- $obj->clients(%options)
-
Returns a LIST with the defined clients; Couch::DB::Client-objects.
-Option--Default role undef
- $obj->jsonText($json, %options)
-
Convert the (complex) $json structure into serialized JSON. By default, it is beautified.
-Option --Default compact false
- $obj->listToPerl($set, $type, @data|\@data)
-
Returns a LIST from all elements in the LIST @data or the ARRAY, each converted from JSON to pure Perl according to rule $type.
- $obj->toJSON(\%data, $type, @keys)
-
Convert the named fields in the %data into a JSON compatible format. Fields which do not exist are left alone.
- $obj->toPerl(\%data, $type, @keys)
-
Convert all fields with @keys in the %data HASH into object of $type. Fields which do not exist are ignored.
As default JSON to Perl translations are currently defined:
abs_uri
,epoch
,isotime
,mailtime
,version
, andnode
. - $obj->toQuery(\%data, $type, @keys)
-
Convert the named fields in the %data HASH into a Query compatible format. Fields which do not exist are left alone.
DETAILS
Early adopters
Be warned that this module is really new. The 127 different endpoints that the CouchDB 3.3.3 API defines, are grouped and combined. The result is often not tested, and certainly not combat ready. Please, report the result of calls which are currently flagged "UNTESTED".
Please help me fix issues by reporting them. Bugs will be solved within a day. Please, contribute ideas to make the use of the module lighter. Together, we can make the quality grow fast.
Integration with your framework
You need to instantiate an extensions of this class. At the moment, you can pick from:
-
Implements the client using the Mojolicious framework, using Mojo::URL, Mojo::UserAgent, Mojo::IOLoop, and many other.
Other extensions are hopefully added in the future. Preferrably as part of this release so it gets maintained together. The extensions are not too difficult to create and certainly quite small.
Where can I find what?
The CouchDB API lists all endpoints as URLs. This library, however, creates an Object Oriented interface around these calls: you do not see the internals in the resulting code. Knowing the CouchDB API, it is usually immediately clear where to find a certain end-point: /{db}
will be in Couch::DB::Database. A major exception is anything what has to do with replication and sharding: this is bundled in Couch::DB::Cluster.
Have a look at https://perl.overmeer.net/couch-db/reference.html. Keep that page open in your browser while developing.
Thick interface
The CouchDB client interface is based on HTTP. It is really easy to construct a JSON, and then use a UserAgent to send it to the CouchDB server. All other CPAN modules which support CouchDB stick on this level of support; except Couch::DB
.
When your library is very low-level, your program needs to put effort to create an abstraction around the interface it itself. When the library offers that abstraction already, you need to write much less code!
The Perl programming language works with functions, methods, and objects, so why would your libary require you to play with URLs? So, Couch::DB
has the following extra features:
Calls have a functional name, and are grouped into classes: the endpoint URL processing is totally abstracted away;
Define multiple clients at the same time, for automatic fail-over, read, write, and permission separation, or parallellism;
Resolving differences between CouchDB-server versions. You may even run different CouchDB versions on your nodes;
JSON-types do not match Perl's type concept: this module will convert boolean and integer parameters (and more) from Perl to JSON and back transparently;
Offer error handling and event processing on each call;
Event framework independent (currently only a Mojolicious connector).
Using the CouchDB API
All methods which are marked with [CouchDB API]
are, as the name says: client calls to some CouchDB server. Often, this connects to a node on your local server, but you can also connect to other servers and even multiple servers.
All these API methods return a Couch::DB::Result object, which can tell you how the call worked, and the results. The resulting object is overloaded boolean to produce false
in case of an error. So typically:
my $couch = Couch::DB::Mojolicious->new(version => '3.3.3');
my $result = $couch->requestUUIDs(100);
$result or die;
my $uuids = $result->values->{uuids};
This CouchDB library hides the fact that endpoint /_uuids
has been called. It also hides the client (UserAgent) which was used to collect the data.
You could also write
my $uuids = $couch->requestUUIDs(100)->values->{uuids};
because "values()" will terminate when the database call did not result in a successful answer. Last alternative:
my @uuids = $couch->freshUUIDs(100);
Besides calls, there are all kinds of facility methods, which add further abstraction from the server connection.
Type conversions
With the Couch::DB::Result::values() method, conversions between JSON syntax and pure Perl are done. This also hides database interface changes for you, based on your new(api) setting. Avoid Couch::DB::Result::answer(), which gives the uninterpreted, unabstracted results.
This library also converts parameters from Perl space into JSON space. POST and PUT parameters travel in JSON documents. In JSON, a boolean is true
and false
(without quotes). In Perl, these are undef
and 1
(and many alternatives). For anything besides your own documents, Couch::DB
will totally hide these differences for you!
Generic parameters
Each method which is labeled [CouchDB API]
also accepts a few options which are controlling the calling progress. They are handled by the call() method which implements the API calls.
These parameters are available for every API call, hence no-where documented explicitly. These options are either about the connection between client and server, for result paging, or processing event hooks.
Connection parameters
At the moment, the following generic %options
are supported everywhere:
delay
=> BOOLEAN, defaultfalse
Do not perform and wait for the actual call, but prepare it to be used in parallel querying. TO BE IMPLEMENTED/DOCUMENTED.
client
=> $client-object or -nameUse only the specified client (=server) to perform the call.
clients
=> ARRAY-of-clients or a roleUse any of the specified clients to perform the call. When not an ARRAY, the parameter is a
role
: select all clients which can perform that role (the logged-in user of that client is allowed to perform that task).headers
=> HASHAdd headers to the request. When applicable (for instance, the
Accept
-header) this will overrule the internally calculated defaults.
Processing events
Besides, at the moment we support the following events:
on_error
=> CODE or ARRAY-of-CODEA CODE (sub) which is called when the interaction with the server has been completed without success. The CODE gets the result object as only parameter.
on_final
=> CODE or ARRAY-of-CODEA CODE (sub) which is called when the interaction with the server has been completed. This may happen much later, when combined with
delay
. The CODE gets the result object as only parameter, and returns a result object which might be different... as calls can be chained.on_chain
=> CODERun the CODE after the call has been processed. It works as if the changed logic is run after the call, with the difference is that this next step is defined before the call has been made. This sometimes produces a nicer interface (like paging).
on_values
=> CODERun the CODE on the result on the returned JSON data, to translate the raw
answer()
intovalues()
. Wherever seemed useful, this is already hidden for you. However: there may be cases where you want to add changes.on_row
=> CODEUsed to produce rows where the answer of a call produces a list of answers.
Searching with CouchDB
CouchDB supports various search mechanisms, which are confusingly named. Their configuration is stored in design documents (see Couch::DB::Design). The mechanisms can often be combined.
Views
Views are docid-key-value tables which are following the changes made in a database. The value may be complex, and may contain extracts and computed elements based on each document. See https://docs.couchdb.org/en/stable/ddocs/views/
Search
Use Lucene (mainly full text search) indexes on a database. The interface uses Clouseau wrapper libraries around the Lucene core. See https://docs.couchdb.org/en/stable/ddocs/search.html
Clouseau was introduced in CouchDB 3.0.0, and now being phased out because it got stuck on Java 8. It has index type
text
.Search
Uses Lucene indexes via the (new) Nouveau wrapper. Nouveau was introduced as beta in 3.4.1 as alternative, with index type
nouveau
.Mango
The
find()
method uses MongoDB compatible search statements, which can be combined with index and view restrictions. These indexes are of the (ill-named) typejson
. See https://docs.couchdb.org/en/stable/ddocs/mango.html
Confusing? Yes it is. There are often multiple solutions for the same problem.
Paging
Searches tend to give a large number of results. The CouchDB server will refuse to return too many answers at a time (typically 25). When you need more results, you will need to do more calls.
We distinguish three cases:
- 1. no paging possible
-
Most commands will give you the full requested knowledge in one go. In some cases, the result may still present parts of the reply in convenient rows.
- 2. paging possible, but not used
-
Command may support
skip
,limit
, and/orbookmark
. You decide you need them. You can even usesucceed
(see below). However, the number of rows you get in the result can differ from your expectations. You may expect to get 50 elements, but sometimes get 10. Use the "row"- and "doc" methods on the result object. - 3. paging used
-
When you provide options
all
orpage_size
, then your call gets into paging mode: the server will be polled until the requested number of results has been received.You cannot use the row commands on the result, because it will only return the rows of the last call which was made to fulfil your request. Now, you need to use the page commands of the result object.
paging possible
To get more answers, CouchDB implements two mechanisms: some calls provide a skip
and limit
only. Other calls implement the more sophisticated bookmark mechanism. Both mechanisms are abstracted away in this library by the succeed
mechanism.
Be aware that you shall provide the same query parameters to each call of the search method. Succession may be broken when you change some parameters: it is not fully documented which ones are needed to continue, so simply pass all again. Probably, it is save to change the limit
between pages.
The following parameters can be used then the CouchDB call supports paging (this will be documented explicitly at these methods)
skip
=> INTEGERDo not return this amount of first following elements. Be warned: use as
%option
, not as search parameter.limit
=> INTEGERDo not request more than
limit
number of results per request. May be less thanpage_size
. Be warned: use as%option
, not as search parameter.bookmark
=> STRINGIf you accidentally know the bookmark for the search. Usually, this is automatically picked-up via
succeed
.stop
=> CODE|'EMPTY'|'SMALLER'|'UPTO($nr)'When do we stop asking the server for more pages? When the call returned no rows, then we always stop.
EMPTY
reflects the same.With
SMALLER
, you will also stop when fewer rows were returned than in the first call. It's not sure what the normal number of rows is what the server returns, but probably always the same until the source runs out.When
UPTO
is used with some value (for instanceUPTO(5)
) then no new call is made when less or equal to that number of rows is returned.succeed
=> $result or $result->pagingMake this query as successor of a previous query. Some requests support paging (via bookmarks). See examples in a section below.
harvester
=> CODEHow or what to extract per request. You may add other information, like collecting response objects. The CODE returns the extract LIST of objects/elements.
map
=> CODECall the CODE on each of the (defined) harvested page elements. The CODE is called with the result object, and one of the harvested elements. When a single page requires multiple requests to the CouchDB server, this map will happen on the moment each response has been received, which may help to create a better interactive experience.
Your CODE may return the harvested object, but also something small (even undef) which will free-up the memory use of the object immediately. However: at least return a single scalar (it will be returned in the "page"), because an empty list signals "end of results".
paging used
To manage paged results, selected calls support the following options:
all
=> BOOLEAN (default false)Return all results at once. This may involve multiple calls, like when the number of results is larger than what the server wants to produce in one go.
Avoid to use this when you expect many or large results. Although, in such case
map
may help you a lot.page_size
=> INTEGER (default 25)The CouchDB server will often not give you more than 25 or 50 answers at a time, but you do not want to know.
page
=> INTEGER (default 1)Start-point of returned results, for calls which support paging. Pages are numbered starting from 1. When available, bookmarks will be used for next pages. Succeeding searches will automatically move through pages (see examples)
pagenr
=> INTEGER (defaultpage
)The start of the page counter, for display purposes.
. Example: paging through result
Get page by page, where you may use the limit
parameter to request for a number of elements. Do not use skip
, except in the first call. The succeed
handling will play tricks with page
, harvester
, and client
, which you do not wish to know.
my $page1 = $couch->find(\%search, limit => 12, skip => 300, page_size => 10);
my $rows1 = $page1->page;
my @rows1 = $page1->pageRows;
my @docs1 = map $_->doc, @rows1;
my $page2 = $couch->find(\%search, succeed => $page1);
my $rows2 = $page2->page;
my @docs2 = map $_->doc, $page2->page;
my @docs2 = $page2->pageDocs;
sub h { my ($result) = @_; $result->docs }
my $page3 = $couch->find(\%search, succeed => $page2, harvester => \&h);
my $docs3 = $page3->page; # now docs!
my @docs3 = $page3->pageRows;
. Example: paging with a website user session
When you cannot ask for pages within a single continuous process, because the page is shown to a user who has to take action to see an other page, then save the pagingState.
The state cannot contain code references, so when you have a specific harvester or map, then you need to resupply those.
my $page1 = $couch->find(\%search, page_size => 50);
my $rows1 = $page1->page;
$session->save(current => serialized $page1->pagingState);
...
my $prev = deserialize $session->load('current');
my $page2 = $couch->find(\%search, succeed => $prev);
my $rows2 = $page2->page;
. Example: get all results in a loop
Handle the responses which are coming in one by one. This is useful when the documents (with attachements?) are large. Each $list
is a new result object.
my $list;
while($list = $couch->find(\%search, succeed => $list))
{ my @rows = $list->rows;
@rows or last; # nothing left
...; # use the rows
}
# Checks the success of $list, not the number of rows
$list or die "Stopped somewhere with ". $list->message;
This call does not use page_size
or all
, so the number of rows received per loop may differ by decission of the server. You cannot use the page*
set of methods, only the row*
and doc
methods.
. Example: get one page of results
You can jump back and forward in the pages: bookmarks will remember the pages already seen.
my $page4 = $couch->find(\%search,
limit => 10, # results per server request
page_size => 50, # results until complete
page => 4, # start point, may use bookmark
harvester => sub { $_[0]->rows }, # default
);
my $rows4 = $page4->page;
my $page5 = $couch->find(\%search, succeed => $page4);
my $rows5 = $page5->page;
When paging (here selected via page_size
, you cannot use the the row*
and doc
methods on the result object: you need to use the page*
versions.
. Example: get all results in one call
When you ask for all answers at once, you must be aware this can consume a lot of time and memory. Your clients will keep on requesting data from the server until all rows have been collected.
my $all = $couch->find(\%search, all => 1) or die;
my @rows6 = $all->pageRows;
You may consider to use map
here: to extract the data immediately when it comes in. In this case, you can reduce the amount of information which has to be kept in memory immediately on arrival.
. Example: processing results when they arrive
When a page (may) require multiple calls to the server, this may enhance the user experience.
sub do_something($$) { my ($result, $doc) = @_; ...; 42 }
my $all = $couch->find(\%search, all => 1, map => \&do_something);
# $all->page will now show elements containing '42'.
SEE ALSO
This module is part of Couch-DB distribution version 0.200, built on June 18, 2025. Website: http://perl.overmeer.net/CPAN/
LICENSE
Copyrights 2024-2025 by [Mark Overmeer]. For other contributors see ChangeLog.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See http://dev.perl.org/licenses/