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;
2229using namespace function ;
2330using namespace binder ;
2431using 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
26114struct ScoreData {
27115 uint64_t df;
@@ -69,43 +157,32 @@ struct QFTSEdgeCompute final : EdgeCompute {
69157class QFTSOutputWriter {
70158public:
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-
84171private:
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
96181QFTSOutputWriter::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
110187void 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
139214class QFTSVertexCompute final : public VertexCompute {
@@ -233,7 +308,7 @@ static void initFrontier(FrontierPair& frontierPair, table_id_t termsTableID,
233308static 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+
345456function_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;
0 commit comments