X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=proxy%2Ftmdatabase.cpp;fp=proxy%2Ftmdatabase.cpp;h=25e5a6c8e0a3c74caf4be1f4775d26ea55613ada;hb=3d0f5661d0cd3583f7d7b0b8158703b6cb3a4e4d;hp=d04737d043a0ebd0ed6f2cf3d9a3d0d566551b3b;hpb=071cc1ce9be277e9175381194150b7b5461bd586;p=wordring-tm%2Fwordring-tm.git diff --git a/proxy/tmdatabase.cpp b/proxy/tmdatabase.cpp index d04737d..25e5a6c 100644 --- a/proxy/tmdatabase.cpp +++ b/proxy/tmdatabase.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "debug.h" @@ -86,7 +87,7 @@ TM::SiteDatabase::SiteDatabase(Settings *settings) "site_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," "host_name TEXT UNIQUE);", Q_FUNC_INFO); - exec("CREATE INDEX IF NOT EXISTS domain_index ON sites(host_name);", Q_FUNC_INFO); + exec("CREATE INDEX IF NOT EXISTS host_name_index ON sites(host_name);", Q_FUNC_INFO); // クエリ作成。 m_find_site_id = prepare("SELECT site_id FROM sites WHERE host_name=?;", Q_FUNC_INFO); @@ -380,6 +381,154 @@ TM::SentenceDatabase::pointer TM::SentenceDatabase::create( return pointer(new SentenceDatabase(settings, site_id, name)); } + +// IndexDatabase -------------------------------------------------------------- + +TM::index_data_type::index_data_type() + : index_id(0) + , word_id(0) + , is_stop_word(false) +{ +} + +TM::index_data_type::pointer TM::index_data_type::create() +{ + return pointer(new index_data_type()); +} + +TM::IndexDatabase::IndexDatabase(Settings *settings, QString dbname) +{ + m_index_limit = 20 * 1024; + + m_database_name = + settings->value(TMDATABASE_ROOT_PATH_KEY).toString() + + "/" + dbname; + open(Q_FUNC_INFO); + + // テーブル作成。 + exec("CREATE TABLE IF NOT EXISTS indexes(" + "index_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," + "word_id INTEGER UNIQUE NOT NULL," + "sentence_ids BLOB," + "is_stop_word BOOLEAN);", Q_FUNC_INFO); + + exec("CREATE INDEX IF NOT EXISTS word_id_index ON indexes(word_id);", Q_FUNC_INFO); + + // クエリ作成。 + m_find_index = prepare( + "SELECT * FROM indexes WHERE word_id=?;", Q_FUNC_INFO); + m_insert_index = prepare( + "INSERT INTO indexes(word_id, sentence_ids, is_stop_word) " + "VALUES(?, ?, ?);", Q_FUNC_INFO); + m_update_index = prepare( + "UPDATE indexes SET " + "word_id=?," + "sentence_ids=?," + "is_stop_word=? " + "WHERE index_id=?;", Q_FUNC_INFO); +} + +TM::index_data_type::pointer TM::IndexDatabase::find_index(quint32 word_id) +{ + assert(word_id); + + m_find_index.bindValue(0, word_id); + exec(m_find_index, Q_FUNC_INFO); + + index_data_type::pointer result; + + if(m_find_index.next()) + { + result = index_data_type::create(); + result->index_id = m_find_index.value(0).toUInt(); + result->word_id = m_find_index.value(1).toUInt(); + QByteArray sentence_ids = m_find_index.value(2).toByteArray(); + QDataStream ds(sentence_ids); + while(!ds.atEnd()) + { + quint32 sentence_id; + ds >> sentence_id; + result->sentence_ids.insert(sentence_id); + } + result->is_stop_word = m_find_index.value(3).toBool(); + } + + return result; +} + +void TM::IndexDatabase::insert_index(WordDatabase::pointer word_database, + sentence_data_type::pointer source, quint32 target_id) +{ + QString const &string = source->sentence; + for(QPair const & range: source->words) + { + QString word = string.mid( + range.first, range.second - range.first + 1); + if(word.size() <= 2) continue; + + int word_id = 0; + word_id = word_database->word_id(word); + assert(word_id); + + index_data_type::pointer index_data = find_index(word_id); + if(index_data) + { + index_data->sentence_ids.insert(target_id); + // レコードが大きくなりすぎた場合、ストップワードとする。 + if(m_index_limit < index_data->sentence_ids.size()) + { + index_data->sentence_ids.clear(); + index_data->is_stop_word = true; + } + update_index(index_data); + } + else + { + index_data = index_data_type::create(); + index_data->word_id = word_id; + index_data->sentence_ids.insert(target_id); + index_data->is_stop_word = false; + insert_index(index_data); + } + } +} + +void TM::IndexDatabase::insert_index(index_data_type::pointer index_data) +{ + assert(index_data); + + QByteArray sentence_ids; + QDataStream ds(&sentence_ids, QIODevice::WriteOnly); + for(quint32 sentence_id : index_data->sentence_ids) ds << sentence_id; + + m_insert_index.bindValue(0, index_data->word_id); + m_insert_index.bindValue(1, sentence_ids); + m_insert_index.bindValue(2, index_data->is_stop_word); + + exec(m_insert_index, Q_FUNC_INFO); +} + +void TM::IndexDatabase::update_index(index_data_type::pointer index_data) +{ + assert(index_data); + + QByteArray sentence_ids; + QDataStream ds(&sentence_ids, QIODevice::WriteOnly); + for(quint32 sentence_id : index_data->sentence_ids) ds << sentence_id; + + m_update_index.bindValue(0, index_data->word_id); + m_update_index.bindValue(1, sentence_ids); + m_update_index.bindValue(2, index_data->is_stop_word); + m_update_index.bindValue(3, index_data->index_id); + + exec(m_update_index, Q_FUNC_INFO); +} + +TM::IndexDatabase::pointer TM::IndexDatabase::create(Settings *settings, QString dbname) +{ + return pointer(new IndexDatabase(settings, dbname)); +} + // Database ------------------------------------------------------------------- TM::Database::Database(Settings *settings, Service *service) @@ -406,6 +555,9 @@ void TM::Database::setup() m_sentence_cache_limit = m_settings->value( "Database/sentence-cache-limit", QVariant::fromValue(20)).toInt(); + m_index_cache_limit = m_settings->value( + "Database/index-cache-limit", QVariant::fromValue(20)).toInt(); + // データベース用のフォルダが無ければ作成する。 if(!m_settings->contains(TMDATABASE_ROOT_PATH_KEY)) qFatal("An error occured while find settings in Database()."); QString path = m_settings->value(TMDATABASE_ROOT_PATH_KEY).toString(); @@ -447,11 +599,23 @@ void TM::Database::find_sentence(quint32 site_id, int scode, int tcode, SentenceDatabase::pointer tdb = find_sentence_database(site_id, tcode); assert(tdb); + // 文脈込の検索を試みる。 quint32 source_id = sdb->find_sentence_id_with_context( source->sentence, source->previous_crc, source->next_crc); + if(source_id) + { + result = tdb->find_sentence_by_source_id(source_id); + if(result) result->quality = 101; + } + // 文脈込で発見できない場合、文脈無視で検索を試みる。 + if(!result) + { + source_id = sdb->find_sentence_id(source->sentence); + if(source_id) result = tdb->find_sentence_by_source_id(source_id); + if(result) result->quality = 100; + } - if(source_id) result = tdb->find_sentence_by_source_id(source_id); - + // 検索結果を返す。 QMetaObject::invokeMethod( m_service, "sentence_found", Qt::QueuedConnection, @@ -466,6 +630,7 @@ void TM::Database::insert_sentence(quint32 site_id, quint32 scode, sentence_data_type::pointer source, quint32 tcode, sentence_data_type::pointer target) { + // 入力の検証。 assert(site_id); assert(scode); assert(tcode); @@ -475,11 +640,13 @@ void TM::Database::insert_sentence(quint32 site_id, assert(target->previous_crc == 0); assert(target->next_crc == 0); + // 原文の更新。 SentenceDatabase::pointer sdb = find_sentence_database(site_id, scode); assert(sdb); - SentenceDatabase::pointer tdb = find_sentence_database(site_id, tcode); - assert(tdb); + // 原文IDの検索、無い場合登録。 + // 現在、前後の文脈が一致する場合のみ、IDを検索できたこととしている。 + // 文脈が一致しない場合、新たにIDを付与する。 quint32 source_id = source->sentence_id; if(!source_id) source_id = sdb->find_sentence_id_with_context( source->sentence, source->previous_crc, source->next_crc); @@ -492,17 +659,40 @@ void TM::Database::insert_sentence(quint32 site_id, } assert(source_id); + // 訳文の更新。 + SentenceDatabase::pointer tdb = find_sentence_database(site_id, tcode); + assert(tdb); + target->source_id = source_id; target->time = QDateTime::currentDateTime().toMSecsSinceEpoch(); + // 原文IDに対応する訳文を検索する。 + // 現在、原文IDに対して登録できる訳文は一つのみ。 sentence_data_type::pointer target_sentence = tdb->find_sentence_by_source_id(source_id); - if(target_sentence) + if(target_sentence) // 訳文があった場合、上書きして更新する。 { target->sentence_id = target_sentence->sentence_id; tdb->update(target); } - else tdb->insert(target); + else + { + tdb->insert(target); // 訳文が無かった場合、新規挿入する。 + // 挿入後、検索することで訳文IDを取得する。 + target_sentence = tdb->find_sentence_by_source_id(source_id); + } + assert(target_sentence); + quint32 target_id = target_sentence->sentence_id; + assert(target_id); // 挿入あるいは更新しているので、IDがある。 + + // 単語の登録。 + IndexDatabase::pointer index_database = + find_index_database(site_id, scode, tcode); + assert(index_database); + WordDatabase::pointer word_database = + find_word_database(scode); + index_database->insert_index(word_database, source, target_id); + assert(index_database); } QString TM::Database::find_language_name(int code) const @@ -527,6 +717,68 @@ int TM::Database::find_word_id(int code, QString word) return result; } +TM::WordDatabase::pointer TM::Database::find_word_database(int code) +{ + QMap::iterator it = m_word_databases.find(code); + assert(it != m_word_databases.end()); + return it.value(); +} + +/*! + * \brief 原文の単語IDと訳文の文IDとの照応を保持するデータベースを返します。 + * \param site_id サイトのID。 + * \param scode 原文の言語コード。 + * \param tcode 訳文の言語コード。 + * \return データベースへのポインタ。 + * + * このメンバは接続をキャッシュしています。 + */ +TM::IndexDatabase::pointer TM::Database::find_index_database( + quint32 site_id, int scode, int tcode) +{ + assert(site_id); + assert(scode); + assert(tcode); + + // キャッシュが大きすぎる場合、消去する。 + if(m_index_cache_limit < m_index_databases.size()) m_index_databases.clear(); + + // データベース名を作成する。 + QString dbname, sname, tname, site_name; + sname = find_language_name(scode).toLower(); + assert(!sname.isEmpty()); + if(!sname.isEmpty()) + { + tname = find_language_name(tcode).toLower(); + assert(!tname.isEmpty()); + } + if(!tname.isEmpty()) + { + site_name = QString::number(site_id); + assert(!site_name.isEmpty()); + } + if(!site_name.isEmpty()) + { + dbname = QString("index-"); + dbname += site_name + "-" + sname + "-" + tname + ".db"; + } + + IndexDatabase::pointer result; + + if(!dbname.isEmpty()) + { + QMap::iterator it = + m_index_databases.find(dbname); + if(it == m_index_databases.end()) + it = m_index_databases.insert(dbname, IndexDatabase::create(m_settings, dbname)); + assert(it != m_index_databases.end()); + + if(it != m_index_databases.end()) result = it.value(); + } + + return result; +} + /*! * \brief 引数として与えられたサイトと言語用のセンテンス・データベースを返します。 * \param site_id サイトを識別する番号。