アプリケーション開発ポータルサイト
ServerNote.NET
Amazon.co.jpでPC関連商品タイムセール開催中!
カテゴリー【GroongaDebianC/C++
【Groongaメモ・3】C言語ライブラリを使用し緯度経度周辺検索を行う
POSTED BY
2023-10-17

【Groongaメモ・2】データを作成し緯度経度周辺検索を行うの続きです。

前回の、東京駅(35.681454,139.767200)周辺10km以内にある駅を、近い順に距離(m)つきで100個出力する、というのをC言語APIを使用し実現します。コマンドコンソールでは以下のようなSQLでした。

select EkiPos --filter 'geo_in_circle(location, "35.681454,139.767200", 10000)' --scorer '_score = geo_distance(location, "35.681454,139.767200")' --output_columns '_key,_score' --sortby _score --offset 0 --limit 100

C言語APIについては以下にリファレンスがあるのですが

https://groonga.org/ja/docs/reference/api.html

これがすこぶるわかりづらい。。まずはライブラリのインストールが済んでいる必要があります。

sudo apt install libgroonga-dev

ヘッダファイルが/usr/include/groongaに入りますので、g++コンパイル時 -I/usr/include/groongaとします。

以下、上記を実現するサンプルソースです。

C/C++groonga_test.cGitHub Source
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <sys/time.h>
#include <groonga.h>

double getMicroTime(void) {
  struct timeval tv;
  gettimeofday(&tv, NULL);
  return (((double)tv.tv_sec)*((double)1000000)+((double)tv.tv_usec));
}

//grn_obj* getSelectParam(grn_ctx *ctx, grn_obj *obj, int index, const char *value) {
grn_obj* getSelectParam(grn_ctx *ctx, grn_obj *obj, const char *name, const char *value) {
//  grn_obj *ret = grn_expr_get_var_by_offset(ctx, obj, index);
  grn_obj *ret = grn_expr_get_var(ctx, obj, name, strlen(name));
  grn_obj_reinit(ctx, ret, GRN_DB_TEXT, 0);
  GRN_TEXT_PUTS(ctx, ret, value);
  return ret;
}

int main (int argc, char **argv) {
  grn_ctx ctx;
  grn_obj *db;
  grn_ctx_info info;
  const char *path = "/home/hogeuser/grn/db";
  double mt1,mt2;

  mt1 = getMicroTime();

  grn_init();
  grn_ctx_init(&ctx, 0);
  GRN_DB_OPEN_OR_CREATE(&ctx, path, NULL, db);
  grn_ctx_set_output_type(&ctx, GRN_CONTENT_JSON);

  mt2 = getMicroTime();
  printf("initialize library (%lfms)\n",(mt2 - mt1) / 1000);

  mt1 = getMicroTime();

/*
  grn_obj *obj = grn_ctx_get(&ctx, "select", strlen("select"));
  grn_obj *table = getSelectParam(&ctx, obj, 0, "EkiPos");
  grn_obj *filter = getSelectParam(&ctx, obj, 3, "geo_in_circle(location, \"35.681454,139.767200\", 10000)");
  grn_obj *scorer = getSelectParam(&ctx, obj, 4, "_score = geo_distance(location, \"35.681454,139.767200\")");
  grn_obj *output_columns = getSelectParam(&ctx, obj, 6, "_key,_score");
  grn_obj *sortby = getSelectParam(&ctx, obj, 5, "_score");
  grn_obj *offset = getSelectParam(&ctx, obj, 7, "0");
  grn_obj *limit = getSelectParam(&ctx, obj, 8, "100");
*/

  grn_obj *obj = grn_ctx_get(&ctx, "select", strlen("select"));
  grn_obj *table = getSelectParam(&ctx, obj, "table", "EkiPos");
  grn_obj *filter = getSelectParam(&ctx, obj, "filter", "geo_in_circle(location, \"35.681454,139.767200\", 10000)");
  grn_obj *scorer = getSelectParam(&ctx, obj, "scorer", "_score = geo_distance(location, \"35.681454,139.767200\")");
  grn_obj *output_columns = getSelectParam(&ctx, obj, "output_columns", "_key,_score");
  grn_obj *sortby = getSelectParam(&ctx, obj, "sortby", "_score");
  grn_obj *offset = getSelectParam(&ctx, obj, "offset", "0");
  grn_obj *limit = getSelectParam(&ctx, obj, "limit", "100");


  mt2 = getMicroTime();
  printf("create sql objects (%lfms)\n",(mt2 - mt1) / 1000);

  mt1 = getMicroTime();

  grn_expr_exec(&ctx, obj, 0);

  mt2 = getMicroTime();
  printf("grn_expr_exec (%lfms)\n",(mt2 - mt1) / 1000);

  mt1 = getMicroTime();

  grn_ctx_info_get(&ctx, &info);
  printf("%.*s\n", (int)GRN_TEXT_LEN(info.outbuf), GRN_TEXT_VALUE(info.outbuf));

  grn_expr_clear_vars(&ctx, obj);
  grn_obj_unlink(&ctx, obj);

  mt2 = getMicroTime();
  printf("get result print free (%lfms)\n",(mt2 - mt1) / 1000);

  mt1 = getMicroTime();

  grn_obj_close(&ctx, db);
  grn_ctx_fin(&ctx);
  grn_fin();

  mt2 = getMicroTime();
  printf("free library objects (%lfms)\n",(mt2 - mt1) / 1000);

  return 0;
}

流れとしては、ライブラリ・ハンドルの初期化、SELECT文オブジェクトの生成と付随パラメータオブジェクトをどんどん作成していきSELECTオブジェクトに登録してgrn_expr_execでSQL実行、結果文字列を取得し表示、開放です。

コンパイル・実行

g++ -I /usr/include/groonga groonga_test.c -lgroonga

./a.out

initialize library (25.814000ms)
create sql objects (0.411000ms)
grn_expr_exec (2.937000ms)
[[[538],[["_key","ShortText"],["_score","Int32"]],["1501-東京",99],["1448-東京",99],["1411-東京",99],["1261-東京",99],["1572-東京",99],["1898-東京",99],["1959-東京",99],["2213-東京",99],["2012-東京",99],["1305-東京",99],["5839-東京",227],["5888-大手町",384],["5913-二重橋前",475],["7638-大手町",537],["5820-日本橋",573],["5889-日本橋",573],["5838-大手町",574],["5821-京橋",574],["5912-大手町",623],["5957-三越前",658],["5956-大手町",661],["5940-有楽町",728],["1306-有楽町",733],["2013-有楽町",733],["7622-日本橋",768],["7621-宝町",782],["5941-銀座一丁目",788],["5819-三越前",851],["7637-日比谷",884],["1573-新日本橋",969],["5873-日比谷",990],["5840-銀座",1089],["5822-銀座",1089],["5872-銀座",1089],["5914-日比谷",1105],["1502-神田",1122],["1304-神田",1122],["2011-神田",1122],["5869-八丁堀",1146],["5890-茅場町",1169],["5868-茅場町",1169],["1899-八丁堀",1213],["7620-東銀座",1330],["5871-東銀座",1330],["5942-新富町",1353],["5887-竹橋",1385],["5818-神田",1386],["5866-小伝馬町",1444],["7623-人形町",1462],["5867-人形町",1462],["5939-桜田門",1485],["5837-淡路町",1551],["7663-小川町",1557],["5870-築地",1558],["5958-水天宮前",1643],["7636-内幸町",1666],["5874-霞ケ関",1694],["5915-霞ケ関",1694],["5841-霞ケ関",1694],["5823-新橋",1744],["7664-岩本町",1746],["7639-神保町",1759],["7662-神保町",1827],["5955-神保町",1827],["7665-馬喰横山",1835],["7590-築地市場",1837],["5911-新御茶ノ水",1846],["1412-新橋",1862],["2014-新橋",1862],["1262-新橋",1862],["1307-新橋",1862],["7619-新橋",1895],["7791-新橋",1896],["1574-馬喰町",1902],["7624-東日本橋",1980],["1303-秋葉原",1982],["2010-秋葉原",1982],["1551-秋葉原",1982],["5865-秋葉原",1997],["5824-虎ノ門",1999],["1550-御茶ノ水",2024],["1503-御茶ノ水",2024],["7765-秋葉原",2036],["7666-浜町",2044],["7591-汐留",2056],["5954-九段下",2085],["7661-九段下",2085],["5886-九段下",2085],["5836-御茶ノ水",2145],["5916-国 会議事堂前",2149],["5842-国会議事堂前",2149],["7792-汐留",2161],["5953-半蔵門",2352],["1552-浅草橋",2354],["7588-月島",2396],["5943-月島",2396],["5817-末広町",2421],["5952-永田町",2446],["5938-永田町",2446],["5975-永田町",2446]]]
get result print free (0.026000ms)
free library objects (2.995000ms)

コマンドコンソールで打ったのと全く同じ結果が得られました。結果をバイナリ構造体で操作する方法がわからず、返るのはJSON文字列です。parsonなどのパーサにかけるか、自力で文字列処理する感じになるでしょうか。

処理速度に関しては、約1万件の駅緯度経度データから周辺駅検索の結果としては、うーん、それほど速いとは言えない。。(2.93ミリ秒)もちろん呼び方が間違っている可能性も大いにあるわけですが。

あとライブラリの初期化と開放に妙に時間がかかってるのも気になるところ。

※本記事は当サイト管理人の個人的な備忘録です。本記事の参照又は付随ソースコード利用後にいかなる損害が発生しても当サイト及び管理人は一切責任を負いません。
※本記事内容の無断転載を禁じます。
【WEBMASTER/管理人】
自営業プログラマーです。お仕事ください!
ご連絡は以下アドレスまでお願いします★

☆ServerNote.NETショッピング↓
ShoppingNote / Amazon.co.jp
☆お仲間ブログ↓
一人社長の不動産業務日誌
【キーワード検索】