MatchExpression(2):实现

本文介绍MongoDB中如何将BSON类型的过滤器转换为MatchExpression对象树的过程,详细解析MatchExpressionParser的功能及其核心函数_parse()的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于任何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的继承关系如下图:

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值