Here we will document the most basic API functions implemented in Emilda for You to use and modify. For all functions, You should best consult the source of Emilda.
The purpose of this book is to give insight into how the search API of Emilda works, and how it can help You design custom pages or help us make Emilda even better.
require_once "search.inc";
require_once "mgmnt.inc";
$search = new Search;
$search->init("localhost:9999/Default", "emilda", "abc123");
$ccl = $search->generate_ccl(array("100" => "Jansson",
"245" => "Mumin"));
$rpn = $search->ccl2rpn($rpn, generate_ccl_conf("localhost:9999/Default"));
$search->perform($rpn, "1=1004 ai");
if($this->hits()) {
while($record = $search->next(10)) {
... record manipulation ...
}
} else {
die "No hits";
}
The search class is the core of the search and retrieval process of records (thus data) in Emilda. The reason for making it in object form, is the ease of storing information related to the search or retrieval process at stake.
The class iself contains all vital information allowing the user to only use the object itself when performing the search without having to remember to pass connection id:s or similar.
This new class system also contains the call to the record class, so that the user using the search class does not have to instantiate the record object to be able to access the data; this is done by the search class and is such one step less to remember for the user before the data is available.
In contrast to the engine used in Emilda 1.1 and earlier, this new search class system supports simultaneous search statements within one connection; what this in practice implies is that the connection is instatiated at script start, and terminated at script end and unlimited search statements can be performed inbetween.
$search = new Search; $search->init() ... preform searches ... $search->terminate();
In addition, the record retrieval function itself verifies that the number of records desired is available, so that the user requesting the records does not have to separately verify the availability of records. This is one step colser to making the process of search and retrieval in Emilda both less error prone and optimized.
Below is an example of how an user can request records from the search class. In this case, the user wants 10 records, and will retrieve 10 if there are that many, otherwise the number of available records.
... instantiate and search ...
while($record = $search->next(10)) {
... use or manipulate record ...
}
... clean up ...
As we see, there is no real need to verify that there are any hits. This can however be useful when wanting to trap custom errors.
For more detailed description on the different methods, please see the Methods section
The logics itself has undergone an conciderable amount of testing and rethinking before reaching its current state. We think that this class system is a working entirety which is mature for real life implementation but at the same time open for suggestions and improvements.
The component that needs most attention in upcoming releases is the error handling. Currently the class contains an intrnal function called __raise_error() which is called every time there is a possibility of an error. This function will, at least by default not halt the search process, only inform the user that an error has occurred. This can in some cases result in the code to break, as the code might be designed so that it assumes that records are retrieved.
Below is an example how one can trap an error using the search class.
... instantiate ...
$search->perform($rpn);
if($search->errno()) {
die writeLog("Search error\n");
} else{
... process more ...
}
... terminate ...
As the above example illustrates, the __raise_error() function which is internally called within the search class informs the user of possible errors, and as such there is no need to generate custom errors, unless explicitly desired.
What needs to be done, is to make the search request "die" cleanly upon an error, so that even the calling code understands and exits with a clean error. Suggestions are more than welcome, as we have numerous alternatives of which none seems to fit our needs completely.
This section contains a more deatiled description on all the different methods contained within the search class.
mixed additional (string syntax)
Gives the user the possibility to retrieve the record in an additional format than the Emilda Record Model, e.g. Raw MARC, GRS array etc. This is particulary suitable for cases where the user wants to pass the raw MARC to some script or similar.
The argument passed is the format desired. To see which formats are present, please turn to the PHP/YAZ documentation.
Example of first extracting the Emilda Record and then the raw MARC data.
... Initialize and search ...
while($record = $search->next()) {
$raw = $search->additional("raw");
... process ...
}
string ccl2rpn (string query, array config)
This function will generate a RPN query of Your passed CCL query based on the config that You supply. The config can automatically be generated using the function generate_ccl_conf([string url]) or then alternatively be passed manually. To avoid errors and corrupt queries, we recommend using the generate_ccl_conf()-function.
The result of this function can directly be passed as the query argument to the perform() function. See examples below.
Example of generating an RPN-query of a passed CCL query.
... Initialize ...
$rpn = $search->ccl2rpn("title=mumin and author=jansson", generate_ccl_conf());
... End and clean ...
Will result in the following RPN query (example definitions, may vary from system to system).
@and @attr 5=3 @attr 1=1004 "mumin" @attr 5=3 @attr 1=4 "jansson"
Additionally and example of using the function to feed the perform() function.
... Initialize ...
$search->perform($search->ccl2rpn("title=mumin", generate_ccl_conf()), "1=4 ai")
if(!$search->errno()) {
... process ...
}
... End anc clean ...
int errno (void)
Returns the error code of the last error, and Null if no error is accessible.
Example of how to trap an error. It might be a good idea to use errno() to "see" if there is an error present.
... initialize and perform ...
if ($search->errno()) {
writeLog($search->error());
}
... terminate ...
string error (void)
Returns a formatted error string for either printing or storing to a log. This error is different depending if debugging has been turned on on the system. If debuging is on, the error is more descriptive with the exact explanation from YAZ accompanied with an error code, whereas it when debuging is turned off is only an error notice with the error code.
Example of how to trap an error
... initialize and perform ...
if ($search->errno()) {
custom_error_processor($search->error());
}
... end ...
There is no function called custom_error_processor(); it only represents some arbitrary function that can accept an error and process it meaningfully.
string generate_ccl (array array)
Generates a valid CCL query from the passed array. The idea of this function is to centralize the way Emilda processes CCL queries, so that it is easy to alter this logics.
generate_ccl() accepts an arbitrary key-value pair array, where it only processes the key-value pairs where the key is a valid MARC field. By valid we mean a field defined in the MARC-configuration section, not that it is according to some MARC standard. If there is more than one valid key-value pair, these are appended to each other with the logical "AND"-operator.
As such, if You have a form with fields which You want to search from, You only have to give $_REQUEST as the argument to generate valid and working CCL, assuming that the fields are named e.g. 020 for ISBN, or 100 for author.
Example of an array that will generate the example query.
Array (
"100" => "Jansson",
"245" => "Mumin",
)
Will generate the following query (with the example CCL-names from emilda)
author=Jansson and title=Mumin
int hits (void)
Returns the number of hits that the last search query returned, 0 if no search has been performed or there were no hits.
Example of checking using hits() before proceeding with processing.
... Initialize and perform ...
if($search->hits()) {
... process ...
}
... End ...
int id init (string host [, string user, string password])
Initialize the connection to the given host with the given parameters.
This method has to be called prior to any other method as it populates the object with the required information, and at the same time prepares PHP/YAZ for the upcoming search statements.
Simple example of initializing a connection to the local database without user information. As it can be seen, the default host is the local zebra server, so no server needs to be specified.
$search = new Search; $search->init(); ... perform searches ....
Example of initializing a connection to the Library of Congress, which needs no login information.
$search = new Search;
$search->init("z3950.loc.gov:7090/voyager");
... perform searches ...
Last an example of a connection to an arbitrary server with login information supplied.
$search = new Search;
$search->init("some.host.com", "username", "abc123");
... perform searches ...
Record next ([int count])
Wrapper method for retrieving the Record objects containing the data. The advantage of this function is that the user does not need to keep track of the records; next() will return records as long as there are records or alternatively until count records have been retrieved. For security issues, a hard-coded value of 10000 will be assigned to count unless a value is given.
Example of retrieving the first 10 records.
... initialize and search ...
while ($record = $search->next(10)) {
... process records ...
}
... clean up and end ...
Omitting the argument passed to next() will only return the first 10000 records, so if You suspect that there might be more hits (this is probably not very realistic) You have to pass the number of hits as an argument. The below example illustrates this.
... intilialize and search ...
while ($record = $search->next($search->hits())) {
... process records ...
}
... clean up and exit ...
int perform (string rpn [, string sort, int from, int how_many])
A function that will do all the numerous PHP/YAZ calls so that the next thing to do is only to retrieve the records.
Of the arguments only the rpn query is required, the others have predefined defaults. The rpn query can either be user specified or alternatively generated (e.g. using the ccl2rpn() method) but there is no check that the rpn query is valid, so an invalid query will result in failure.
The sort criteria can be used where desired, and if not specified, the result set will be sorted according to the system default, which varies. For more information on the syntax of the sort query, please consult more appropriate documentation sources, or the examples below.
Of the from and how_many arguments only from is the interesting one, as how_many can be overrid in the next() method. The from method is mainly interesting when wanting to start the retrieval from a certain position, e.g. when using a page index where only 10 hits are visible per page.
Example of an rpn-query; this query selects all items which have an author that contains "jansson"
@attr 1=1004 'jansson'
The first example is an sorting query that will sort according to title, ascending and case insensitive, and the second will first sort according to author then title, both ascending and case insensitive.
1=4 ai
1=1004 ai 1=4 ai
Example of a search request when we want to search for authors containing "jansson", sort the results according to author and title, and start from the 30th record.
... initialize ...
$search->perform("@attr 1=1004 jansson", "1=1004 ai 1=4 ai", 30);
while ($record = $search->next(10)) {
... manipulate ...
}
... end ...
void restart (void)
Restarts the record retrieval process. After calling restart() the next() functio will operate as were the records never retrieved.
An example of retrieving the first 10 records twice.
... initialize and search ...
while($record = $search->next(10)) {
... process ...
}
$search->restart();
while($record = $search->next(10)) {
... process more ...
}
... end and clean ...
int search_by_cn (string cn)
This is a wrapper function for the above functions enabling the searching for a control number using just one function. This function performs all tasks so that nothing else than the next() need to be called to be able to start the retreval of records. See examples below.
It is possible that similar wrapper functions will be written, but as control number is the most common search term (70% of cases excluding user instantiated searches) this was the most logical to implement.
Example of using the function
... Initialize ...
$search->search_by_cn("1234");
while($record = $search->next()) {
... process ...
}
... End ...
bool terminate (void)
Terminates the class and the connection before script end. If this is forgotten, no harm is done as PHP will clean up after script completion. If You however want to destroy the class, this is the method to use.
Example of how to destroy the class.
... instantiate and use class ... $search->terminate();
void __raise_error (void)
Internal function within the search class which handles all errors in the class.
If You want to change the way the search errors appear to the user, or want to customize these, this is the function to change, not error() or errno()
This method is not intended to be used externally from the calling code. For this purpose, see the methods error() and errno()
N/A
Example of an search using the control number as criteria. This is possibly the most useful example as searching for by the control number of an item is one of the most common searches in the Emilda code.
require_once "mgmnt.inc";
require_once "search.inc";
$search = new Search;
$search->search_by_cn("1234");
if (!$search->errno()) {
while($record = $search->next(10)) {
print $record->ffield("020", "This book has the ISBN number '%a'\n");
}
} else {
die "Failed!";
}
$search->terminate();
More complicated search using more details in the API
require_once "mgmnt.inc";
require_once "search.inc";
$search = new Search;
$ccl = $search->generate_ccl(array("100" => "Jansson"));
$rpn = $search->ccl2rpn($ccl, generate_ccl_conf());
$search->perform($rpn, "1=4 ai");
if (!$search->errno()) {
while($record = $search->next(10)) {
print $record->ffield("020", "This book has the ISBN number '%a'\n");
printf("Raw MARC: %s\n", $search->additional("raw"));
}
} else {
die "Failed!";
}
$search->terminate();
The Emilda Search API was first introduced in Emilda 1.2-alpha and is thus rather new and fresh. What this implies is that cosmetic changes and additions to the functionality is are very likely, whereas the main functionality and logics prbably will remain unchanged.
Due to the fact that the API has not been largely tested, it would be great if some of testers interested in customizing and maybe coding would take a standpoint in how well the API works and if something need to be changed, then what.
Christoffer Landtman ( emilda.org account )
Copyright © 2003-2004 Oy Realnode Ab
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.