A Simple Example
Here is a listing of source file lgrep.c, a simple, fully functional program that loads a text file of one-field records into the DevKit, then looks up records for a query. If you are familiar with Unix systems, consider it as an TIBCO Patterns analogue of the grep utility. While simple, this program demonstrates most of the DevKit's essential functions.
DevKit type names and function names appear in boldface. DevKit functions fall into three categories:
|
•
|
Command functions perform administrative tasks, as well as matching and machine learning scoring operations. These are the functions that do real work. lgrep.c invokes two command functions: lkt_dbload to load data, and lkt_dbsearch to search the data. |
|
•
|
Parameter functions create, manipulate, and destroy the input and output parameters that are passed to and from command functions. Most of the functions shown in boldface are parameter functions (for example, lpar_create_lst, lpar_create_record, lpar_create_int, lpar_destroy). |
|
•
|
System functions perform DevKit system tasks. The most important are for startup (lkt_devkit_init) and shutdown (lkt_devkit_shutdown). |
In outline, lgrep.c performs the following steps:
|
1.
|
DevKit system initialization. |
Packaging each line as a DevKit record object and
Appending it to a DevKit list object.
|
3.
|
Define the field structure for the table. |
|
4.
|
Call the lkt_dbload command function with the list of records and a string that names the new database. The lkt_dbload command creates and populates the database. |
|
5.
|
Build a list of search parameters (srchpars). |
|
6.
|
Call the function lkt_dbsearch with the query, the database name, and the list of search parameters. lkt_dbsearch returns a ranked list of record objects (matches). |
|
7.
|
Print some statistics, including the search time, followed by the text of each record in the ranked list. |
|
8.
|
Call the system shutdown function lkt_devkit_shutdown. |
lgrep.c
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "netrics/devkit.h"
#define TRUE 1
#define FALSE 0
typedef unsigned char UCHAR;
#define MAX_RECORD_LEN 2048
char *ProgName;
static void checkerr( dvkerr_t err, char *function );
int main( int argc, char *argv[] )
{
int qlen, matchesret, matchnum, numrecs, reclen ;
char *query, *datafile, dataline[MAX_RECORD_LEN], reckey[10];
FILE *fp;
int fldlens[1];
/* our database will have one field named "text" */
static const UCHAR *fldnames[1] = { "text" };
const UCHAR *rectxt;
/* Simple lpars */
lpar_t dbname, qpar, lpar;
/* Record lpars */
lpar_t record;
/* List lpars */
lpar_t dbpars, reclist, names, srchpars, stats, matches, minfo;
dvkerr_t err;
ProgName = argv[0];
if (argc != 3) {
fprintf(stderr, "Usage: %s <query> <datafile>\n", ProgName);
exit(-1);
}
/* 1. INITIALIZE THE DEVKIT */
err = lkt_devkit_init();
checkerr(err, "lkt_devkit_init");
query = argv[1];
qlen = strlen(query);
datafile = argv[2];
if (!(fp = fopen(datafile, "r"))) {
fprintf(stderr, "%s: Can't open file `%s'\n", ProgName,
datafile);
exit(-1);
}
/* 2. READ THE DATA FILE */
numrecs = 0;
reclist = lpar_create_lst(LPAR_LST_GENERIC);
while (fgets(dataline, MAX_RECORD_LEN, fp)) {
/* 2A: CREATE A RECORD WITH `dataline' AS SEARCHABLE TEXT */
record = lpar_create_record();
sprintf(reckey, "%06d", ++numrecs);
lpar_set_record_key(record, (UCHAR *)reckey);
fldlens[0] = strlen(dataline);
if (dataline[fldlens[0]-1] == '\n') { /* Chop newline */
fldlens[0]--;
}
rectxt = (UCHAR *)dataline;
lpar_set_record_srchtxt(record, rectxt, fldlens[0]);
/* Record field structure must match database field structure */
lpar_set_record_srchflds(record, fldlens, 1);
/* 2B: ADD RECORD TO THE RECORD LIST */
lpar_append_lst(reclist, record);
}
fclose(fp);
/* Package file name as the name for the database */
dbname = lpar_create_str(LPAR_STR_DBDESCRIPTOR, (UCHAR *)datafile);
/* 3. DEFINE THE FIELDS */
dbpars = lpar_create_lst(LPAR_LST_GENERIC);
lpar = lpar_create_strarr(LPAR_STRARR_FIELDNAMES, fldnames, 1);
lpar_append_lst(dbpars, lpar);
/* 4. LOAD THE DATABASE */
err = lkt_dbload(LPAR_NULL, dbname, dbpars, reclist, &stats);
checkerr(err, "lkt_dbload");
/* Clean up */
lpar_destroy(reclist);
lpar_destroy(stats);
lpar_destroy(dbpars);
/* 5. SET UP INPUT PARAMETERS FOR LOOKUP */
srchpars = lpar_create_lst(LPAR_LST_GENERIC);
lpar = lpar_create_int(LPAR_INT_MATCHESREQ, 10);
lpar_append_lst(srchpars, lpar);
/* Add name to a list of names, and package query */
names = lpar_create_lst(LPAR_LST_GENERIC);
lpar_append_lst(names, dbname);
qpar = lpar_create_blk(LPAR_BLK_SEARCHQUERY, (UCHAR *)query, qlen);
/* 6. DO THE LOOKUP */
err = lkt_dbsearch(LPAR_NULL, names, LPAR_NULL, qpar, srchpars, &stats,
&matches, &minfo);
checkerr(err, "lkt_dbsearch");
/* 7. PRINT THE RESULTS */
printf("Query: `%s'\n", query);
matchesret = lpar_get_lst_num_items(matches);
printf("Matches returned: %d\n", matchesret);
lpar = lpar_find_lst_lpar(stats, LPAR_DBL_SEARCHTIME);
printf("Lookup time: %.2f seconds\n", lpar_get_dbl(lpar));
printf("\n");
/* Print the ranked list of records */
for (matchnum=0 ; matchnum < matchesret ; matchnum++) {
record = lpar_get_lst_item(matches, matchnum);
rectxt = lpar_get_record_srchtxt(record, &reclen);
printf("[%d] `%.*s'\n", matchnum+1, reclen, rectxt);
}
/* Clean up */
lpar_destroy(names);
lpar_destroy(qpar);
lpar_destroy(srchpars);
lpar_destroy(stats);
lpar_destroy(matches);
lpar_destroy(minfo);
/* 8. SYSTEM TERMINATION */
err = lkt_devkit_shutdown();
checkerr(err, "lkt_devkit_shutdown");
exit(0);
}
static void
checkerr( dvkerr_t err, char *function )
{
lpar_t erritem;
if (DVKERR(err)) {
fprintf(stderr, "%s() returned %d (%s)\n", function,
DVKERR(err), dvkerr_get_string(err));
if ((erritem = dvkerr_get_item(err))) {
lpar_fprintf(stderr, erritem, -1);
}
dvkerr_clear(err);
exit(-1);
}
}
On a Unix system, after compiling lgrep.c and linking it with the DevKit, run the program as follows:
lgrep dictioneryprblembounds cspapers.lst
where dictioneryprblembounds is an inexact search query, and cspapers.lst is a text file of single-line records. The output looks like this, where some long lines are abbreviated:
Query: 'dictioneryprblembounds'
Matches returned: 10
Lookup time: 0.01 seconds
[1] `Andersson, Optimal Bounds on the Dictionary Problem|LNCS|401|1989'
[2] `..., Upper and Lower Bounds for the Dictionary Problem|LNCS|318|1988'
[3] `Sundar, A Lower Bound for the Dictionary Problem ...|FOCS|32|1991'
[4] `... Concurrency Methods for the Dictionary Problem: A Survey|...'
[5] `Fich, Lower Bounds for the Cycle Detection Problem|STOC||1981'
[6] `Fich, Lower Bounds for the Cycle Detection Problem|JCSS|26|1983'
[7] `... Bounds for Communication Complexity Problems|ACTAINF|28|1991'
[8] `... Dictionary-Matching on Unbounded Alphabets ...|LIBTR||1993'
[9] `... Dictionary-Matching on Unbounded Alphabets...|ALGORITHMS|18|1995'
[10] `Willard, Lower Bounds for Dynamic Range Query Problems ...|ICALP||1986'
The lgrep program loads data from a file to service a single query. Most DevKit applications run as services that maintain searchable data in memory, concurrently processing matching or machine learning scoring requests and database administration requests. In either case, the steps in assembling and passing parameters for the lkt_dbload and lkt_dbsearch functions are the same.
That is how simple it is to use DevKit.