mongodb源码分析(八)查询4之mongod文档的匹配

           前面用两篇文章讲解了游标的产生流程,下面我们将继续讲解文档的匹配过程.查询的流程很简单

就是取出document然后与条件比对,比对成功,在开启shard时还要继续查看当前document是否在在当

前的chunkManager中,最后将查看当前文档是否已经记录了,若已经记录则跳过,需要排序则这里将满足

要求的文档排序.

          当我使用mongodb时我对于查询有几个疑问1. mongodb对于{x:{y:{z:1}}这种深层次的嵌套是怎么

做的匹配工作.2 mongodb的elemMatch具体是怎么匹配的.本文将作出解答.先来看看mongodb官方文

档的三种形式的匹配处理吧.


        对于这种{author:{name:jane}}的匹配,mongodb将author当着匹配域,{name:jane}当着匹配的内容,匹配方式

为相等,意思是使用类似memcmp的方式,内容必须一致且排列方式也得一样,否则则视为不匹配.

        对于{author.name:"jane"}这种方式则将整个的author.name作为匹配域的名称,匹配的时候会递归查询直到

找到name对象为止,以name的值来匹配jane.

        


        这里只有$elemMatch会匹配想要的结果,因为elemMatch会建立子查询,在子查询中完成elem内部的匹配,最后

达到目的,下文将会讨论到.

下面来看代码吧.

document的匹配是通过CoveredIndexMatcher这个类来实现的,首先看看这个类对象的创建吧.

    shared_ptr<Cursor> CursorGenerator::singlePlanCursor() {
        shared_ptr<Cursor> single = singlePlan->newCursor();
        if ( !_query.isEmpty() && !single->matcher() ) {
            single->setMatcher( singlePlan->matcher() );//创建具体的类对象并设置.
        }
    }
回到queryWithQueryOptimizer函数来看看查询流程,去掉了其它和流程无关的代码.

    string queryWithQueryOptimizer() {        
        for( ; cursor->ok(); cursor->advance() ) {
            if ( pq.getMaxScan() && cursor->nscanned() > pq.getMaxScan() ) {//超出了请求的document数目则停止查询
                break;
            }//实际的比对过程
            if ( !queryResponseBuilder->addMatch() ) {
                continue;
            }
        }
    }
    bool QueryResponseBuilder::addMatch() {
        MatchDetails details;
        if ( _parsedQuery.getFields() && _parsedQuery.getFields()->getArrayOpType() == Projection::ARRAY_OP_POSITIONAL ) {
            // field projection specified, and contains an array operator
            details.requestElemMatchKey();
        }//我们关心的主角,匹配过程
        if ( !currentMatches( details ) ) {//for query match
            return false;
        }
        if ( !chunkMatches() ) {//chunkMatch,用于在开启了shard的服务器查询
            return false;
        }
        bool orderedMatch = false;
        bool match = _builder->handleMatch( orderedMatch, details );//for field projection
        return match;
    }
QueryResponseBuilder::addMatch->QueryResponseBuilder::currentMatches

    bool QueryResponseBuilder::currentMatches( MatchDetails& details ) {
        if ( _cursor->currentMatches( &details ) ) {//最后调用了cursor的匹配函数,这里上一篇文章分析到了cursor的类别其类别很多,这里挑选一个最复杂的QueryOptimizerCursorImpl作讲解,其它cursor的匹配大同小异,感兴趣的自己去看看吧
            return true;
        }
    }
		virtual bool QueryOptimizerCursorImpl::currentMatches( MatchDetails *details = 0 ) {
            if ( _takeover ) {
                return _takeover->currentMatches( details );
            }//调用了QueryOp的currentMatches函数做匹配,也就是转发了匹配要求而已.
            return _currOp->currentMatches( details );
        }
        bool QueryOptimizerCursorOp::currentMatches( MatchDetails *details ) {
            if ( !_c || !_c->ok() ) {//当前游标没有数据了,肯定不会再匹配什么,返回false
                _matchCounter.setMatch( false );
                return false;
            }//匹配过程再次被转发到了queryPlan的matcher中,这里的matcher就是singlePlanCursor那么讲解的生成的CoveredIndexMatcher对象
            bool match = queryPlan().matcher()->matchesCurrent( _c.get(), details );
            // Cache the match, so we can count it in mayAdvance().
            bool newMatch = _matchCounter.setMatch( match );//记录这次匹配结果.
            return match;
        }
在继续之前我们有必要分析一下CoveredIndexMatcher对象的构造.

    CoveredIndexMatcher::CoveredIndexMatcher( const BSONObj &jsobj,
                                             const BSONObj &indexKeyPattern ) :
        _docMatcher( new Matcher( jsobj ) ),//建立整个查询的匹配的docMatcher对象
        _keyMatcher( *_docMatcher, indexKeyPattern ) {//根据docMatcher和索引pattern只建立包含索引域的keyMatcher匹配对象
        init();//这里设置匹配过程中是否需要加载实际的数据做匹配,当查询中带有不是索引的域时就需要加载实际数据
    }
Matcher对象的构造:

    Matcher::Matcher(const BSONObj &jsobj, bool nested) :
        _where(0), _jsobj(jsobj), _haveSize(), _all(), _hasArray(0), _haveNeg(), _atomic(false) {
  
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值