[Hyper Estraier] _estraiernative.Database.get_docの第2引数には、オプションを指定する。

環境

この記事の内容は、Ubuntu Linux 6.10, Python 2.4.4, Hyper Estraier 1.4.10, qdbm 1.8.75, libiconv 1.11, estraiernative 0.2で確認しました(Hyper Estraierとqdbm, libiconv, estraiernativeは、パッケージ管理システムからではなく、ソースコードからmake installしました)。

estraiernativeにおける検索方法

estraiernativeは、Hyper EstraierPythonバインディングです。現時点では開発中でドキュメントもありませんが、検索の仕方は、examples/search.pyのソースコード(以下)を読めば、おおよそ理解できます。

# -*- coding: utf-8 -*-
## $Id: search.py,v 1.1 2007/04/15 05:52:47 yosida Exp $
#
try :
    from _estraiernative import *
except ImportError :
    import sys; sys.path.insert(0, '../')
    from _estraiernative import *

# create the database object
db = Database()

# open the database
if not db.open("casket", Database.DBREADER) :
    print "error: %s\n", db.err_msg(db.error)
    raise SystemExit(1)

# create a search condition object
cond = Condition()

# set the search phrase to the search condition object
cond.set_phrase("rainbow AND lullaby")

# get the result of search
result = db.search(cond)

# for each document in the result
dnum = result.doc_num()
for i in range(0, dnum) :
    # retrieve the document object
    doc = db.get_doc(result.get_doc_id(i), 0)
    if doc is None :
        continue

    # display attributes
    uri = doc.attr("@uri")
    if uri :
        print "URI: %s", uri
    title = doc.attr("@title")
    if title :
        print "Title: %s", title

    # display the body text
    for text in doc.texts() :
        print "%s", text

# close the database
if not db.close() :
    print "error: %s", db.err_msg(db.error)

この中で、おそらく問題となるのは、次の行です。

    doc = db.get_doc(result.get_doc_id(i), 0)

ここでは、_estraiernative.Database.get_docメソッドの第2引数に0を指定していますが、これはなんなのでしょうか? これを調べるため、estraiernative.cを読んでみます。get_docメソッドは、以下のようにして設定されていました。

    {"get_doc", _est_db_get_doc, METH_VARARGS, NULL},

どうやら、get_docメソッドを呼び出すと、_est_db_get_doc関数を呼び出すようです。では、_est_db_get_doc関数を除いてみます(以下)。

static PyObject*
_est_db_get_doc(PyObject *self, PyObject *args)
{
    PyESTDB *pdb = (PyESTDB*)self;
    int id, opts;
    PyESTDOC *pdoc;
    ESTDOC *doc;

    if (!PyArg_ParseTuple(args, "ii", &id, &opts))
        return NULL;

    null_check(pdb->db, "db is closed");

    doc = est_mtdb_get_doc(pdb->db, id, opts);
    if (doc) {
        pdoc = PyObject_New(PyESTDOC, &PyESTDOC_Type);
        pdoc->doc = doc;
        return (PyObject*)pdoc;
    } else {
        Py_INCREF(Py_None);
        pdb->ecode = est_mtdb_error(pdb->db);
        return Py_None;
    }
}

get_docメソッドに与えられた引数は、

    if (!PyArg_ParseTuple(args, "ii", &id, &opts))

の行により、id変数とopts変数に格納されるのが分かります。では、opts変数を使っている場所を調べてみます。それは、以下の箇所です。

    doc = est_mtdb_get_doc(pdb->db, id, opts);

est_mtdb_get_doc関数は、Hyper Estraierの関数です。そこで、Hyper Estraierソースコードgrep して、est_mtdb_get_doc関数の所在を調べます。すると、est_mtdb_get_doc関数はestmtdb.cにあり、以下のようであることが分かりました。

/* Retrieve a document in a database. */
ESTDOC *est_mtdb_get_doc(ESTMTDB *db, int id, int options){
  ESTDOC *rv;
  assert(db && id > 0);
  if(!est_mtdb_lock(db)) return NULL;
  rv = est_db_get_doc(db->db, id, options);
  est_mtdb_unlock(db);
  return rv;
}

与えたoptionsは、さらにest_db_get_doc関数に渡っています。est_db_get_doc関数は、estraier.cにありました。

/* Retrieve a document in a database. */
ESTDOC *est_db_get_doc(ESTDB *db, int id, int options){
  ESTDOC *doc;
  const char *cbuf;
  char *vbuf, numbuf[ESTNUMBUFSIZ];
  int i, csiz, vsiz, num;
  assert(db && id > 0);
  if(id >= ESTPDOCIDMIN){
    if((num = id - ESTPDOCIDMIN) >= CB_LISTNUM(db->pdocs)){
      est_set_ecode(&(db->ecode), ESTENOITEM, __LINE__);
      return NULL;
    }
    if((vbuf = cbreadfile(CB_LISTVAL(db->pdocs, num), NULL)) != NULL){
      doc = est_doc_new_from_draft(vbuf);
      free(vbuf);
    } else {
      doc = est_doc_new();
    }
    doc->id = id;
    sprintf(numbuf, "%d", id);
    est_doc_add_attr(doc, ESTDATTRID, numbuf);
    if(!est_doc_attr(doc, ESTDATTRURI))
      est_doc_add_attr(doc, ESTDATTRURI, CB_LISTVAL(db->pdocs, num));
    return doc;
  }
  cbuf = NULL;
  if(options & ESTGDNOATTR){
    if(crvsiz(db->attrdb, (char *)&id, sizeof(int)) == -1){
      if(dpecode == DP_ENOITEM){
        est_set_ecode(&(db->ecode), ESTENOITEM, __LINE__);
        return NULL;
      } else {
        est_set_ecode(&(db->ecode), ESTEDB, __LINE__);
        db->fatal = TRUE;
        return NULL;
      }
    }
    vbuf = NULL;
  } else if((cbuf = cbmapget(db->attrcc, (char *)&id, sizeof(int), &csiz)) != NULL){
    cbmapmove(db->attrcc, (char *)&id, sizeof(int), FALSE);
    vbuf = NULL;
  } else if(!(vbuf = est_crget(db->attrdb, db->zmode, id, &vsiz))){
    if(dpecode == DP_ENOITEM){
      est_set_ecode(&(db->ecode), ESTENOITEM, __LINE__);
      return NULL;
    } else {
      est_set_ecode(&(db->ecode), ESTEDB, __LINE__);
      db->fatal = TRUE;
      return NULL;
    }
  }
  doc = est_doc_new();
  doc->id = id;
  if(cbuf){
    doc->attrs = cbmapload(cbuf, csiz);
  } else if(vbuf){
    doc->attrs = cbmapload(vbuf, vsiz);
    if(db->acmnum > 0) cbmapput(db->attrcc, (char *)&id, sizeof(int), vbuf, vsiz, TRUE);
    free(vbuf);
    if(cbmaprnum(db->attrcc) > db->acmnum){
      num = cbmaprnum(db->attrcc) * 0.1 + 1;
      cbmapiterinit(db->attrcc);
      for(i = 0; i < num && (cbuf = cbmapiternext(db->attrcc, NULL)) != NULL; i++){
        cbmapout(db->attrcc, cbuf, sizeof(int));
      }
    }
  } else {
    doc->attrs = NULL;
  }
  if(!(options & ESTGDNOTEXT)){
    if((cbuf = cbmapget(db->textcc, (char *)&id, sizeof(int), &csiz)) != NULL){
      cbmapmove(db->textcc, (char *)&id, sizeof(int), FALSE);
      doc->dtexts = cblistload(cbuf, csiz);
    } else {
      if(!(vbuf = est_crget(db->textdb, db->zmode, id, &vsiz))){
        est_set_ecode(&(db->ecode), ESTEDB, __LINE__);
        db->fatal = TRUE;
        est_doc_delete(doc);
        return NULL;
      }
      doc->dtexts = cblistload(vbuf, vsiz);
      if(db->tcmnum > 0) cbmapput(db->textcc, (char *)&id, sizeof(int), vbuf, vsiz, TRUE);
      free(vbuf);
      if(cbmaprnum(db->textcc) > db->tcmnum){
        num = cbmaprnum(db->textcc) * 0.1 + 1;
        cbmapiterinit(db->textcc);
        for(i = 0; i < num &&(cbuf = cbmapiternext(db->textcc, NULL)) != NULL; i++){
          cbmapout(db->textcc, cbuf, sizeof(int));
        }
      }
    }
  }
  if(!(options & ESTGDNOKWD)) doc->kwords = est_db_get_keywords(db, id);
  return doc;
}

この関数の中で、options引数は、以下の定数と組み合わせて使用されています。

  1. ESTGDNOATTR
  2. ESTGDNOTEXT
  3. ESTGDNOKWD

これは何を意味するのでしょうか? 答えは、Hyper Estraierのプログラミングガイドにありました。

ESTDOC *est_db_get_doc(ESTDB *db, int id, int options);
`db' はデータベースオブジェクトを指定します。`id' は登録文書のID番号を指定します。`options' はオプションを指定します。`ESTGDNOATTR' は属性を取得しないことを指示し、`ESTGDNOTEXT' は本文を取得しないことを指示し、`ESTGDNOKWD' はキーワードを取得しないことを指示します。オプションはビット和で同時に指定できます。戻り値は文書オブジェクトか、エラーなら `NULL' です。戻り値のオブジェクトは `est_doc_new' で生成されているので、不要になったら `est_doc_delete' で破棄してください。

これらの定数をPythonから使用できるようにするため、estraiernativeでは、以下のように定数を定義しています(どうやら、ESTGDNOKWDは定義されていないようです)。

PyMODINIT_FUNC
init_estraiernative(void)
{
:
    define_const(d, "GDNOATTR", ESTGDNOATTR);
    define_const(d, "GDNOTEXT", ESTGDNOTEXT);
:

これにより、Python内ではこれらの定数を以下のようにして参照できます(もちろん、本来の使いかたは、_estraiernative.Database.get_docメソッドの第2引数に使用することです)。

import _estraiernative

_estraiernative.Database.GDNOATTR | _estraiernative.Database.GDNOTEXT