How Solr Join Works


QParserPlugin
Exaple query: http://localhost:8080/solr/select?q={!join from=classid to=classid}classname:"Grade 1"
QueryComponent.prepare call QParser.getParser which will 
QParser parser = QParser.getParser(rb.getQueryString(), defType, req);

QParser.getParser
default parser is lucene. It will parse local parameter to get the parser type, in this case it is join: {!join from=classid to=classid}. Put the query classname:"Grade 1" into localMap, key=QueryParsing.V.

if (qstr != null && qstr.startsWith(QueryParsing.LOCALPARAM_START)) {
{
 localParamsEnd = QueryParsing.parseLocalParams(qstr, 0, localMap, globalParams);
 //It first tries to get query string from local param
 String val = localMap.get(QueryParsing.V);
 if (val == null) {
  val = qstr.substring(localParamsEnd);
  localMap.put(QueryParsing.V, val);
 }
}
For the join query, this will get JoinQParserPlugin.
QParserPlugin qplug = req.getCore().getQueryPlugin(parserName);
QParser parser =  qplug.createParser(qstr, localParams, req.getParams(), req);

QueryComponent.prepare then calls the parser to get the query. 
 Query q = (QParser)parser.getQuery();, 
This will call the abstract parse method to pear the query string -the parse is only called once.
In this case, it is the anonymous parser class returned by JoinQParserPlugin.createParser.

In the anonymous parser class, 
if fromIndex parameter is not null, 
Then it creates subQuery from the query string:classname:"Grade 1". This will again determine/ create the parser for the query string via Parser.getParser, and get the query via LuceneQParser.parse.
QParser fromQueryParser = subQuery(v, null);
fromQuery = fromQueryParser.getQuery();

Then create one join query, and return it.
JoinQuery jq = new JoinQuery(fromField, toField, fromIndex, fromQuery);
jq.fromCoreOpenTime = fromCoreOpenTime;

Classes:
LuceneQParserPlugin
createParser creates LuceneQParser.
QParser
QParser is an abstract class, main methods include:
static getParser like a factory which creats concrete parser class based on local parameter. 
abstract parse method: create and return the Query object represented by qstr.
LuceneQParser extends QParser and implements parse method. In the parse method, LuceneQParser creates a QueryParser, then call QueryParser.parse to parse the qstr and return a Query.
SolrQueryParserBase
QueryParser extends SolrQueryParserBase
SolrQueryParser extends QueryParser
Query
JoinQuery, TermQuery,BooleanQuery.

QueryComponent.prepare then parse fq(filer query), save it to ResponseBuilder.
QParser fqp = QParser.getParser(fq, null, req);
filters.add(fqp.getQuery());
rb.setFilters( filters )

QueryComponent.process
this will create a QueryCommand: Query:JoinQuery.
SolrIndexSearcher.QueryCommand cmd = rb.getQueryCommand();
searcher.search(result,cmd);
filterList:[studentname:*], query:JoinQuery{!join from=classid to=classid}classname:Grade 1

Main task is done in SolrIndexSearcher.search
SolrIndexSearcher extends lucene.search.IndexSearcher.

SolrIndexSearcher.getDocListC(QueryResult, QueryCommand)
Query q = cmd.getQuery() --> JoinQuery
It will first try to get it from cache, the key is comprised of the Query object, sort, nflags. 
key = new QueryResultKey(q, cmd.getFilterList(), cmd.getSort(), flags);
superset = (SolrCache<QueryResultKey,DocList> queryResultCache)queryResultCache.get(key);
if (maxDocRequested < queryResultWindowSize) {
  supersetMaxDoc=queryResultWindowSize;
}
cmd.setSupersetMaxDoc(supersetMaxDoc);
At the first time, superset is null, then it will call getDocListNC(qr,cmd);

[getDocListNC]
ProcessedFilter pf = getProcessedFilter(cmd.getFilter(), cmd.getFilterList());
This will search the fq, and save the returned docset in pf.
cmd.getFilterList() is List<Query>: [studentname:*]
sets[end] = getPositiveDocSet(posQuery);
answer is DocSet, in this case BitDocSet which includs OpenBitSet.
answer = getDocSetNC(q,null);
if (filterCache != null) filterCache.put(q,answer);
pf.answer = answer;

getDocSetNC calls (lucene.search.IndexSearcher)super.search(query,null,collector);

[getDocListNC]
topCollector = TopScoreDocCollector.create(len, true);
Query is {!join from=classid to=classid}classname:Grade 1. luceneFilter is the the result which fq search returns
(lucene.search.IndexSearcher)super.search(query, luceneFilter, collector);

Here
(lucene.search.IndexSearcher)search(leafContexts, createNormalizedWeight(wrapFilter(query, filter)), results);

createNormalizedWeight will call Weight weight = query.createWeight(this); This will go to JoinQuery.createWeight.

[JoinQueryWeight]
Threadlocal:
SolrRequestInfo info = SolrRequestInfo.getRequestInfo();

[QueryComponent.process]
searcher.search(result,cmd);
rb.setResult( result );
doFieldSortValues(rb, searcher);
doPrefetch(rb);

Classes
lucene.search.IndexSearcher.search(List<AtomicReaderContext>, Weight, Collector)

Labels

adsense (5) Algorithm (69) Algorithm Series (35) Android (7) ANT (6) bat (8) Big Data (7) Blogger (14) Bugs (6) Cache (5) Chrome (19) Code Example (29) Code Quality (7) Coding Skills (5) Database (7) Debug (16) Design (5) Dev Tips (63) Eclipse (32) Git (5) Google (33) Guava (7) How to (9) Http Client (8) IDE (7) Interview (88) J2EE (13) J2SE (49) Java (186) JavaScript (27) JSON (7) Learning code (9) Lesson Learned (6) Linux (26) Lucene-Solr (112) Mac (10) Maven (8) Network (9) Nutch2 (18) Performance (9) PowerShell (11) Problem Solving (11) Programmer Skills (6) regex (5) Scala (6) Security (9) Soft Skills (38) Spring (22) System Design (11) Testing (7) Text Mining (14) Tips (17) Tools (24) Troubleshooting (29) UIMA (9) Web Development (19) Windows (21) xml (5)