Match for Vector Property¶
约 331 个字 76 行代码 预计阅读时间 3 分钟
Simplest match case¶
Process¶
- MatchValidator 验证阶段
在验证阶段,节点 (v) 被识别为:
- 没有指定标签的节点模式
- 没有属性过滤条件
- 别名为 v,类型为 AliasType::kNode
- MatchPathPlanner 路径规划阶段
StartVidFinder 寻找起始点
在 MatchPathPlanner::findStarts() 中
C++
// 遍历所有的 StartVidFinder
for (auto& finder : startVidFinders) {
for (size_t i = 0; i < nodeInfos.size() && !foundStart; ++i) {
NodeContext nodeCtx(qctx, bindWhereClause, spaceId, &nodeInfos[i]);
nodeCtx.aliasesAvailable = &nodeAliasesSeen;
auto nodeFinder = finder();
if (nodeFinder->match(&nodeCtx)) {
// 找到匹配的 finder,生成初始计划
}
}
}
对于 MATCH (v) 模式,会使用 ScanSeek:
ScanSeek::matchNode() 匹配
C++
bool ScanSeek::matchNode(NodeContext *nodeCtx) {
auto &node = *nodeCtx->info;
// 没有指定标签,获取所有标签
if (node.tids.empty()) {
// empty labels means all labels
auto allLabels = qctx->schemaMng()->getAllTags(nodeCtx->spaceId);
for (const auto &label : allLabels.value()) {
nodeCtx->scanInfo.schemaIds.emplace_back(label.first);
nodeCtx->scanInfo.schemaNames.emplace_back(label.second);
}
nodeCtx->scanInfo.anyLabel = true;
}
return true;
}
ScanSeek::transformNode() 生成 ScanVertices
C++
StatusOr<SubPlan> ScanSeek::transformNode(NodeContext *nodeCtx) {
SubPlan plan;
// 创建 ScanVertices 节点
auto *scanVertices = ScanVertices::make(qctx, nullptr, nodeCtx->spaceId, std::move(vProps));
plan.root = scanVertices;
plan.tail = scanVertices;
// 如果需要,添加标签过滤器
if (prev != nullptr) {
auto *filter = Filter::make(qctx, scanVertices, prev);
plan.root = filter;
}
nodeCtx->initialExpr = InputPropertyExpression::make(pool, kVid);
return plan;
}
- 路径扩展阶段
由于 MATCH (v) 只有一个节点,没有边,在 MatchPathPlanner::expandFromNode() 中:
C++
Status MatchPathPlanner::expandFromNode(size_t startIndex, SubPlan& subplan) {
const auto& nodeInfos = path_.nodeInfos;
DCHECK_LT(startIndex, nodeInfos.size());
// 只有一个节点的情况,startIndex = 0 且 nodeInfos.size() = 1
nodeAliasesSeenInPattern_.emplace(nodeInfos[startIndex].alias);
if (startIndex == 0) {
// Pattern: (start) - 没有后续边
return rightExpandFromNode(startIndex, subplan);
}
}
在 rightExpandFromNode() 中,由于没有边信息(edgeInfos.size() = 0),会直接跳过循环,执行最后的 AppendVertices:
C++
Status MatchPathPlanner::rightExpandFromNode(size_t startIndex, SubPlan& subplan) {
const auto& nodeInfos = path_.nodeInfos;
const auto& edgeInfos = path_.edgeInfos;
// edgeInfos.size() = 0,所以 for 循环不执行
auto& lastNode = nodeInfos.back(); // 即节点 v
// 创建 AppendVertices 节点
auto appendV = AppendVertices::make(qctx, subplan.root, spaceId);
auto vertexProps = SchemaUtil::getAllVertexProp(qctx, spaceId, true);
appendV->setVertexProps(std::move(vertexProps).value());
appendV->setSrc(nextTraverseStart); // 来自 ScanVertices 的输出
appendV->setVertexFilter(genVertexFilter(lastNode));
appendV->setDedup();
appendV->setTrackPrevPath(!edgeInfos.empty()); // false,因为没有边
appendV->setColNames(genAppendVColNames(subplan.root->colNames(), lastNode, !edgeInfos.empty()));
subplan.root = appendV;
return Status::OK();
}
- RETURN 和 LIMIT 处理
YieldClausePlanner 处理 RETURN v
处理 RETURN v 部分,生成 Project 节点来投影列 v。
PaginationPlanner 处理 LIMIT 3 5. 最终执行计划结构
生成 Limit -> AppendVertices -> ScanVertices 这样的三层执行计划。
- ScanVertices 只扫描顶点 ID 和标签信息,不包含顶点的完整属性
- AppendVertices 根据 ScanVertices 输出的顶点 ID,去获取完整的顶点属性数据
这种执行计划设计允许:
- 延迟加载:只有真正需要的顶点才会获取完整属性
- 内存效率:扫描阶段只处理 ID 和基本过滤,减少内存使用
- 可扩展性:支持复杂的图遍历模式
Vector Property Support¶
- AppendVertices 支持向量属性的处理
- 在 SchemaUtil::getAllVertexProp()中获取所有 tag 的 schema 信息,需要获取 vector property 的信息。