あいまいな動的テキスト検索? そんなに怖くない

ウラルジミール・ルミャンツンフフ-サンクトペテルブリュクの冒冒...猫

ダイナミクスにおけるファジー検索(オンライン)という強い意見があります

信じられないほど複雑なためアクセスできません。

次に、この迷惑なエラーを解消し、表示します

許容できるパフォーマンスを備えた独自の検索エンジンを構築するもの

誰でも小さなデータを利用できるわけではありません。



主なアイデアは次のとおりです。



テストデータ。

実験のために、プーシキンとドストエフスキー、アリギエーリの「神の喜劇」、トルストイの「戦争と平和」を英訳(出典-www.lib.ruwww.gutenberg.org )で取り上げます。 utf8エンコーディングで18 mbのみ。

空でないテキストの各行は、データベース内の1つのエントリになります。

行が長い場合、800ワードに分割されます。

合計で約16万件のレコード



DBMS

以前と同様OpenLink Virtuosoバージョン7.0.0を使用します。 Cプラグインだけで管理することはできません。 プラグインで利用可能な機能は十分ではありません。 サーバーをOEM共有ライブラリー(libvirtuoso-t)として接続し、同時にエクスポートされた関数のリストを少し魔法で操作する必要があります。



データスキーマ

まず、辞書:
create table MRC_WORDS ( WD_ID integer, WD_ITSELF nvarchar, WD_COUNT integer, primary key (WD_ID));
      
      



各エントリには、単語自体、その識別子、および出現頻度が含まれています。 テキストを挿入するたびに頻度を更新するとコストが高くなりすぎるため、メモリ内で変更され、定期的に記録されます。 または、「クラウン」で更新できます。 この頻度はランキングに役立つ場合がありますが、現在は使用しません。



トライグラム:
 create table MRC_TRIPLES ( TR_ID integer identity, TR_DATA nvarchar, TR_WORDID integer, primary key(TR_DATA, TR_WORDID, TR_ID));
      
      



各エントリには、トライグラム自体、それが由来する単語の識別子、およびトリグラムが単語内で複数回見つかった場合の一意の識別子が含まれます(例: 'Even cue cue ')



出現リスト:
 create table MRC_DATA ( DT_WORDID integer, DT_OID integer, DT_COL integer, DT_POSITION integer, primary key(DT_WORDID, DT_OID, DT_COL, DT_POSITION));
      
      



ここで、どの位置のどの単語がどこに会ったかを保存します。



実際のデータ:
 create table MRC_TEXT ( TX_ID integer identity, TX_AUTHOR nvarchar, TX_SUBJ nvarchar, TX_STRING nvarchar, TX_LONG_STRING long nvarchar, TX_TS timestamp, primary key (TX_ID));
      
      



テストタスクのすべてのデータは、作成者、作品、テキストという3つの列の1つのテーブルに保存されます。 テキストが500文字を超える場合、ブロブに分類されます。 もちろん、テキストはさまざまなテーブルに表示される可能性があり、インデックスはマルチテーブルになります。 これをどのように扱うかはここに書かれています



トリガーを挿入

挿入のトリガー内のすべてのインデックスを非表示にします。

 create trigger MRC_TEXT_I after insert on MRC_TEXT { declare wordid integer; declare str nvarchar; str := coalesce(TX_STRING, cast(blob_to_string(TX_LONG_STRING)as nvarchar)); MRC_PROCESS_STR_I(str, TX_ID, 1); str := TX_SUBJ; MRC_PROCESS_STR_I(str, TX_ID, 2); str := TX_AUTHOR; MRC_PROCESS_STR_I(str, TX_ID, 3); };
      
      



つまり インデックス付きフィールドごとにMRC_PROCESS_STR_I関数を3回呼び出します。

 create procedure MRC_PROCESS_STR_I( in str nvarchar, in oid integer, in col integer) { if (str is not null) { declare vec any; vec := nv_split(str); declare n any; declare wordid any; if (vec <> 0 and vec is not null) { n := length(vec); declare i integer; i := 0; while (i < n) { wordid := treat_nword(vec[i])); if (wordid > 0) { insert into MRC_DATA (DT_WORDID, DT_OID, DT_COL, DT_POSITION) values (wordid, oid, col, i); } i := i + 1; } } } };
      
      



ここで、 nv_split関数を使用して文字列を個別の単語に分割し、各単語をtreat_nwordで処理し、 MRC_DATAテーブルに各単語に関するデータを書き込みます。

上記のnv_splittreat_nwordは(このタスクのために)Cで記述されており、プラグインインターフェイスからアクセスできます。

最初のものはすべて明らかで、2番目のものは単語を3グラムに解析し、対応するテーブルに書き込み、単語テーブルに単語を書き込み、メモリ内の辞書を更新する必要があります。



メモリ内の辞書

次のエンティティで構成されます。

それとは別に、単語をトライグラムに分割する場合、単語の先頭と末尾にスペースが追加されるため、単語の正しい先頭および/または末尾にボーナスが提供されることに注意してください。



語彙検索

辞書検索の結果は、候補のリストと提示されたサンプルとの類似性です。

アルゴリズムは次のとおりです。

構造検索

この場合の構造検索のタスクは、すべてのソースワードの候補が見つかったエントリの識別子のリストを作成するために、辞書検索によって発行された候補のリストに基づいています。

たとえば、私たちのようにデータが比較的少ない場合、割り当てられたメモリの量を特に気にする必要はなく、関心のある辞書候補のすべての行識別子をダウンロードするだけです。 だから:

メモリ内のすべての識別子を保持するにはデータが多すぎるとします。 この場合、次のことを行います。

ボキャブラリーの候補がいたるところに繰り返し見つかった場合、これはおそらく不要な単語であり、単に無視する必要があることに注意してください。



ランキング

かなり原始的なランキングスキームを使用します。



PL / SQLを検索

通常のSQLで検索を使用するためのプライマリ識別子のフローを整理するには、それにアクセスするために次の手順と手順ビューが必要です。

 create procedure MRC_QUERY_STRING_ALL ( in query varchar) { declare vec any; declare len integer; result_names('oid','score'); vec := query_phrase(query); if (vec <> 0 and vec is not null) { len := length(vec); declare i integer; i := 0; while(i<len) { declare oid integer; oid := vec[i]; result (oid, vec[i + 1]); i := i + 2; } } }; create procedure view v_query_phrase_all as MRC_QUERY_STRING_ALL(str)(oid integer, score integer);
      
      





クエリは次のようになります。
 select a.TX_ID, a.TX_TS, b.score, a.TX_AUTHOR, a.TX_SUBJ, coalesce (a.TX_STRING, blob_to_string(TX_LONG_STRING)) from MRC_TEXT as a, v_query_phrase_all as b where b.str = 'Posting Date' and a.TX_ID = b.oid;
      
      



使用されるquery_phrase関数はC拡張であり、上記のすべての低レベルのアクティビティを実行します。



ベンチマーク

i7-3612QM、Win64。

160 254レコードを埋めるには、3分2秒またはレコードあたり1.14 msかかります。

検索をテストするために、各レコードの最初の2つの単語、1、2、および4ストリームで合計160,254の検索クエリを検索します。 行を持ち上げて渡す時間を考慮しないように、見つかったレコードの数のみを検索します。 クエリは、ネイティブODBCインターフェイス、ローカルホスト経由のTCP / IPを介して実行されます。

Nスレッド 総時間 1リクエスト
1 11'7 '' 4.16ミリ秒
2 11'57 '' 4.47ミリ秒
4 14'51 5.61ミリ秒
結論道徳

作成、発明、試してください! (C)マヤコフスキー



PS

機能からのテキスト、突然誰かが重宝します
#include <stdio.h>

#include <stdlib.h>

#include <string.h>



#include <libutil.h>



#ifdef WIN32

#include <crtdbg.h>

#endif



#include "sqlnode.h"

#include "sqlbif.h"

#include "wi.h"

#include "Dk.h"



#include <math.h>

#include "caseutils.h"

#include "list_sort.h"

#include <assert.h>



//#<ksrvext.h>を含める



static id_hash_t * ht_dict_ = NULL;

static dk_hash_t * ht_dict_by_id_ = NULL;

static id_hash_t * ht_triples_ = NULL;

static dk_mutex_t * dict_mtx_ = NULL;



struct dict_item_s {

char * word_;

size_t id_;

size_t count_;

size_t attr_;

};

typedef struct dict_item_s dict_item_t;



struct triple_item_s {

size_t wordid_;

struct triple_item_s * next_;

};

typedef struct triple_item_s triple_item_t;



struct triple_head_s {

lenmem_t lm_;

wchar_t data_ [4];

triple_item_t * list_;

};

typedef struct triple_head_s triple_head_t;



const wchar_t seps_ [] = L "、。\ t \ r \ n '\" = *!%^:;〜 `<> + |?";

const wchar_t glues_ [] = L "()&@#$:{} / \\-[] _";



size_t next_wordid(caddr_t * qst)

{

size_t _id = 0;

query_instance_t * q =(query_instance_t *)qst;

client_connection_t * cli = q-> qi_client;

query_t * stmt = NULL;

local_cursor_t * lc = NULL;

caddr_t lerr = NULL;

caddr_t * err =&lerr;

char buf [1024];

sprintf(buf、 "select sequence_next( 'MRC_WORD_ID')");

if(NULL ==(stmt = sql_compile(buf、cli、err、0)))

後藤終了;



if(NULL!=(* err =

qr_rec_exec(stmt、cli、&lc、(query_instance_t *)qst、NULL、

0)))

後藤終了;



終了:

if(lc)

{

lc_next(lc);

_id =(size_t)unbox(lc_nth_col(lc、0));

}

if(lc)

{

lc_free(lc);

lc = NULL;

}

if(stmt)

{

qr_free(stmt);

stmt = NULL;

}

return _id;

}



size_t store_triple(caddr_t * qst、size_t wordid、const wchar_t * triple)

{

query_instance_t * q =(query_instance_t *)qst;

client_connection_t * cli = q-> qi_client;

query_t * stmt = NULL;

local_cursor_t * lc = NULL;

caddr_t lerr = NULL;

caddr_t * err =&lerr;

char buf [1024];

wchar_t wlt [4];

wcsncpy(wlt、triple、3);

wlt [3] = 0;



sprintf(buf、 "--utf8_execs = yes \ nをMRC_TRIPLES(TR_DATA、TR_WORDID)値に挿入(?、?)");

if(NULL ==(stmt = sql_compile(buf、cli、err、0)))

後藤終了;



if(NULL!=(* err =

qr_rec_exec(stmt、cli、&lc、(query_instance_t *)qst、NULL、2

「:0」、box_wide_string(wlt)、QRP_RAW、

「:1」、box_num(wordid)、QRP_RAW

)))

後藤終了;

{

char ** place = NULL;



triple_item_t * pitem =(triple_item_t *)dk_alloc_box_zero(sizeof(triple_item_t)、DV_BIN);

triple_head_t * phead =(triple_head_t *)dk_alloc_box_zero(sizeof(triple_head_t)、DV_BIN);



phead-> lm_.lm_length = sizeof(phead-> data_);

phead-> lm_.lm_memblock =(char *)phead-> data_;

memcpy(phead-> data_、wlt、sizeof(phead-> data_));



pitem-> wordid_ = wordid;



place =(char **)id_hash_get(ht_triples_、(caddr_t)&phead-> lm_);

if(場所)

{

triple_head_t * ohead = *(triple_head_t **)場所;

pitem-> next_ = ohead-> list_;

ohead-> list_ = pitem;

}

他に

{

id_hash_set(ht_triples_、(caddr_t)(&phead-> lm_)、(caddr_t)&phead);

pitem-> next_ = pitem;

}

}



終了:

if(lc)

{

lc_free(lc);

lc = NULL;

}

if(stmt)

{

qr_free(stmt);

stmt = NULL;

}

0を返します。

}



wchar_t **

nv_split(wchar_t * tmp)

{

wchar_t ** arr = NULL;

size_t len = wcslen(tmp);

size_t ix = 0;

size_t i;

size_t cnt = 0;

for(i = 0; i <len; i ++)

{

if(NULL!= wcschr(seps_、tmp [i]))

{

tmp [ix ++] = 0;

cnt ++;

}

他に

{

if(NULL == wcschr(glues_、tmp [i]))

tmp [ix ++] = mrc_toupper(tmp [i]);

}

}

tmp [ix] = 0;

cnt = 0;

for(i = 0; i <len; i ++)

{

if(tmp [i])

{

cnt ++;

while(i <len && 0!= tmp [++ i]);

}

}

if(cnt)

{

/ *そして、その多くの要素の1回または2回のベクトルを割り当てます。 * /

arr = dk_alloc_box((cnt * sizeof(caddr_t))、DV_ARRAY_OF_POINTER);



ix = 0;

for(i = 0; i <len; i ++)

{

if(0!= tmp [i])

{

int loclen = wcslen(tmp + i);

((caddr_t *)arr)[ix] =((char *)dk_alloc_box_zero((loclen + 1)* sizeof(wchar_t)、DV_LONG_WIDE));

memcpy(((caddr_t *)arr)[ix ++]、tmp + i、(loclen + 1)* sizeof(wchar_t));

while(i <len && 0!= tmp [++ i]);

}

}

}

return arr;

}



caddr_t

bif_nv_split(caddr_t * qst、caddr_t * err_ret、state_slot_t ** args)

{

char * me = "nv_split";

caddr_t arr = NULL;

caddr_t arg = bif_arg_unrdf(qst、args、0、me);

dtp_t dtp = DV_TYPE_OF(arg);

if(DV_DB_NULL == dtp || NULL == arg)

{

return(NULL);

}

if(IS_STRING_DTP(dtp))

{

wchar_t * wide = box_utf8_as_wide_char(arg、NULL、strlen(arg)、0、DV_WIDE);

arr =(caddr_t)nv_split(ワイド);

dk_free_box(ワイド);

return arr;

}

if(IS_WIDE_STRING_DTP(dtp))

{

wchar_t * tmp = wcsdup((const wchar_t *)arg);

arr =(caddr_t)nv_split(tmp);

無料(tmp);

return arr;

}



{

sqlr_new_error( "22023"、 "SR007"、

「Function%sには引数としてnvstringまたはNULLが必要です、„

「タイプ%s(%d)の引数ではありません」、

me、1、dv_type_title(dtp)、dtp);

}

return arr;

}



caddr_t

bif_treat_nword(caddr_t * qst、caddr_t * err_ret、state_slot_t **引数)

{

char * me = "treat_nword";

caddr_t arg = bif_arg_unrdf(qst、args、0、me);

int len;

const wchar_t * wide =(const wchar_t *)arg;

const wchar_t * newwide = NULL;

wchar_t * tmpbuf;

size_t wordid = 0;

dtp_t dtp = DV_TYPE_OF(arg);

if(DV_DB_NULL == dtp)

{

return(NULL);

}

if(!IS_WIDE_STRING_DTP(dtp))

{

sqlr_new_error( "22023"、 "SR007"、

「Function%sには引数としてnvstringまたはNULLが必要です、„

「タイプ%s(%d)の引数ではありません」、

me、1、dv_type_title(dtp)、dtp);

}

len = wcslen(ワイド);

tmpbuf =(wchar_t *)_ alloca(sizeof(wchar_t)*(len + 3));

tmpbuf [0] = L "';

wcscpy(tmpbuf + 1、ワイド);

tmpbuf [len + 1] = L "';

tmpbuf [len + 2] = 0;

newwide = tmpbuf;



mutex_enter(dict_mtx_);

{

char * utf8 = box_wide_as_utf8_char((const char *)wide、len、DV_LONG_STRING);

char ** place = NULL;



place =(char **)id_hash_get(ht_dict_、(caddr_t)&utf8);

if(場所)

{

dict_item_t * pitem = *(dict_item_t **)場所;

pitem-> count _ ++;

dk_free_box(utf8);

wordid = pitem-> id_;

}

他に

{

query_instance_t * q =(query_instance_t *)qst;

client_connection_t * cli = q-> qi_client;

query_t * stmt = NULL;

local_cursor_t * lc = NULL;

caddr_t lerr = NULL;

caddr_t * err =&lerr;

char buf [1024];



dict_item_t * pitem = dk_alloc_box_zero(sizeof(dict_item_t)、DV_BIN);

pitem-> word_ = utf8;

pitem-> count_ = 1;

wordid = next_wordid(qst);

pitem-> id_ = wordid;

id_hash_set(ht_dict_、(caddr_t)&utf8、(caddr_t)&pitem);

sethash((void *)wordid、ht_dict_by_id_、(void *)pitem);



sprintf(buf、 "--utf8_execs = yes \ n MRC_WORDS(WD_ITSELF、WD_ID)値への挿入(?、?)");

//キャスト(charset_recode( '%s'、 'UTF-8'、 '_WIDE_')as nvarchar)) "、wordid、utf8);

if(NULL!=(stmt = sql_compile(buf、cli、err、0)))

{

*エラー=

qr_rec_exec(stmt、cli、&lc、(query_instance_t *)qst、NULL、2

「:0」、box_wide_string(newwide)、QRP_RAW、

「:1」、box_num(wordid)、QRP_RAW

);

// * err = qr_rec_exec(stmt、cli、&lc、(query_instance_t *)qst、NULL、0);

}

if(lc)

lc_free(lc);

if(stmt)

qr_free(stmt);



{

int len = wcslen(newwide);

int i;

for(i = 0; i <len-2; i ++)

{

store_triple(qst、wordid、newwide + i);

}

}

}

}

mutex_leave(dict_mtx_);

return box_num(wordid);

}



int64

box2long(caddr_t arg)

{

dtp_t dtp = DV_TYPE_OF(arg);

if(dtp == DV_SHORT_INT || dtp == DV_LONG_INT)

return(int64)(unbox(arg));

else if(dtp == DV_SINGLE_FLOAT)

return(int64)(unbox_float(arg));

else if(dtp == DV_DOUBLE_FLOAT)

return(int64)(unbox_double(arg));

else if(dtp == DV_NUMERIC)

{

int64 dt;

numeric_to_int64((numeric_t)arg、&dt);

return dt;

}

else if(dtp == DV_DB_NULL)

return(int64)(0);

アサート(0);

0を返します。

}



void flush_dict()

{

char ** key = NULL;

char ** val = NULL;

id_hash_iterator_t hit;

id_hash_iterator(&hit、ht_dict_);

while(hit_next(&hit、(caddr_t *)&key、(caddr_t *)&val))

{

dk_free_box(*キー);

dk_free_box(* val);

}

id_hash_clear(ht_dict_);

}



void flush_triples()

{

char ** key = NULL;

char ** val = NULL;

id_hash_iterator_t hit;

id_hash_iterator(&hit、ht_triples_);

while(hit_next(&hit、(caddr_t *)&key、(caddr_t *)&val))

{

triple_head_t * phead = *(triple_head_t **)val;

triple_item_t * pit = phead-> list_;



while(pit)

{

triple_item_t * tmp = pit-> next_;

dk_free_box(ピット);

pit = tmp;

}

dk_free_box(* val);

}

id_hash_clear(ht_triples_);

}



size_t reload_triples(query_instance_t * qst)

{

client_connection_t * cli = qst-> qi_client;

query_t * stmt = NULL;

local_cursor_t * lc = NULL;

caddr_t lerr = NULL;

caddr_t * err =&lerr;

char buf [1024];



flush_triples();



sprintf(buf、「MRC_TRIPLESからTR_DATA、TR_WORDIDを選択」);

if(NULL!=(stmt = sql_compile(buf、cli、err、0)))

{

* err = qr_rec_exec(stmt、cli、&lc、(query_instance_t *)qst、NULL、0);

if(lc)

{

int64 id = 0;

caddr_t tmp = 0;

int64 cnt = 0;

char * utf8 = NULL;

char ** place = NULL;

lenmem_t lm;

triple_head_t * phead = NULL;

triple_head_t * ohead = NULL;

triple_item_t * pitem = NULL;



while(lc_next(lc))

{

if(lc-> lc_error)

{

* err = box_copy_tree(lc-> lc_error);

休憩;

}

id = box2long(lc_nth_col(lc、1));

tmp = lc_nth_col(lc、0);



pitem =(triple_item_t *)dk_alloc_box_zero(sizeof(triple_item_t)、DV_BIN);

pitem-> wordid_ =(size_t)id;



lm.lm_length = sizeof(phead-> data_);

lm.lm_memblock =(caddr_t)tmp;



place =(char **)id_hash_get(ht_triples_、(caddr_t)&lm);

if(場所)

{

ohead = *(triple_head_t **)場所;

pitem-> next_ = ohead-> list_;

ohead-> list_ = pitem;

}

他に

{

phead =(triple_head_t *)dk_alloc_box_zero(sizeof(triple_head_t)、DV_BIN);

phead-> list_ = pitem;

phead-> lm_.lm_length = sizeof(phead-> data_);

phead-> lm_.lm_memblock =(caddr_t)phead-> data_;

memcpy(phead-> data_、tmp、sizeof(phead-> data_));



pitem-> next_ = NULL;

id_hash_set(ht_triples_、(caddr_t)&phead-> lm_、(caddr_t)&phead);

}

}

}

}

if(lc)

lc_free(lc);

if(stmt)

qr_free(stmt);



0を返します。

}



caddr_t

bif_reload_dict(caddr_t * qst、caddr_t * err_ret、state_slot_t ** args)

{

query_instance_t * q =(query_instance_t *)qst;

client_connection_t * cli = q-> qi_client;

query_t * stmt = NULL;

local_cursor_t * lc = NULL;

caddr_t lerr = NULL;

caddr_t * err =&lerr;

char buf [1024];



mutex_enter(dict_mtx_);

flush_dict();



sprintf(buf、「WD_ID、WD_ITSELF、WD_COUNTをMRC_WORDSから選択」);

if(NULL!=(stmt = sql_compile(buf、cli、err、0)))

{

* err = qr_rec_exec(stmt、cli、&lc、(query_instance_t *)qst、NULL、0);

if(lc)

{

int64 id = 0;

caddr_t tmp = 0;

int64 cnt = 0;

char * utf8 = NULL;

char ** place = NULL;

size_t maxid = 0;



while(lc_next(lc))

{

if(lc-> lc_error)

{

* err = box_copy_tree(lc-> lc_error);

休憩;

}

id = box2long(lc_nth_col(lc、0));

tmp = lc_nth_col(lc、1);

cnt = box2long(lc_nth_col(lc、2));

utf8 = box_wide_as_utf8_char(tmp、box_length(tmp)/ sizeof(wchar_t)-1、DV_LONG_STRING);



place =(char **)id_hash_get(ht_dict_、(caddr_t)&utf8);

if(場所)

{

アサート(0);

}

他に

{

dict_item_t * pitem = dk_alloc_box_zero(sizeof(dict_item_t)、DV_BIN);

pitem-> word_ = utf8;

pitem-> count_ = 1;

pitem-> id_ =(size_t)id;

if(maxid <id)

maxid = id;

id_hash_set(ht_dict_、(caddr_t)&utf8、(caddr_t)&pitem);

sethash((void *)id、ht_dict_by_id_、(void *)pitem);

}

}

}

}

if(lc)

lc_free(lc);

if(stmt)

qr_free(stmt);



reload_triples(q);



mutex_leave(dict_mtx_);

0を返します。

}



// ------------------------------------------------ ---------------------------

// l_dist_raw()

//静的/ローカル関数!!!

//

//目的:2つの文字列(単語)のL距離を計算します。

//

//入力:char * str1、* str2-比較する入力文字列(単語)

// int len1、len2-str1およびstr2の長さの短い方

//それぞれまたはMAX_LDIST_LEN。

//注意! エラーチェックは行われません。

//スタック上の配列オーバーフローが発生します

//どちらかが範囲外の場合。

//出力:なし

//

//戻り値:L距離値が返されます

//

//注、このコメントヘッダーの直後に2つの定義があり、

//この関数でのみ使用されます。

//

//(すべてのCAPSの値はLDIST.Hヘッダーファイルで定義されています)

//

// ------------------------------------------------ ---------------------------

#define MAX_LDIST_LEN 40 //比較する最大単語len

#define MIN3(a、b、c)(a <b?\

(a <c?a:c):\

(b <c?b:c))



int

l_dist_raw(const wchar_t * str1、const wchar_t * str2、int len1、int len2)

{

int arr1 [MAX_LDIST_LEN + 1];

int arr2 [MAX_LDIST_LEN + 1];

int i、j;

if(len1> MAX_LDIST_LEN)

len1 = MAX_LDIST_LEN;

if(len2> MAX_LDIST_LEN)

len2 = MAX_LDIST_LEN;

for(i = 0; i <= len2; i ++)

arr1 [i] = i;



for(i = 1; i <= len1; i ++)

{

arr2 [0] = i;

for(j = 1; j <= len2; j ++)

{

intスコア=(str1 [i-1] == str2 [j-1])?0:1;

int i1 = arr2 [j-1] +1;

int i2 = arr1 [j] +1;

int i3 = arr1 [j-1] +スコア。

arr2 [j] = MIN3(i1、i2、i3); // arr2 [j-1] + 1、arr1 [j] + 1、arr1 [j] +スコア);

// d [(j-1)* n + i] +1、d [j * n + i-1] +1、d [(j-1)* n + i-1] + cost);

}

memcpy(arr1、arr2、sizeof(int)*(len2 + 1));

}

return arr2 [len2];

}



struct ipair_s {

ptrlong id_;

ptrlong len_;

ptrlong pos_;

ptrlong score_;

};

typedef struct ipair_s ipair_t;



int

cmp_pairs(const void * a、const void * b)

{

const ipair_t * pa = *(const ipair_t **)a;

const ipair_t * pb = *(const ipair_t **)b;

if(pb-> id_ == pa-> id_)

return pa-> score_-pb-> score_;

return pa-> id_-pb-> id_;

}



int compare_by_id(定数void * a、定数void * b、定数void * arg)

{

ipair_t * pa =(ipair_t *)a;

ipair_t * pb =(ipair_t *)b;

return pa-> id_-pb-> id_;

}



int compare_by_score(const void * a、const void * b、const void * arg)

{

ipair_t * pa =(ipair_t *)a;

ipair_t * pb =(ipair_t *)b;

return pb-> score_-pa-> score_;

}



dk_set_t

load_oid_list(ipair_t **単語、query_instance_t * q、mem_pool_t * mp)

{

client_connection_t * cli = q-> qi_client;

/ * static * / query_t * stmt = NULL;

local_cursor_t * lc = NULL;

caddr_t lerr = NULL;

caddr_t * err =&lerr;

dk_set_t out_list = NULL;

char buf [1024];

dk_set_t pairs_list = NULL;

ipair_t * item = NULL;

size_t i = 0;

size_t len = box_length(単語)/ sizeof(ipair_t *);

size_t cnt = 0;



if(NULL == stmt)

{

sprintf(buf、「DT_WORDID =?„のMRC_DATAからDT_OID、DT_POSITIONを選択);

if(NULL ==(stmt = sql_compile_static(buf、/ *ブートストラップ_ * / cli、err、0)))

NULLを返します。

}

for(i = 0; i <len; i ++)

{

size_t id;

ipair_t * pair = words [i];

// printf(“ \ n ---%d ----- \ n”、pair-> id_);

* err = qr_rec_exec(stmt、cli、&lc、(query_instance_t *)q、NULL、1

「:0」、box_num(pair-> id _)、QRP_RAW);

if(NULL == lc)

続ける;



while(lc_next(lc))

{

if(lc-> lc_error)

{

* err = box_copy_tree(lc-> lc_error);

休憩;

}

id = box2long(lc_nth_col(lc、0));

item =(ipair_t *)mp_alloc_box(mp、sizeof(ipair_t)、DV_ARRAY_OF_LONG);

item-> id_ = id;

item-> len_ = pair-> len_;

item-> pos_ = box2long(lc_nth_col(lc、1));

item-> score_ = pair-> score_;

mp_set_push(mp、&pairs_list、item);

cnt ++;

// printf( "%d"、id);

}

if(lc)

lc_free(lc);

}

if(stmt)

qr_free(stmt);



// if(stmt)

// qr_free(stmt);

// printf( "+%d +"、cnt);

return list_sort(pairs_list、compare_by_id、NULL);

}



ipair_t **

get_word_candidates(wchar_t * arg)

{

ipair_t ** res = NULL;

dk_set_t ids_list = NULL;

caddr_t arr = NULL;



{

dk_hash_t * ht_ids = NULL;

int maxcount = 1;

size_t i;



wchar_t * word =(wchar_t *)arg;

wchar_t * pbuf = NULL;

size_t isnum =((* word)> = L'0 '&&(* word)<= L'9');

size_t len = wcslen(単語);

// int slen = len;

if(len <3 &&!isnum)

{

NULLを返します。

}

word =(wchar_t *)box_copy(word);

mrc_toupper_str(単語);



pbuf =(wchar_t *)_ alloca(sizeof(wchar_t)*(len + 3));

pbuf [0] = L "';

wcscpy(pbuf + 1、単語);

pbuf [len + 1] = L "';

pbuf [len + 2] = L '\ 0';



ht_ids = hash_table_allocate(101);



mutex_enter(dict_mtx_);

for(i = 0; i <(len); i ++)

{

char ** place = NULL;

lenmem_t lm;

triple_head_t * phead = NULL;

triple_item_t * pitem = NULL;

wchar_t trbuf [4];

trbuf [0] = mrc_toupper(pbuf [i]);

trbuf [1] = mrc_toupper(pbuf [i + 1]);

trbuf [2] = mrc_toupper(pbuf [i + 2]);

trbuf [3] = L '\ 0';



lm.lm_length = sizeof(phead-> data_);

lm.lm_memblock =(caddr_t)trbuf;



place =(char **)id_hash_get(ht_triples_、(caddr_t)&lm);

if(場所)

{

phead = *(triple_head_t **)場所;

pitem = phead-> list_;

while(pitem)

{

int wordid = pitem-> wordid_;



int ptr =(int)gethash((void *)wordid、ht_ids);

if(0 == ptr)

sethash((void *)wordid、ht_ids、(void *)1);

他に

{

sethash((void *)wordid、ht_ids、(void *)(++ ptr));

if(ptr> maxcount)

maxcount = ptr;

}



pitem = pitem-> next_;

}

}

}

mutex_leave(dict_mtx_);



{

dk_set_t pairs_list = NULL;

int nids = 0;

int mx = maxcount;

int nallids = ht_ids-> ht_count;

void * key、* val;

dk_hash_iterator_t hit;



maxcount =(maxcount + 1)/ 2;

if(maxcount> = len)

maxcount = len-1;

for(dk_hash_iterator(&hit、ht_ids);

dk_hit_next(&hit、(void **)&key、(void **)&val);

/ * * /)

{

int wordid =(int)key;

int cnt =(int)val;

if(cnt> = maxcount)

{

dict_item_t * pptr =(dict_item_t *)gethash((void *)wordid、ht_dict_by_id_);

if(pptr)

{

ipair_t * item = NULL;

wchar_t buf [128];

size_t lbuf、dist、score;

box_utf8_as_wide_char((caddr_t)pptr-> word_、(caddr_t)buf、strlen(pptr-> word_)、127、DV_WIDE);

lbuf = wcslen(buf);

dist = l_dist_raw(word、buf、len、lbuf);

スコア= 100-(dist * 100)/((len> lbuf)?len:lbuf);

//スコア= 100-(dist * 200)/(len + lbuf);

if(単語[0]!= buf [0])

スコア=(スコア* 3)>> 2;

//スコア= 100-(dist * 100)/((len> lbuf)?len:lbuf);

// wprintf(L "%s->%s(%d)\ n"、word、buf、score);

item =(ipair_t *)dk_alloc_box(sizeof(ipair_t)、DV_ARRAY_OF_LONG);

item-> id_ = wordid;

item-> len_ = lbuf;

item-> score_ =スコア;

dk_set_push(&pairs_list、item);

nids ++;

}

アサート(pptr);

}

}

if(pairs_list)

{

res =(ipair_t **)dk_set_to_array(pairs_list);

dk_set_free(pairs_list);

assert(nids == box_length(res)/ sizeof(void *));

qsort(res、nids、sizeof(void *)、cmp_pairs);

}

}

hash_table_free(ht_ids);

dk_free_box(単語);

}

解像度を返す;

}



caddr_t

bif_get_word_candidates(caddr_t * qst、caddr_t * err_ret、state_slot_t ** args)

{

char * me = "get_word_candidates";

ipair_t ** res = NULL;

dk_set_t ids_list = NULL;

caddr_t arr = NULL;

caddr_t arg = bif_arg_unrdf(qst、args、0、me);

dtp_t dtp = DV_TYPE_OF(arg);

if(DV_DB_NULL == dtp)

{

return(NULL);

}

if(!IS_WIDE_STRING_DTP(dtp))

{

sqlr_new_error( "22023"、 "SR007"、

「Function%sには引数としてnvstringまたはNULLが必要です、„

「タイプ%s(%d)の引数ではない」、

me、1、dv_type_title(dtp)、dtp);

}



if(0 == ht_dict _-> ht_count)

{

bif_reload_dict(qst、err_ret、args);

}



res = get_word_candidates((wchar_t *)arg);



ids_list = load_oid_list(res、(query_instance_t *)qst、NULL);

DO_SET(ipair_t *、アイテム、およびids_list)

{

// printf( "%d"、item-> id_);

dk_free_box(アイテム);

}

END_DO_SET();



return(caddr_t)res;

}



caddr_t

bif_calc_similarity(caddr_t * qst、caddr_t * err_ret、state_slot_t ** args)

{

char * me = "calc_similarity";

caddr_t arg1 = bif_arg_unrdf(qst、args、0、me);

caddr_t arg2 = bif_arg_unrdf(qst、args、1、me);

dtp_t dtp1 = DV_TYPE_OF(arg1);

dtp_t dtp2 = DV_TYPE_OF(arg2);

if(DV_DB_NULL == dtp1 || DV_DB_NULL == dtp2)

{

return(NULL);

}

if((!IS_WIDE_STRING_DTP(dtp1)))||((!IS_WIDE_STRING_DTP(dtp2)))

{

sqlr_new_error( "22023"、 "SR007"、

「Function%sには引数としてnvstringまたはNULLが必要です、„);

}

{

wchar_t * str1 =(wchar_t *)arg1;

wchar_t * str2 =(wchar_t *)arg2;

int l1 = wcslen(str1);

int l2 = wcslen(str2);

int dist = l_dist_raw(str1、str2、l1、l2);

intスコア= 100-(dist * 100)/((l1> l2)?l1:l2);

if(str1 [0]!= str2 [0])

スコア=(スコア* 3)>> 2;

戻り値;

}

}

static int g_cnt = 0;

#定義済みWIN32 &&定義済み(_DEBUG)

static _CrtMemState checkPt1;

#endif



long sqrt_long(長いr)

{

long t、b、c = 0;

assert(r> = 0);



for(b = 0x10000000; b!= 0; b >> = 2)

{

t = c + b;

c >> = 1;

if(t <= r)

{

rは= t;

c + = b;

}

}

return©;

}



caddr_t

bif_query_phrase(caddr_t * qst、caddr_t * err_ret、state_slot_t ** args)

{

char * me =“ query_phrase”;

wchar_t ** words = NULL;

ptrlong * res = NULL;

wchar_t * tmp = NULL;

dk_set_t ids_list = NULL;

caddr_t arr = NULL;

caddr_t arg = bif_arg_unrdf(qst、args、0、me);

dtp_t dtp = DV_TYPE_OF(arg);

int len = 0;

mem_pool_t * mp = mem_pool_alloc();



#if 0 //定義済みWIN32 &&定義済み(_DEBUG)

_CrtCheckMemory();

_CrtMemCheckpoint(&checkPt1);

#endif



// if(0 ==(g_cnt%1000))

// printf( "%d"、g_cnt);

++ g_cnt;

if(DV_DB_NULL == dtp)

{

return(NULL);

}

if(IS_STRING_DTP(dtp))

{

tmp = box_utf8_as_wide_char(arg、NULL、strlen(arg)、0、DV_WIDE);

words = nv_split(tmp);

dk_free_box(tmp);

}

else if(IS_WIDE_STRING_DTP(dtp))

{

tmp = wcsdup((const wchar_t *)arg);

words = nv_split(tmp);

無料(tmp);

}

他に

{

sqlr_new_error( "22023"、 "SR007"、

「Function%sには引数としてnvstringまたはNULLが必要です、„

「タイプ%s(%d)の引数ではない」、

me、1、dv_type_title(dtp)、dtp);

}



if(0 == ht_dict _-> ht_count)

{

bif_reload_dict(qst、err_ret、args);

}



// mutex_enter(dict_mtx_);



if(単語)

{

size_t niters = box_length(単語)/ sizeof(void *);

dk_set_t results = NULL;

dk_set_t * iter_holders = mp_alloc_box(mp、niters * sizeof(dk_set_t)、DV_ARRAY_OF_POINTER);

dk_set_t * iters = mp_alloc_box(mp、niters * sizeof(dk_set_t)、DV_ARRAY_OF_POINTER);

size_t i = 0;

size_t ix = 0;

size_t cnt = 0;

size_t cnt1 = 0;

for(i = 0; i <niters; i ++)

{

ipair_t ** res = get_word_candidates((wchar_t *)words [i]);

if(res)

{

iter_holders [ix] = load_oid_list(res、(query_instance_t *)qst、mp);

iters [ix] = iter_holders [ix];

ix ++;

dk_free_tree(res);

}

}

niters = ix;



if(niters)

{

int64 min_elem = 0;

int fin = 0;

(;!フィン;)

{

int bfound = 1;

size_t div = 1;

size_tスコア= 1;

size_t sumpos = 0;

size_t oldpos = 0;



if(!iters [0])

休憩;

min_elem =((ipair_t *)iters [0]-> data)-> id_;

for(i = 0; i <niters; i ++)

{

if(iters [i])

{

ipair_t * ptr =(ipair_t *)iters [i]->データ。

div * = 100;

スコア* = ptr-> score_;

if(i)

{

sumpos + = abs(oldpos-ptr-> pos_);

}

oldpos = ptr->pos_;

if (ptr->id_ != min_elem)

{

bfound = 0;

}

if (ptr->id_ < min_elem)

{

min_elem = ptr->id_;

}

}

}

if (bfound)

{

ipair_t *item = mp_alloc_box(mp, sizeof(ipair_t), DV_BIN);

div /= 100;

score /= div;

if (niters > 1)

sumpos /= (niters — 1);

item->id_ = min_elem;

item->score_ = score/(1 + (sqrt_long(((100*sumpos)/5))/10));

mp_set_push(mp, &results, item);

cnt1++;



//printf («FOUND:%I64d %d\n», min_elem, score);

}

for (i = 0; i <niters; i++)

{

int bf = bfound;

while (iters[i] && (bf || min_elem == ((ipair_t *)iters[i]->data)->id_))

{

bf = 0;

iters[i] = iters[i]->next;

}

if (!iters[i])

{

fin = 1;

休憩;

}

}

}

}



for (i = 0; i <niters; i++)

{

DO_SET (ipair_t *, item, &iter_holders[i])

{

cnt++;

//dk_free_box(item);

}

END_DO_SET ();

}

//dk_free_box (iters);

//dk_free_box (iter_holders);



//printf ("-%d-", cnt);

len = dk_set_length(results);

//if (len > 100)

{

results = list_sort (results, compare_by_score, NULL);

i = 0;

DO_SET (ipair_t *, entry, &results)

{

entry->len_ = (i >= 100)? 0:1;

i ++;

}

END_DO_SET();

len = (len>100)?100:len;

}

//results = list_sort (results, compare_by_id, NULL);

i = 0;

res = dk_alloc_box(len * 2 * sizeof(ptrlong), DV_ARRAY_OF_LONG);

DO_SET (ipair_t *, entry, &results)

{

if (entry->len_)

{

res[i++] = (entry->id_);

res[i++] = (entry->score_);

}

//dk_free_box(entry);

cnt1--;

}

END_DO_SET();

//dk_set_free (results);

dk_free_tree (words);

//printf("(%d)", cnt1);

}

//mutex_leave (dict_mtx_);

mp_free (mp);



#if 0//defined WIN32 && defined (_DEBUG)

// _CrtMemDumpAllObjectsSince( NULL );

_CrtMemDumpAllObjectsSince( &checkPt1 );

_CrtMemCheckpoint( &checkPt1 );

_CrtMemDumpStatistics( &checkPt1 );

_CrtCheckMemory( );

#endif

return (caddr_t)res;

}



ボイド

init_dict (void)

{

dict_mtx_ = mutex_allocate ();

ht_dict_ = id_hash_allocate (2039, sizeof (caddr_t), sizeof (caddr_t), strhash, strhashcmp);

ht_triples_ = id_hash_allocate (2039, sizeof (lenmem_t), sizeof (caddr_t), lenmemhash, lenmemhashcmp);

ht_dict_by_id_ = hash_table_allocate (2039);



bif_define («nv_split», bif_nv_split);

bif_define («treat_nword», bif_treat_nword);

bif_define («calc_similarity», bif_calc_similarity);

bif_define («reload_dict», bif_reload_dict);

bif_define («get_word_candidates», bif_get_word_candidates);

bif_define («query_phrase», bif_query_phrase);

}



void finit_dict()

{

flush_triples();

flush_dict();



hash_table_free (ht_dict_by_id_);

id_hash_free (ht_triples_);

id_hash_free (ht_dict_);

mutex_free (dict_mtx_);

}



extern int f_foreground;



int

main (int argc, char *argv[])

{

/*f_foreground = 1;

* FIXME: this could not be done in that way; this is a GPF on WIN32 and

* copy on write on linux; a fuinction from the shared object must be used

* to set it

* /

#ifdef MALLOC_DEBUG

dbg_malloc_enable();

#endif

build_set_special_server_model( "Mircalo");

VirtuosoServerSetInitHook(init_dict);

return VirtuosoServerMain(argc、argv);

}



PPS:ウラジミール・ルミャンツェフの作品は、ここで撮影されていますが、イラストとして使用されています。



All Articles