Skip to content
This repository was archived by the owner on Oct 10, 2025. It is now read-only.

Commit 73c56b7

Browse files
Update Kuzu from branch (#51)
Co-authored-by: mewim <14037726+mewim@users.noreply.github.com>
1 parent b0e6a77 commit 73c56b7

38 files changed

Lines changed: 440 additions & 283 deletions

Sources/cxx-kuzu/kuzu/extension/fts/src/function/create_fts_index.cpp

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -266,9 +266,10 @@ void LenCompute::vertexCompute(const graph::VertexScanState::Chunk& chunk) {
266266
static offset_t tableFunc(const TableFuncInput& input, TableFuncOutput&) {
267267
auto& bindData = *input.bindData->constPtrCast<CreateFTSBindData>();
268268
auto& context = *input.context;
269+
auto transaction = context.clientContext->getTransaction();
270+
auto catalog = context.clientContext->getCatalog();
269271
auto docTableName = FTSUtils::getDocsTableName(bindData.tableID, bindData.indexName);
270-
auto docTableEntry = context.clientContext->getCatalog()->getTableCatalogEntry(
271-
context.clientContext->getTransaction(), docTableName);
272+
auto docTableEntry = catalog->getTableCatalogEntry(transaction, docTableName);
272273
graph::NativeGraphEntry entry{{docTableEntry}, {} /* relTableEntries */};
273274
graph::OnDiskGraph graph(context.clientContext, std::move(entry));
274275
auto sharedState = LenComputeSharedState{};
@@ -283,14 +284,11 @@ static offset_t tableFunc(const TableFuncInput& input, TableFuncOutput&) {
283284
auto indexEntry = std::make_unique<catalog::IndexCatalogEntry>(FTSIndexCatalogEntry::TYPE_NAME,
284285
bindData.tableID, bindData.indexName, bindData.propertyIDs,
285286
std::make_unique<FTSIndexAuxInfo>(ftsConfig));
286-
auto catalog = context.clientContext->getCatalog();
287-
catalog->createIndex(context.clientContext->getTransaction(), std::move(indexEntry));
287+
catalog->createIndex(transaction, std::move(indexEntry));
288288

289-
auto nodeTable = context.clientContext->getStorageManager()
290-
->getTable(bindData.tableID)
291-
->ptrCast<storage::NodeTable>();
292-
auto tableEntry =
293-
catalog->getTableCatalogEntry(context.clientContext->getTransaction(), bindData.tableID);
289+
auto storageManager = context.clientContext->getStorageManager();
290+
auto nodeTable = storageManager->getTable(bindData.tableID)->ptrCast<storage::NodeTable>();
291+
auto tableEntry = catalog->getTableCatalogEntry(transaction, bindData.tableID);
294292
std::vector<column_id_t> columnIDs;
295293
std::vector<PhysicalTypeID> columnTypes;
296294
for (auto& propertyID : bindData.propertyIDs) {
@@ -302,11 +300,28 @@ static offset_t tableFunc(const TableFuncInput& input, TableFuncOutput&) {
302300
std::move(columnIDs), std::move(columnTypes),
303301
ftsIndexType.constraintType == storage::IndexConstraintType::PRIMARY,
304302
ftsIndexType.definitionType == storage::IndexDefinitionType::BUILTIN};
305-
auto storageInfo = std::make_unique<FTSStorageInfo>(numDocs, avgDocLen);
303+
auto storageInfo = std::make_unique<FTSStorageInfo>(numDocs, avgDocLen,
304+
nodeTable->getNumTotalRows(context.clientContext->getTransaction()));
306305
auto onDiskIndex = std::make_unique<FTSIndex>(std::move(indexInfo), std::move(storageInfo),
307306
std::move(ftsConfig), context.clientContext);
307+
if (!context.clientContext->isInMemory()) {
308+
// Checkpoint internal tables.
309+
onDiskIndex->getInternalTableInfo().docTable->checkpoint(context.clientContext,
310+
docTableEntry);
311+
auto termsTableName = FTSUtils::getTermsTableName(bindData.tableID, bindData.indexName);
312+
auto termsTableEntry =
313+
context.clientContext->getCatalog()->getTableCatalogEntry(transaction, termsTableName);
314+
onDiskIndex->getInternalTableInfo().termsTable->checkpoint(context.clientContext,
315+
termsTableEntry);
316+
auto appearsInTableName =
317+
FTSUtils::getAppearsInTableName(bindData.tableID, bindData.indexName);
318+
auto appearsInTableEntry = context.clientContext->getCatalog()->getTableCatalogEntry(
319+
transaction, appearsInTableName);
320+
onDiskIndex->getInternalTableInfo().appearsInfoTable->checkpoint(context.clientContext,
321+
appearsInTableEntry);
322+
}
308323
nodeTable->addIndex(std::move(onDiskIndex));
309-
context.clientContext->getTransaction()->setForceCheckpoint();
324+
transaction->setForceCheckpoint();
310325
return 0;
311326
}
312327

Sources/cxx-kuzu/kuzu/extension/fts/src/function/fts_config.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@ QueryFTSConfig::QueryFTSConfig(const function::optional_params_t& optionalParams
185185
} else if (Conjunctive::NAME == lowerCaseName) {
186186
value.validateType(Conjunctive::TYPE);
187187
isConjunctive = value.getValue<bool>();
188+
} else if (TopK::NAME == lowerCaseName) {
189+
value.validateType(TopK::TYPE);
190+
topK = value.getValue<uint64_t>();
188191
} else {
189192
throw common::BinderException{"Unrecognized optional parameter: " + name};
190193
}

Sources/cxx-kuzu/kuzu/extension/fts/src/function/query_fts_bind_data.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ QueryFTSOptionalParams::QueryFTSOptionalParams(const binder::expression_vector&
2727
b = optionalParam;
2828
} else if (paramName == Conjunctive::NAME) {
2929
conjunctive = optionalParam;
30+
} else if (paramName == TopK::NAME) {
31+
topK = optionalParam;
3032
} else {
3133
throw common::BinderException{"Unknown optional parameter: " + paramName};
3234
}
@@ -45,6 +47,9 @@ QueryFTSConfig QueryFTSOptionalParams::getConfig() const {
4547
config.isConjunctive =
4648
ExpressionUtil::evaluateLiteral<bool>(*conjunctive, LogicalType::BOOL());
4749
}
50+
if (topK != nullptr) {
51+
config.topK = ExpressionUtil::evaluateLiteral<uint64_t>(*topK, LogicalType::UINT64());
52+
}
4853
return config;
4954
}
5055

Sources/cxx-kuzu/kuzu/extension/fts/src/function/query_fts_index.cpp

Lines changed: 140 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
#include "function/query_fts_index.h"
22

3+
#include <queue>
4+
35
#include "binder/binder.h"
46
#include "binder/expression/expression_util.h"
57
#include "binder/expression/literal_expression.h"
8+
#include "binder/query/reading_clause/bound_table_function_call.h"
69
#include "catalog/fts_index_catalog_entry.h"
710
#include "common/exception/binder.h"
811
#include "common/types/internal_id_util.h"
912
#include "function/fts_index_utils.h"
1013
#include "function/gds/gds_utils.h"
1114
#include "function/query_fts_bind_data.h"
15+
#include "graph/on_disk_graph.h"
1216
#include "index/fts_index.h"
17+
#include "planner/operator/logical_hash_join.h"
18+
#include "planner/operator/logical_table_function_call.h"
19+
#include "planner/planner.h"
1320
#include "processor/execution_context.h"
1421
#include "storage/storage_manager.h"
1522
#include "storage/table/node_table.h"
@@ -22,6 +29,87 @@ using namespace storage;
2229
using namespace function;
2330
using namespace binder;
2431
using namespace common;
32+
using namespace planner;
33+
34+
struct DocScore {
35+
offset_t offset;
36+
double score;
37+
};
38+
39+
struct QFTSSharedState : public GDSFuncSharedState {
40+
common::table_id_t outputTableID;
41+
42+
QFTSSharedState(std::shared_ptr<processor::FactorizedTable> fTable,
43+
std::unique_ptr<graph::Graph> graph, common::table_id_t outputTableID)
44+
: GDSFuncSharedState{std::move(fTable), std::move(graph)}, outputTableID{outputTableID} {}
45+
46+
virtual void addDocScore(std::vector<ValueVector*> vectors,
47+
processor::FactorizedTable& localTable, DocScore docScore) {
48+
KU_ASSERT(vectors[0]->dataType.getLogicalTypeID() == LogicalTypeID::INTERNAL_ID);
49+
KU_ASSERT(vectors[1]->dataType.getLogicalTypeID() == LogicalTypeID::DOUBLE);
50+
vectors[0]->setValue(0, internalID_t{(common::offset_t)docScore.offset, outputTableID});
51+
vectors[1]->setValue(0, docScore.score);
52+
localTable.append(vectors);
53+
}
54+
55+
virtual void finalizeResult() { factorizedTablePool.mergeLocalTables(); }
56+
};
57+
58+
struct ScoreFTInsertState {
59+
ValueVector docsVector;
60+
ValueVector scoreVector;
61+
std::vector<ValueVector*> vectors;
62+
63+
ScoreFTInsertState();
64+
};
65+
66+
ScoreFTInsertState::ScoreFTInsertState()
67+
: docsVector{LogicalType::INTERNAL_ID()}, scoreVector{LogicalType::DOUBLE()} {
68+
auto state = DataChunkState::getSingleValueDataChunkState();
69+
docsVector.state = state;
70+
scoreVector.state = state;
71+
vectors.push_back(&docsVector);
72+
vectors.push_back(&scoreVector);
73+
}
74+
75+
// This sharedState is only used when the user sets the top parameter.
76+
struct QFTSTopKSharedState : public QFTSSharedState {
77+
struct PriorityQueueComp {
78+
bool operator()(const DocScore& left, const DocScore& right) {
79+
return left.score > right.score;
80+
}
81+
};
82+
std::priority_queue<DocScore, std::vector<DocScore>, PriorityQueueComp> minHeap;
83+
std::mutex mtx;
84+
uint64_t topK;
85+
86+
QFTSTopKSharedState(std::shared_ptr<processor::FactorizedTable> fTable,
87+
std::unique_ptr<graph::Graph> graph, uint64_t topK, common::table_id_t outputTableID)
88+
: QFTSSharedState{std::move(fTable), std::move(graph), outputTableID}, topK{topK} {}
89+
90+
void addDocScore(std::vector<ValueVector*> /*vectors*/,
91+
processor::FactorizedTable& /*localTable*/, DocScore docScore) override {
92+
std::lock_guard guard{mtx};
93+
if (minHeap.size() < topK) {
94+
minHeap.push(docScore);
95+
} else if (docScore.score > minHeap.top().score) {
96+
minHeap.pop();
97+
minHeap.push(docScore);
98+
}
99+
}
100+
101+
void finalizeResult() override {
102+
ScoreFTInsertState insertState;
103+
auto globalTable = factorizedTablePool.getGlobalTable();
104+
while (!minHeap.empty()) {
105+
auto& docScore = minHeap.top();
106+
insertState.docsVector.setValue(0, nodeID_t{(offset_t)docScore.offset, outputTableID});
107+
insertState.scoreVector.setValue(0, docScore.score);
108+
globalTable->append(insertState.vectors);
109+
minHeap.pop();
110+
}
111+
}
112+
};
25113

26114
struct ScoreData {
27115
uint64_t df;
@@ -69,43 +157,32 @@ struct QFTSEdgeCompute final : EdgeCompute {
69157
class QFTSOutputWriter {
70158
public:
71159
QFTSOutputWriter(const node_id_map_t<ScoreInfo>& scores, MemoryManager* mm,
72-
QueryFTSConfig config, const QueryFTSBindData& bindData, uint64_t numUniqueTerms);
160+
QueryFTSConfig config, const QueryFTSBindData& bindData, uint64_t numUniqueTerms,
161+
QFTSSharedState& sharedState);
73162

74163
void write(processor::FactorizedTable& scoreFT, nodeID_t docNodeID, uint64_t len,
75164
int64_t docsID);
76165

77166
std::unique_ptr<QFTSOutputWriter> copy() {
78-
return std::make_unique<QFTSOutputWriter>(scores, mm, config, bindData, numUniqueTerms);
167+
return std::make_unique<QFTSOutputWriter>(scores, mm, config, bindData, numUniqueTerms,
168+
sharedState);
79169
}
80170

81-
private:
82-
std::unique_ptr<ValueVector> createVector(const LogicalType& type);
83-
84171
private:
85172
const node_id_map_t<ScoreInfo>& scores;
86173
MemoryManager* mm;
87174
QueryFTSConfig config;
88175
const QueryFTSBindData& bindData;
89-
90-
std::unique_ptr<ValueVector> docsVector;
91-
std::unique_ptr<ValueVector> scoreVector;
92-
std::vector<ValueVector*> vectors;
93176
uint64_t numUniqueTerms;
177+
QFTSSharedState& sharedState;
178+
ScoreFTInsertState scoreFtInsertState;
94179
};
95180

96181
QFTSOutputWriter::QFTSOutputWriter(const node_id_map_t<ScoreInfo>& scores, MemoryManager* mm,
97-
QueryFTSConfig config, const QueryFTSBindData& bindData, uint64_t numUniqueTerms)
98-
: scores{scores}, mm{mm}, config{config}, bindData{bindData}, numUniqueTerms{numUniqueTerms} {
99-
docsVector = createVector(LogicalType::INTERNAL_ID());
100-
scoreVector = createVector(LogicalType::UINT64());
101-
}
102-
103-
std::unique_ptr<ValueVector> QFTSOutputWriter::createVector(const LogicalType& type) {
104-
auto vector = std::make_unique<ValueVector>(type.copy(), mm);
105-
vector->state = DataChunkState::getSingleValueDataChunkState();
106-
vectors.push_back(vector.get());
107-
return vector;
108-
}
182+
QueryFTSConfig config, const QueryFTSBindData& bindData, uint64_t numUniqueTerms,
183+
QFTSSharedState& sharedState)
184+
: scores{scores}, mm{mm}, config{config}, bindData{bindData}, numUniqueTerms{numUniqueTerms},
185+
sharedState{sharedState}, scoreFtInsertState{} {}
109186

110187
void QFTSOutputWriter::write(processor::FactorizedTable& scoreFT, nodeID_t docNodeID, uint64_t len,
111188
int64_t docsID) {
@@ -131,9 +208,7 @@ void QFTSOutputWriter::write(processor::FactorizedTable& scoreFT, nodeID_t docNo
131208
score += log10((numDocs - df + 0.5) / (df + 0.5) + 1) *
132209
((tf * (k + 1) / (tf + k * (1 - b + b * (len / avgDocLen)))));
133210
}
134-
docsVector->setValue(0, nodeID_t{static_cast<offset_t>(docsID), bindData.outputTableID});
135-
scoreVector->setValue(0, score);
136-
scoreFT.append(vectors);
211+
sharedState.addDocScore(scoreFtInsertState.vectors, scoreFT, {(uint64_t)docsID, score});
137212
}
138213

139214
class QFTSVertexCompute final : public VertexCompute {
@@ -233,7 +308,7 @@ static void initFrontier(FrontierPair& frontierPair, table_id_t termsTableID,
233308
static offset_t tableFunc(const TableFuncInput& input, TableFuncOutput&) {
234309
auto clientContext = input.context->clientContext;
235310
auto transaction = clientContext->getTransaction();
236-
auto sharedState = input.sharedState->ptrCast<GDSFuncSharedState>();
311+
auto sharedState = input.sharedState->ptrCast<QFTSSharedState>();
237312
auto graph = sharedState->graph.get();
238313
auto graphEntry = graph->getGraphEntry();
239314
auto qFTSBindData = input.bindData->constPtrCast<QueryFTSBindData>();
@@ -265,7 +340,7 @@ static offset_t tableFunc(const TableFuncInput& input, TableFuncOutput&) {
265340
auto mm = clientContext->getMemoryManager();
266341
auto numUniqueTerms = getNumUniqueTerms(terms);
267342
auto writer = std::make_unique<QFTSOutputWriter>(scores, mm, qFTSBindData->getConfig(),
268-
*qFTSBindData, numUniqueTerms);
343+
*qFTSBindData, numUniqueTerms, *sharedState);
269344
auto vc = std::make_unique<QFTSVertexCompute>(mm, sharedState, std::move(writer));
270345
auto vertexPropertiesToScan = std::vector<std::string>{DOC_LEN_PROP_NAME, DOC_ID_PROP_NAME};
271346
auto docsEntry = graphEntry->nodeInfos[1].entry;
@@ -282,7 +357,7 @@ static offset_t tableFunc(const TableFuncInput& input, TableFuncOutput&) {
282357
GDSUtils::runVertexCompute(input.context, GDSDensityState::DENSE, graph, *vc, docsEntry,
283358
vertexPropertiesToScan);
284359
}
285-
sharedState->factorizedTablePool.mergeLocalTables();
360+
sharedState->finalizeResult();
286361
return 0;
287362
}
288363

@@ -342,6 +417,42 @@ static std::unique_ptr<TableFuncBindData> bindFunc(main::ClientContext* context,
342417
return bindData;
343418
}
344419

420+
static void getLogicalPlan(Planner* planner, const BoundReadingClause& readingClause,
421+
const expression_vector& predicates, LogicalPlan& plan) {
422+
auto& call = readingClause.constCast<BoundTableFunctionCall>();
423+
auto bindData = call.getBindData()->constPtrCast<GDSBindData>();
424+
auto op = std::make_shared<LogicalTableFunctionCall>(call.getTableFunc(), bindData->copy());
425+
op->computeFactorizedSchema();
426+
planner->planReadOp(std::move(op), predicates, plan);
427+
428+
auto nodeOutput = bindData->nodeOutput->ptrCast<NodeExpression>();
429+
KU_ASSERT(nodeOutput != nullptr);
430+
planner->getCardinliatyEstimatorUnsafe().init(*nodeOutput);
431+
auto scanPlan = planner->getNodePropertyScanPlan(*nodeOutput);
432+
if (scanPlan.isEmpty()) {
433+
return;
434+
}
435+
expression_vector joinConditions;
436+
joinConditions.push_back(nodeOutput->getInternalID());
437+
planner->appendHashJoin(joinConditions, JoinType::INNER, scanPlan, plan, plan);
438+
plan.getLastOperator()->cast<LogicalHashJoin>().getSIPInfoUnsafe().direction =
439+
SIPDirection::FORCE_BUILD_TO_PROBE;
440+
}
441+
442+
std::shared_ptr<TableFuncSharedState> initSharedState(const TableFuncInitSharedStateInput& input) {
443+
auto bindData = input.bindData->constPtrCast<QueryFTSBindData>();
444+
auto graph = std::make_unique<graph::OnDiskGraph>(input.context->clientContext,
445+
bindData->graphEntry.copy());
446+
auto topK = bindData->getConfig().topK;
447+
if (bindData->getConfig().topK == INVALID_TOP_K) {
448+
return std::make_shared<QFTSSharedState>(bindData->getResultTable(), std::move(graph),
449+
bindData->outputTableID);
450+
} else {
451+
return std::make_shared<QFTSTopKSharedState>(bindData->getResultTable(), std::move(graph),
452+
topK, bindData->outputTableID);
453+
}
454+
}
455+
345456
function_set QueryFTSFunction::getFunctionSet() {
346457
function_set result;
347458
// inputs are tableName, indexName, query
@@ -350,10 +461,10 @@ function_set QueryFTSFunction::getFunctionSet() {
350461
LogicalTypeID::STRING});
351462
func->bindFunc = bindFunc;
352463
func->tableFunc = tableFunc;
353-
func->initSharedStateFunc = GDSFunction::initSharedState;
464+
func->initSharedStateFunc = initSharedState;
354465
func->initLocalStateFunc = TableFunction::initEmptyLocalState;
355466
func->canParallelFunc = [] { return false; };
356-
func->getLogicalPlanFunc = GDSFunction::getLogicalPlan;
467+
func->getLogicalPlanFunc = getLogicalPlan;
357468
func->getPhysicalPlanFunc = GDSFunction::getPhysicalPlan;
358469
result.push_back(std::move(func));
359470
return result;

Sources/cxx-kuzu/kuzu/extension/fts/src/include/function/fts_config.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,14 @@ struct Conjunctive {
9191
static constexpr bool DEFAULT_VALUE = false;
9292
};
9393

94+
struct TopK {
95+
static constexpr const char* NAME = "top";
96+
static constexpr common::LogicalTypeID TYPE = common::LogicalTypeID::UINT64;
97+
static constexpr uint64_t DEFAULT_VALUE = UINT64_MAX;
98+
};
99+
100+
constexpr uint64_t INVALID_TOP_K = UINT64_MAX;
101+
94102
struct QueryFTSConfig {
95103
// k: parameter controls the influence of term frequency saturation. It limits the effect of
96104
// additional occurrences of a term within a document.
@@ -99,6 +107,7 @@ struct QueryFTSConfig {
99107
// document length.
100108
double b = B::DEFAULT_VALUE;
101109
bool isConjunctive = Conjunctive::DEFAULT_VALUE;
110+
uint64_t topK = TopK::DEFAULT_VALUE;
102111

103112
QueryFTSConfig() = default;
104113
explicit QueryFTSConfig(const function::optional_params_t& optionalParams);

Sources/cxx-kuzu/kuzu/extension/fts/src/include/function/query_fts_bind_data.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ struct QueryFTSOptionalParams {
1212
std::shared_ptr<binder::Expression> k;
1313
std::shared_ptr<binder::Expression> b;
1414
std::shared_ptr<binder::Expression> conjunctive;
15+
std::shared_ptr<binder::Expression> topK;
1516

1617
explicit QueryFTSOptionalParams(const binder::expression_vector& optionalParams);
1718

0 commit comments

Comments
 (0)