上のエントリーで後日クローラ、インデクサ部分を公開すると書きましたが、クローラ部分がかなり時間がかかりそうなので、インデクサを含むPythonバインディング部分を合わせたLuxのPythonバインディングを公開しておきます。
ソースコード
#include <lux/search.h> #include <lux/index.h> #include <iostream> #include <string> #include <time.h> #include <sys/time.h> #include <boost/python.hpp> using namespace std; class Searcher { public: Searcher() { engine = NULL; } virtual ~Searcher() { if(engine!=NULL) { delete engine; } } int set_service(string service) { Lux::Config::Document doc_config; if (!Lux::DocumentConfigParser::parse(service, doc_config)) { return 0; } engine = new Lux::Engine(doc_config); if (engine->open(service, Lux::DB_RDONLY)) { return 0; } return 1; } int set_keys(PyObject* py_obj) { if(PyList_Check(py_obj)) { PyListObject* py_keys = (PyListObject*)py_obj; if (py_keys) { int num_keys = PyList_GET_SIZE(py_keys); keys.reserve(num_keys); for (int i=0; i<num_keys; i++) { PyObject* py_key = PyList_GET_ITEM(py_keys, i); if (PyString_Check(py_key)) { keys.push_back(PyString_AS_STRING(py_key)); } else { return 0; } } } } else { return 0; } return 1; } PyObject* get_rs(string query, int pages) { double t1, t2; t1 = gettimeofday_sec(); // create search condition Lux::SortCondition scond(Lux::SORT_SCORE, Lux::DESC); Lux::Paging paging(pages); Lux::Condition cond(scond, paging); Lux::Searcher searcher(*engine); Lux::ResultSet rs = searcher.search(query, cond); t2 = gettimeofday_sec(); PyObject* pt(PyDict_New()); // create result py object PyDict_SetItemString(pt, "total_hits", PyInt_FromLong(rs.get_total_num())); PyDict_SetItemString(pt, "base", PyInt_FromLong(rs.get_base())); PyDict_SetItemString(pt, "num", PyInt_FromLong(rs.get_num())); PyDict_SetItemString(pt, "time", PyFloat_FromDouble(t2-t1)); PyObject* prs(PyTuple_New(rs.get_num())); rs.init_iter(); uint32_t index = 0; while (rs.has_next()) { Lux::Result r = rs.get_next(); PyObject* pr(PyDict_New()); PyDict_SetItemString(pr, "id", PyString_FromString(r.get_id().c_str())); PyDict_SetItemString(pr, "score", PyInt_FromLong(r.get_score())); for (vector<string>::iterator i=keys.begin();i!=keys.end();++i) { PyDict_SetItemString(pr, (*i).c_str(), PyString_FromString(r.get(*i).c_str())); } PyTuple_SetItem(prs, index, pr); index++; } PyDict_SetItemString(pt, "result_set", prs); return pt; } protected: Lux::Engine* engine; vector<string> keys; double gettimeofday_sec() { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec + (double)tv.tv_usec*1e-6; } }; class Indexer { public: Indexer() { engine = NULL; indexer = NULL; } virtual ~Indexer() { if(engine!=NULL) { delete engine; } if(indexer!=NULL){ delete indexer; } } int set_service(string service) { Lux::Config::Document doc_config; Lux::DocumentConfigParser::parse(service, doc_config); // search for the query engine = new Lux::Engine(doc_config); if (!engine->open(service, Lux::DB_CREAT)) { return 0; } indexer = new Lux::Indexer(*engine); return 1; } int add(string id, PyObject* py_obj) { if(PyDict_Check(py_obj)) { PyListObject* keys = (PyListObject*) PyDict_Keys(py_obj); if (keys) { PyListObject* values = (PyListObject*) PyDict_Values(py_obj); if (values) { int num_keys = PyList_GET_SIZE(keys); int num_values = PyList_GET_SIZE(values); if (num_keys == num_values) { Lux::Document* doc = new Lux::Document(id); for (int i=0; i<num_keys; i++) { PyObject* key = PyList_GET_ITEM(keys, i); if (PyString_Check(key)) { PyObject* value = PyList_GET_ITEM(values, i); doc->add(Lux::Field::create(PyString_AS_STRING(key), PyString_AS_STRING(value))); } } indexer->add(doc); delete doc; } else { return 0; } } } } else { return 0; } return 1; } protected: Lux::Engine* engine; Lux::Indexer* indexer; }; BOOST_PYTHON_MODULE(lux_python) { using namespace boost::python; class_<Searcher>("Searcher") .def("set_service", &Searcher::set_service) .def("set_keys", &Searcher::set_keys) .def("search", &Searcher::get_rs) ; class_<Indexer>("Indexer") .def("set_service", &Indexer::set_service) .def("add", &Indexer::add) ; }
このソースコードを以下のようにコンパイルするとlux_python.soが作成されます。
g++ -I/usr/include/python2.5 -shared -o lux_python.so $^ -lboost_python -llux -fPIC
使い方
インデックス部分(Indexer)の使い方です。実行するディレクトリに上のセクションで生成したlux_python.soが存在するとします。データはluxのexampleに付属するpostsです。クローラで取得したデータはインデックス部分(Indexer)を用いてインデックス作成を行います。
import lux_python li = lux_python.Indexer() li.set_service("blogs") file_obj = open("posts", "r") index = 0 for i in file_obj.readlines(): key_value = {} line = i.split("\x18") id = line[0] key_value["created_at"] = line[1] key_value["url"] = line[2] key_value["title"] = line[3] if(li.add(id, key_value)): print "%s item indexed."%index index = index + 1
検索部分の使い方です。実行するディレクトリに上のセクションで生成したlux_python.soが存在するとします。データはluxのexampleに付属する上のインデックス部分(Indexer)で作成したblogsです。具体的なWebアプリでの使い方は上のエントリーを参照してください。
import lux_python lse = lux_python.Searcher() lse.set_service("blogs") lse.set_keys(["title", "created_at", "url"]) print lse.search("google", 5)
まとめ
基本的な使い方はこのboost-pythonを用いたお手軽Pythonバインディングでカバーできると思います。どれくらい需要があるか未知数ですがもしよろしければお使い下さい。