前面用两篇文章讲解了游标的产生流程,下面我们将继续讲解文档的匹配过程.查询的流程很简单
就是取出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) {