今日から始めるSenna QL API

Senna QL API(Senna Query Language API)を使ったプログラムを書くための下調べとして、簡単なサンプルプログラムを書いてみました。以下、ソースとコメントをあわせてご覧下さい。SennaQLを文字列そのまま投げる感じが伺えると思います。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <senna/senna.h>

const char *dbpath = "hoge.SEN";
int dbcreate_flag = 0;
sen_encoding db_encoding = sen_enc_utf8;
int ctx_flag = SEN_CTX_USEQL;  /* 必須 */
sen_db *db;
sen_ctx *ctx;

/* SennaQL実行用の関数 */
void hoge(const char *str)
{
  char *ql;
  char *res;
  unsigned int res_len;
  int res_flags;
  char *buf;
  unsigned int ql_len = strlen(str);
  ql = (char*) malloc(ql_len);        /* auto変数の文字列リテラルをそのまま渡すと問題が起きるので */
  strncpy(ql,str,ql_len);             /* heap上にコピーしてます。普通は入力データはheap上にあると思われ。*/
  sen_ctx_send(ctx,ql,ql_len,0);      /* SennaQLの実行 */
  do {
    sen_ctx_recv(ctx,&res,&res_len,&res_flags);
    buf = (char*) calloc(1,1024);
    printf("%s => %s\n",str,strncat(buf,res,res_len));
    free(buf);
  } while (res_flags & SEN_CTX_MORE); /* 結果が複数レコードある場合にはSEN_CTX_MOREフラグが立っています */
  free(ql);
}

int main()
{
  sen_init();                  /* Senna APIを使う時には必ず最初に呼ぶ */

  db = sen_db_open(dbpath);    /* サンプルなのでファイルがなかったら作る */
  if (!db) {
    db = sen_db_create(dbpath,dbcreate_flag,db_encoding);
  }

  ctx = sen_ctx_open(db,ctx_flag);

   /* テーブル定義 */
  hoge("(ptable '<items>)");              
  hoge("(<items> ::def :title <text>)");  
  hoge("(<items> ::def :body <text>)");   

  /* レコードを追加 */
  hoge("(<items> ::new \"key1\")");                    
  hoge("((<items> : \"key1\") :title \"hoge fuga\")");       
  hoge("((<items> : \"key1\") :body \"fuga uho ii fuga\")");

  /* レコードを追加 */
  hoge("(<items> ::new \"key2\")");
  hoge("((<items> : \"key2\") :title \"aaa\")");
  hoge("((<items> : \"key2\") :body \"bbb uho uho bbb\")");

  /* テーブル内のレコード件数を取得 */
  hoge("(<items> ::nrecords)");

  /* N-Gramインデックスを追加 */
  hoge("(ptable '<terms> :ngram)");
  hoge("(<terms> ::def :item_body :as '(<items>.body ::match ()))");

  /* 検索実行(結果をJSON形式で表示)(主キーのみ)*/
  hoge("(disp (<terms>.item_body : \"uho\") :json)");

  /* 検索実行(結果をtab区切り形式で表示)(主キーのみ)*/
  hoge("(disp (<terms>.item_body : \"uho\") :tsv)");

  /* レコードの追加を1行でやるべく関数を定義 */
  hoge("(define (add_items items_key items_title items_body)(<items> ::new items_key :title items_title :body items_body))");
  /* 関数を利用したレコードの追加 */
  hoge("(add_items \"key3\" \"dondon\" \"ai ue o uho eee\")");

  /* 検索時に主キー以外のカラムも同時に取得するための関数を定義 */
  hoge("(define (sen-output . x)(disp (car x).:nr :tsv)(disp (cons : x) :tsv))");
  /* 検索実行 */
  hoge("(sen-output (<terms>.item_body : \"uho\") '(.:key .title .body))");

  sen_ctx_close(ctx);
  sen_db_close(db);
  sen_fin();
  return 0;
}

実行結果

(ptable '<items>) => #t
(<items> ::def :title <text>) => #t
(<items> ::def :body <text>) => #t
(<items> ::new "key1") => #p<0000900001>
((<items> : "key1") :title "hoge fuga") => "hoge fuga"
((<items> : "key1") :body "fuga uho ii fuga") => "fuga uho ii fuga"
(<items> ::new "key2") => #p<0000900002>
((<items> : "key2") :title "aaa") => "aaa"
((<items> : "key2") :body "bbb uho uho bbb") => "bbb uho uho bbb"
(<items> ::nrecords) => 2
(ptable '<terms> :ngram) => #t
(<terms> ::def :item_body :as '(<items>.body ::match ())) => #t
(disp (<terms>.item_body : "uho") :json) => ["key1", "key2"]
(disp (<terms>.item_body : "uho") :json) => #t
(disp (<terms>.item_body : "uho") :tsv) => key1
(disp (<terms>.item_body : "uho") :tsv) => key2
(disp (<terms>.item_body : "uho") :tsv) => #t
(define (add_items items_key items_title items_body)(<items> ::new items_key :title items_title :body items_body)) => add_items
(add_items "key3" "dondon" "ai ue o uho eee") => #p<0000900003>
(define (sen-output . x)(disp (car x).:nr :tsv)(disp (cons : x) :tsv)) => sen-output
(sen-output (<terms>.item_body : "uho") '(.:key .title .body)) => 3
(sen-output (<terms>.item_body : "uho") '(.:key .title .body)) => key1  hoge fuga       fuga uho ii fuga
(sen-output (<terms>.item_body : "uho") '(.:key .title .body)) => key2  aaa     bbb uho uho bbb
(sen-output (<terms>.item_body : "uho") '(.:key .title .body)) => key3  dondon  ai ue o uho eee
(sen-output (<terms>.item_body : "uho") '(.:key .title .body)) => #t