Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
112 commits
Select commit Hold shift + click to select a range
7b2ad52
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
70f08f6
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
11d78d0
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
184989a
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
34aeac5
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
4942b95
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
02c5bf2
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
4cf02ad
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
1a86623
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
4fcbde4
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
b4b0ea7
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
edc3bc7
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
230430d
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
cecb6f2
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
792f6e9
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
a3b9d99
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
77585be
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
c24d559
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
0269b6d
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
6a13db8
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
f2a5668
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
28fa166
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
9d495fd
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
cb04076
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
e277b5e
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
dae6abc
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
d171ad9
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
311259c
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
9b0d81a
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
28cd470
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
e95f535
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
b88a6fe
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
9e7a1e1
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
3100c76
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
0f73b3f
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
1c292ee
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
9091aac
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
a018da3
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
57c6e7f
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
f8e61b5
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
2483d3d
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
aa87820
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
0c788c2
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
eac96c4
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
86fb7b8
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
01d9a9b
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
e735e27
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
9e4a4a7
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
1c9a7be
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
020f1fe
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
54a3fa9
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
7752955
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
e4d0254
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
938d037
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
1a26ae8
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
ea3546b
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
36ba02a
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
07fe5a8
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
a8fa1d5
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
1d234d8
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
c1fb15f
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
1384874
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
f26ca77
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
b55aace
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
d467504
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
827d7f4
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
5624444
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
08ba585
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
8af99fe
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
7c6e0e4
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
3c20751
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
62f7e31
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
c621dec
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
87c84ff
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
0a76d63
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
cd0fcf5
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
4a552d6
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
ab33310
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
4b1a61d
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
71f01f5
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
5437cf5
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
15af2cc
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
6ce6173
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
4ea87df
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
eb990f2
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
63b54a6
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
311ba3c
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
3e3d642
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
65a5f2a
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
b455dbc
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
240c91d
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
c0590f9
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
1cc19e4
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
cd3aea1
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
4872f50
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
41a5c38
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
cf18848
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
3c0d78d
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
af3f95a
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
02a5c26
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
00c2b31
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
38255bb
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
7a77160
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
d912727
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
ee066e8
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
da235b9
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
ef8c247
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
159a5ec
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
52d0e68
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
587cab1
feat(fts): add MeCab Japanese tokenizer + tokenizer registry
chiangchenghsin-hash Aug 13, 2026
42af2b0
fix(fts): resolve mecabrc path at runtime (-r) and fix out-of-tree ad…
chiangchenghsin-hash Aug 13, 2026
25a358e
fix(fts): resolve mecabrc path at runtime (-r) and fix out-of-tree ad…
chiangchenghsin-hash Aug 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 39 additions & 3 deletions fts/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,33 @@
# Vendored dependencies live in the extensions repo root's third_party/.
# Use CMAKE_CURRENT_LIST_DIR (not PROJECT_SOURCE_DIR) so this also resolves
# when built under the engine's build system, where PROJECT_SOURCE_DIR points
# at the engine checkout.
set(FTS_EXTENSIONS_ROOT ${CMAKE_CURRENT_LIST_DIR}/../..)

include_directories(
${PROJECT_SOURCE_DIR}/src/include
${CMAKE_BINARY_DIR}/src/include
src/include
third_party/snowball/libstemmer
${PROJECT_SOURCE_DIR}/third_party/cppjieba/include
${PROJECT_SOURCE_DIR}/third_party/cppjieba/deps/limonp/include)
${FTS_EXTENSIONS_ROOT}/third_party/cppjieba/include
${FTS_EXTENSIONS_ROOT}/third_party/cppjieba/deps/limonp/include
${FTS_EXTENSIONS_ROOT}/third_party/mecab/src)

# cppjieba (jieba tokenizer dependency) is a header-only interface library;
# upstream referenced it but never vendored it, so define the target here.
if(NOT TARGET cppjieba)
add_library(cppjieba INTERFACE)
target_include_directories(cppjieba INTERFACE
${FTS_EXTENSIONS_ROOT}/third_party/cppjieba/include
${FTS_EXTENSIONS_ROOT}/third_party/cppjieba/deps/limonp/include)
endif()

# MeCab (Japanese tokenizer): static lib + mecab-dict-index tool + build-time
# ipadic dictionary compilation (downloads the dictionary CSV at configure).
# The source dir is outside this extension's tree, so an explicit binary dir
# is required (this also applies when built under the engine's build system).
add_subdirectory(${FTS_EXTENSIONS_ROOT}/third_party/mecab
${CMAKE_CURRENT_BINARY_DIR}/mecab)

add_subdirectory(src/catalog)
add_subdirectory(src/function)
Expand All @@ -15,16 +38,29 @@ add_subdirectory(src/utils)

add_subdirectory(third_party/snowball)

# tokenizer.cpp includes mecab.h, which requires DLL_EXPORT to declare the
# MeCab API without __declspec(dllimport) on Windows.
target_compile_definitions(lbug_fts_utils PRIVATE DLL_EXPORT)

build_extension_lib(${BUILD_STATIC_EXTENSION} "fts")

target_link_libraries(lbug_${EXTENSION_LIB_NAME}_extension
PRIVATE
snowball
re2
cppjieba)
cppjieba
mecab)

# Copy Jieba dictionaries next to the built extension so runtime can find a default path
add_custom_command(TARGET lbug_${EXTENSION_LIB_NAME}_extension POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${PROJECT_SOURCE_DIR}/third_party/cppjieba/dict
${PROJECT_SOURCE_DIR}/extension/fts/build/dict)

# Copy the compiled ipadic dictionary (UTF-8) next to the built extension so
# the default mecab_dict_dir resolves at runtime.
add_custom_command(TARGET lbug_${EXTENSION_LIB_NAME}_extension POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_BINARY_DIR}/mecab-dict/ipadic
${PROJECT_SOURCE_DIR}/extension/fts/build/dict-ipadic)
add_dependencies(lbug_${EXTENSION_LIB_NAME}_extension mecab-ipadic-dict)
27 changes: 23 additions & 4 deletions fts/src/function/create_fts_index.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,22 @@ std::string createFTSIndexQuery(ClientContext& context, const TableFuncBindData&
std::string query = "";
if (!catalog::Catalog::Get(context)->containsMacro(transaction::Transaction::Get(context),
FTSUtils::getTokenizeMacroName(tableID, indexName))) {
// TOKENIZE(text, tokenizer, extra_param)
// TOKENIZE(text, tokenizer, extra_param); the extra param is the
// dictionary directory for dictionary-based tokenizers. Pick it by
// tokenizer name (the params map holds defaults for all tokenizers).
auto& tokenizerParams = ftsBindData->createFTSConfig.tokenizerInfo.params;
auto& tokenizerName = ftsBindData->createFTSConfig.tokenizerInfo.tokenizer;
std::string extraParam = "";
if (tokenizerName == "jieba") {
extraParam = tokenizerParams.at("jieba_dict_dir");
} else if (tokenizerName == "mecab") {
extraParam = tokenizerParams.at("mecab_dict_dir");
}
query += std::format(R"(CREATE MACRO `{}`(query) AS
TOKENIZE(lower(regexp_replace(CAST(query as STRING), '{}', ' ', 'g')), '{}', '{}');)",
FTSUtils::getTokenizeMacroName(tableID, indexName),
formatStrInCypher(ftsBindData->createFTSConfig.ignorePattern),
ftsBindData->createFTSConfig.tokenizerInfo.tokenizer,
ftsBindData->createFTSConfig.tokenizerInfo.jiebaDictDir);
ftsBindData->createFTSConfig.tokenizerInfo.tokenizer, extraParam);
}

// Create the stop words table if not exists, or the user is not using the default english
Expand Down Expand Up @@ -236,7 +245,17 @@ std::string createFTSIndexQuery(ClientContext& context, const TableFuncBindData&
std::string params;
params += std::format("stemmer := '{}', ", ftsBindData->createFTSConfig.stemmer);
params +=
std::format("stopWords := '{}'", ftsBindData->createFTSConfig.stopWordsTableInfo.stopWords);
std::format("stopWords := '{}', ", ftsBindData->createFTSConfig.stopWordsTableInfo.stopWords);
// The internal index uses this config for incremental inserts/updates and
// queries, so the tokenizer (and its parameters) must be forwarded or it
// silently falls back to the default 'simple' tokenizer.
params += std::format("tokenizer := '{}'", ftsBindData->createFTSConfig.tokenizerInfo.tokenizer);
auto& ftsTokenizerParams = ftsBindData->createFTSConfig.tokenizerInfo.params;
if (ftsBindData->createFTSConfig.tokenizerInfo.tokenizer == "jieba") {
params += std::format(", jieba_dict_dir := '{}'", ftsTokenizerParams.at("jieba_dict_dir"));
} else if (ftsBindData->createFTSConfig.tokenizerInfo.tokenizer == "mecab") {
params += std::format(", mecab_dict_dir := '{}'", ftsTokenizerParams.at("mecab_dict_dir"));
}
query += std::format("CALL _CREATE_FTS_INDEX('{}', '{}', {}, {});", tableName, indexName,
properties, params);
query += std::format("RETURN 'Index {} has been created.' as result;", ftsBindData->indexName);
Expand Down
30 changes: 24 additions & 6 deletions fts/src/function/fts_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,11 @@ CreateFTSConfig::CreateFTSConfig(main::ClientContext& context, common::table_id_
Tokenizer::validate(tokenizerInfo.tokenizer);
} else if (lowerCaseName == "jieba_dict_dir") {
value.validateType(common::LogicalTypeID::STRING);
tokenizerInfo.jiebaDictDir =
common::StringUtils::getLower(value.getValue<std::string>());
// Note: the dict dir is a file path and must not be lower-cased.
tokenizerInfo.params["jieba_dict_dir"] = value.getValue<std::string>();
} else if (lowerCaseName == "mecab_dict_dir") {
value.validateType(common::LogicalTypeID::STRING);
tokenizerInfo.params["mecab_dict_dir"] = value.getValue<std::string>();
} else {
throw common::BinderException{"Unrecognized optional parameter: " + name};
}
Expand All @@ -166,17 +169,25 @@ CreateFTSConfig::CreateFTSConfig(main::ClientContext& context, common::table_id_

FTSConfig CreateFTSConfig::getFTSConfig() const {
return FTSConfig{stemmer, stopWordsTableInfo.tableName, stopWordsTableInfo.stopWords,
ignorePattern, ignorePatternQuery, tokenizerInfo.tokenizer, tokenizerInfo.jiebaDictDir};
ignorePattern, ignorePatternQuery, tokenizerInfo.tokenizer, tokenizerInfo.params};
}

// Magic marker written before the tokenizer params so that deserialization can
// tell apart new catalogs (marker + unordered map) from legacy ones (a single
// "jiebaDictDir" string field).
namespace {
constexpr const char* TOKENIZER_PARAMS_MAGIC = "lbug_tokenizer_params_v1";
} // namespace

void FTSConfig::serialize(common::Serializer& serializer) const {
serializer.serializeValue(stemmer);
serializer.serializeValue(stopWordsTableName);
serializer.serializeValue(stopWordsSource);
serializer.serializeValue(ignorePattern);
serializer.serializeValue(ignorePatternQuery);
serializer.serializeValue(tokenizer);
serializer.serializeValue(jiebaDictDir);
serializer.serializeValue(std::string{TOKENIZER_PARAMS_MAGIC});
serializer.serializeUnorderedMap(tokenizerParams);
}

FTSConfig FTSConfig::deserialize(common::Deserializer& deserializer) {
Expand All @@ -187,7 +198,14 @@ FTSConfig FTSConfig::deserialize(common::Deserializer& deserializer) {
deserializer.deserializeValue(config.ignorePattern);
deserializer.deserializeValue(config.ignorePatternQuery);
deserializer.deserializeValue(config.tokenizer);
deserializer.deserializeValue(config.jiebaDictDir);
std::string tokenizerParamsField;
deserializer.deserializeValue(tokenizerParamsField);
if (tokenizerParamsField == TOKENIZER_PARAMS_MAGIC) {
deserializer.deserializeUnorderedMap(config.tokenizerParams);
} else {
// Legacy catalog: the field after tokenizer was the jieba dict dir.
config.tokenizerParams["jieba_dict_dir"] = tokenizerParamsField;
}
return config;
}

Expand All @@ -214,7 +232,7 @@ void TopK::validate(uint64_t value) {
}

void Tokenizer::validate(const std::string& tokenizer) {
if (tokenizer == "simple" || tokenizer == "jieba") {
if (TokenizerRegistry::isSupported(tokenizer)) {
return;
}
throw common::BinderException{
Expand Down
77 changes: 31 additions & 46 deletions fts/src/function/tokenize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,28 @@
#include "common/types/string_t.h"
#include "common/types/types.h"
#include "common/vector/value_vector.h"
#include "cppjieba/Jieba.hpp"
#include "expression_evaluator/expression_evaluator_utils.h"
#include "function/scalar_function.h"
#include "re2.h"
#include "utils/tokenizer.h"

namespace lbug {
namespace fts_extension {

using namespace function;
using namespace common;

struct JiebaBindData final : public FunctionBindData {
std::shared_ptr<cppjieba::Jieba> jieba;
struct TokenizerBindData final : public FunctionBindData {
std::shared_ptr<const ITokenizer> tokenizer;

JiebaBindData(common::logical_type_vec_t paramTypes, std::shared_ptr<cppjieba::Jieba> jieba)
TokenizerBindData(common::logical_type_vec_t paramTypes,
std::shared_ptr<const ITokenizer> tokenizer)
: FunctionBindData{std::move(paramTypes),
common::LogicalType::LIST(common::LogicalType::STRING())},
jieba{std::move(jieba)} {}
tokenizer{std::move(tokenizer)} {}

std::unique_ptr<FunctionBindData> copy() const override {
return std::make_unique<JiebaBindData>(copyVector(paramTypes), jieba);
return std::make_unique<TokenizerBindData>(copyVector(paramTypes), tokenizer);
}
};

Expand All @@ -38,21 +39,11 @@ static void addTokensToVector(const std::vector<std::string>& tokens, list_entry
}
}

struct JiebaTokenizer {
struct TokenizeOp {
static void operation(string_t& text, string_t& /*tokenizerName*/, string_t& /*extraParam*/,
list_entry_t& result, common::ValueVector& resultVector, void* dataPtr) {
std::vector<std::string> tokens;
auto bindData = reinterpret_cast<JiebaBindData*>(dataPtr);
bindData->jieba->CutForSearch(text.getAsString(), tokens);
addTokensToVector(tokens, result, resultVector);
}
};

struct SimpleTokenizer {
static void operation(string_t& text, string_t& /*tokenizerName*/, string_t& /*extraParam*/,
list_entry_t& result, common::ValueVector& resultVector, void* /*dataPtr*/) {
auto tokens =
StringUtils::split(text.getAsString(), " ", true /* ignoreEmptyStringParts */);
auto bindData = reinterpret_cast<TokenizerBindData*>(dataPtr);
auto tokens = bindData->tokenizer->tokenize(text.getAsString());
addTokensToVector(tokens, result, resultVector);
}
};
Expand All @@ -62,37 +53,31 @@ static std::unique_ptr<FunctionBindData> bindFunc(const ScalarBindFuncInput& inp
throw BinderException{"The tokenizer parameter must be a literal expression."};
}
if (input.arguments[2]->expressionType != ExpressionType::LITERAL) {
throw BinderException{"The path to the jieba dict directory must be a literal expression."};
throw BinderException{"The tokenizer parameter must be a literal expression."};
}
auto value = evaluator::ExpressionEvaluatorUtils::evaluateConstantExpression(input.arguments[1],
input.context);
auto tokenizer = common::StringUtils::getLower(value.getValue<std::string>());
if (tokenizer == "jieba") {
std::string dictDir = evaluator::ExpressionEvaluatorUtils::evaluateConstantExpression(
input.arguments[2], input.context)
.getValue<std::string>();
std::string dict = dictDir + "/jieba.dict.utf8";
std::string hmm = dictDir + "/hmm_model.utf8";
std::string user = dictDir + "/user.dict.utf8"; // Contains custom AI/ML terms
std::string idf = dictDir + "/idf.utf8";
std::string stop = dictDir + "/stop_words.utf8";
auto jieba = std::make_unique<cppjieba::Jieba>(dict, hmm, user, idf, stop);
input.definition->ptrCast<ScalarFunction>()->execFunc =
ScalarFunction::TernaryRegexExecFunction<string_t, string_t, string_t, list_entry_t,
JiebaTokenizer>;
return std::make_unique<JiebaBindData>(
binder::ExpressionUtil::getDataTypes(input.arguments), std::move(jieba));
} else if (tokenizer == "simple" || tokenizer == "") {
input.definition->ptrCast<ScalarFunction>()->execFunc =
ScalarFunction::TernaryRegexExecFunction<string_t, string_t, string_t, list_entry_t,
SimpleTokenizer>;
return FunctionBindData::getSimpleBindData(input.arguments,
LogicalType::LIST(LogicalType::STRING()));
} else {
throw common::BinderException{
"Unsupported tokenizer: " + tokenizer +
".\nSupported tokenizers: 'simple' (default), 'jieba' (advanced Chinese)"};
auto tokenizerName = common::StringUtils::getLower(value.getValue<std::string>());
if (tokenizerName.empty()) {
tokenizerName = "simple";
}
// The third argument is the tokenizer's extra parameter; for 'jieba' it is
// the dictionary directory.
auto extraParam = evaluator::ExpressionEvaluatorUtils::evaluateConstantExpression(
input.arguments[2], input.context)
.getValue<std::string>();
TokenizerParams params;
if (tokenizerName == "jieba") {
params["jieba_dict_dir"] = extraParam;
} else if (tokenizerName == "mecab") {
params["mecab_dict_dir"] = extraParam;
}
auto tokenizer = TokenizerPool::getOrCreate(tokenizerName, params);
input.definition->ptrCast<ScalarFunction>()->execFunc =
ScalarFunction::TernaryRegexExecFunction<string_t, string_t, string_t, list_entry_t,
TokenizeOp>;
return std::make_unique<TokenizerBindData>(
binder::ExpressionUtil::getDataTypes(input.arguments), std::move(tokenizer));
}

function::function_set TokenizeFunction::getFunctionSet() {
Expand Down
21 changes: 15 additions & 6 deletions fts/src/include/function/fts_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "common/types/types.h"
#include "function/table/bind_input.h"
#include "utils/tokenizer.h"
#include <format>

namespace lbug {
Expand Down Expand Up @@ -65,9 +66,14 @@ struct Tokenizer {

struct TokenizerInfo {
std::string tokenizer = Tokenizer::DEFAULT_VALUE;
std::string jiebaDictDir = std::format("{}/extension/fts/build/dict", LBUG_ROOT_DIRECTORY);

TokenizerInfo() = default;
// Tokenizer-specific parameters (e.g. "jieba_dict_dir" for the jieba tokenizer).
TokenizerParams params;

TokenizerInfo() {
params["jieba_dict_dir"] = std::format("{}/extension/fts/build/dict", LBUG_ROOT_DIRECTORY);
params["mecab_dict_dir"] =
std::format("{}/extension/fts/build/dict-ipadic", LBUG_ROOT_DIRECTORY);
}
};

struct FTSConfig;
Expand Down Expand Up @@ -95,16 +101,19 @@ struct FTSConfig {
std::string ignorePattern = "";
std::string ignorePatternQuery = "";
std::string tokenizer = "";
std::string jiebaDictDir = "";
// Serialized as an unordered map after a magic marker; entries of legacy
// catalogs (which stored a single "jiebaDictDir" string here) are folded
// into tokenizerParams["jieba_dict_dir"] at deserialization time.
TokenizerParams tokenizerParams = {};

FTSConfig() = default;
FTSConfig(std::string stemmer, std::string stopWordsTableName, std::string stopWordsSource,
std::string ignorePattern, std::string ignorePatternQuery, std::string tokenizer,
std::string jiebaDictDir)
TokenizerParams tokenizerParams)
: stemmer{std::move(stemmer)}, stopWordsTableName{std::move(stopWordsTableName)},
stopWordsSource{std::move(stopWordsSource)}, ignorePattern{std::move(ignorePattern)},
ignorePatternQuery{std::move(ignorePatternQuery)}, tokenizer{std::move(tokenizer)},
jiebaDictDir{std::move(jiebaDictDir)} {}
tokenizerParams{std::move(tokenizerParams)} {}

void serialize(common::Serializer& serializer) const;

Expand Down
3 changes: 2 additions & 1 deletion fts/src/include/utils/fts_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ struct FTSUtils {
return std::format("{}_tokenize", getInternalTablePrefix(tableID, indexName));
}

static std::vector<std::string> tokenizeString(std::string& str, const FTSConfig& tokenizer);
static std::vector<std::string> tokenizeString(const std::string& str,
const FTSConfig& tokenizer);
};

} // namespace fts_extension
Expand Down
Loading
Loading