对于任何query, 输入都会有一个bson类型的filter, MatchExpressionParser的作用就是把Bson对象转换为一个树形的MatchExpression对象。 本节详细介绍这个过程的实现。
MatchExpressionParser 只有一个public的函数:
static StatusWithMatchExpression parse(
const BSONObj& obj, const ExtensionsCallback& extensionsCallback = ExtensionsCallback()) {
// The 0 initializes the match expression tree depth.
return MatchExpressionParser(&extensionsCallback)._parse(obj, 0);
}
可以看出, MatchExpressionParser::_parse()是产生MatchExpression的入口。如下是简化版的_parse()函数, 这里以开头的操作符有好多种,我只保留了or, 其他的都类似:
StatusWithMatchExpression MatchExpressionParser::_parse(const BSONObj& obj, int level) {
std::unique_ptr<AndMatchExpression> root = stdx::make_unique<AndMatchExpression>();
bool topLevel = (level == 0);
level++;
BSONObjIterator i(obj);
while (i.more()) {
BSONElement e = i.next();
if (e.fieldName()[0] == '$') {
const char* rest = e.fieldName() + 1;
// TODO: optimize if block?
if (mongoutils::str::equals("or", rest)) {
if (e.type() != Array)
return {Status(ErrorCodes::BadValue, "$or needs an array")};
std::unique_ptr<OrMatchExpression> temp = stdx::make_unique<OrMatchExpression>();
Status s = _parseTreeList(e.Obj(), temp.get(), level);
if (!s.isOK())
return s;
root->add(temp.release());
}
...
continue;
}
if (_isExpressionDocument(e, false)) {
Status s = _parseSub(e.fieldName(), e.Obj(), root.get(), level);
if (!s.isOK())
return s;
continue;
}
if (e.type() == RegEx) {
StatusWithMatchExpression result = _parseRegexElement(e.fieldName(), e);
if (!result.isOK())
return result;
root->add(result.getValue().release());
continue;
}
std::unique_ptr<ComparisonMatchExpression> eq =
stdx::make_unique<EqualityMatchExpression>();
Status s = eq->init(e.fieldName(), e);
if (!s.isOK())
return s;
root->add(eq.release());
}
if (root->numChildren() == 1) {
std::unique_ptr<MatchExpression> real(root->getChild(0));
root->clearAndRelease();
return {std::move(real)};
}
return {std::move(root)};
}
在这里处理了4类的filter:
1. 操作符 or,and, not,nor, atomic,isolated, where.text, comment,id, $db;
2. sub-field, 其他的操作符;
3. regex 正则表达式;
4. 比较表达式
第一种情况
这里是一个递归调用, 首先根据当前节点的类型创建一个XXXMatchExpression节点, 这里是OrMatchExpression,
然后$or 有多个sub-node,通过 MatchExpressionParser::_parseTreeList():
Status MatchExpressionParser::_parseTreeList(const BSONObj& arr,
ListOfMatchExpression* out,
int level) {
if (arr.isEmpty())
return Status(ErrorCodes::BadValue, "$and/$or/$nor must be a nonempty array");
BSONObjIterator i(arr);
while (i.more()) {
BSONElement e = i.next();
if (e.type() != Object)
return Status(ErrorCodes::BadValue, "$or/$and/$nor entries need to be full objects");
StatusWithMatchExpression sub = _parse(e.Obj(), level);
if (!sub.isOK())
return sub.getStatus();
out->add(sub.getValue().release());
}
return Status::OK();
}
_parseTreeList 会调用 _parse 递归的调用每一个节点的子节点,并且为每个节点生成一个MatchExpression的子节点, 加入到当前
的节点的父节点的子节点里面。 这里, 函数的参数out, 类型是ListOfMatchExpression, 其里面有add函数可以将level层的所有节点
都放在同一个父节点下面;
第二种情况
如果, 我们的Node是一个expression,但是operator不是第一种情况的, 就是这里需要处理的。
通过_parseSub 调用_paseSubField来产生一个个MatchExpression的继承类的对象, 并将这一个个对象
添加到其父节点的子节点里面;
第三种情况
除了第一, 第二与操作符相关的处理之后, 如果是正则表达式,就产生一个RegexMatchExpression对象;
第四种情况
最后的一种filter, 就会产生一个ComparisonMatchExpression对象, 加入其父节点的子节点里面;
经过上述的4种情况的处理, 我们就会产生一个MatchExpression的树型结构, 为后续的查询处理做好准备。
这里, 我们看到有各种各样的操作符、正则表达式、一般的表达式, 在代码实现里面, 为每一种都定义了一个class, 所有的class
都继承自MatchExpression, 这样在所有的parse函数的返回值类型都是 MatchExpression*或者MatchExpression&, 通过C++的多态,
运行的时候绑定到具体的类型上面。
MatchExpression的继承关系如下图: