void ResultCollectorImpl::collect(const QueryResult& queryResults)
{
    if (queryResults.hasError())
    {
        m_result.setError(m_result.getErrorMsg() + queryResults.getErrorMsg());
    }
    else
    {
        m_result.setTotalHits(m_result.getTotalHits() + queryResults.getTotalHits());
        if (queryResults.getTimeCost() > m_result.getTimeCost())
        {
            m_result.setTimeCost(queryResults.getTimeCost());
        }

        shardid_t shardId = -1;
        QueryResult::Iterator it = queryResults.iterator();
        while (it.hasNext())
        {
            const ResultDocPtr& pDoc = it.next();
            m_pResultQueue->insert(pDoc);
            shardId = pDoc->getShardId();
        }
        
        const QueryTracerPtr& pTracer = queryResults.getTracer();
        if (pTracer.isNotNull())
        {
            if (m_result.getTracer().isNull())
            {
                m_result.setTracer(new QueryTracer("proxy", pTracer->getLevel()));
            }
            QueryTracerPtr& pResTracer = m_result.getTracer();
            string str;
            NumberFormatter::append(str, shardId);
            pResTracer->addChildTracer(pTracer)->prependPath(str);
        }
    }
}
void SearchShardsBase::collectPhaseTwoResult()
{
    FX_DEBUG("Collect phase two result");

    SEARCH_LATENCY_BEGIN();

    QueryResult phaseTwoResult;
    const QueryResult& qResult = m_pResultCollector->getResult();
    phaseTwoResult.setTracer(qResult.getTracer());

    int64_t nMaxTimeCost = 0;
    m_shardResults.clear();
    for (size_t i = 0; i < m_requestedShards.size(); ++i)
    {
        SearchShardBase* pShard = m_requestedShards[i];
        const string& sResult = pShard->getResponse();
        if (pShard->getStatus() != SearchReplicaBase::EC_OK)
        {
            stringstream ss;
            ss << "Has error on shard: [" << pShard->getName() 
               << "]: " << parseError(sResult);

            FX_LOG(ERROR, ss.str().c_str());
            setError(ss.str());
            SEARCH_LATENCY_END(m_nPhaseTwoCollectLatency);
            return;
        }

        FX_TRACE("Phase2 result: [%s]", sResult.c_str());

        XMLResultParser parser;
        QueryResultPtr& pTmpQRes = m_shardResults[pShard->getShardId()].second;
        parser.parse(sResult, *(pTmpQRes));
        if (pTmpQRes->getTimeCost() > nMaxTimeCost)
        {
            nMaxTimeCost = pTmpQRes->getTimeCost();
        }

        // merge trace information
        QueryTracerPtr& pTracer = pTmpQRes->getTracer();
        if (pTracer.isNotNull())
        {
            if (phaseTwoResult.getTracer().isNull())
            {
                phaseTwoResult.setTracer(new QueryTracer("proxy", pTracer->getLevel()));
            }
            string str;
            NumberFormatter::append(str, pShard->getShardId());
            phaseTwoResult.getTracer()->addChildTracer(pTracer)->prependPath(str);
        }
    }

    // Merge hit docs
    phaseTwoResult.setTotalHits(qResult.getTotalHits());
    phaseTwoResult.setTimeCost(nMaxTimeCost + qResult.getTimeCost());
    QueryResult::Iterator it = qResult.iterator();
    while (it.hasNext())
    {
        const ResultDocPtr& pDoc = it.next();
        shardid_t shardId = pDoc->getShardId();
        FIRTEX_ASSERT2((size_t)shardId <= m_shards.size());
        
        ResultPair& resPair = m_shardResults[shardId];
        QueryResultPtr& pQResult2 = resPair.second;
        if (pQResult2->hasError())
        {
            FX_LOG(ERROR, "Has error on shard: [%d], msg: [%s]",
                   pQResult2->getErrorMsg().c_str());
            phaseTwoResult.setError(pQResult2->getErrorMsg());
            break;
        }
        ResultDocPtr& pDoc2 = (*pQResult2)[resPair.first++];
        pDoc2->setScore(pDoc->getScore());
        phaseTwoResult.addDoc(pDoc2);
    }

    SEARCH_LATENCY_END(m_nPhaseTwoCollectLatency);

    // Add query trace information
    QueryTracerPtr& pResTracer = phaseTwoResult.getTracer();
    if (pResTracer.isNotNull())
    {
        FX_QUERY_TRACE(DEBUG, pResTracer, "Phase one search latency: [%d] ms",
                       m_nPhaseOneLatency);
        FX_QUERY_TRACE(DEBUG, pResTracer, "Phase one collection latency: [%d] ms",
                       m_nPhaseOneCollectLatency);
        FX_QUERY_TRACE(DEBUG, pResTracer, "Phase two search latency: [%d] ms",
                       m_nPhaseTwoLatency);
        FX_QUERY_TRACE(DEBUG, pResTracer, "Phase two collection latency: [%d] ms",
                       m_nPhaseTwoCollectLatency);
    }

    FX_DEBUG("Begin format result");
    stringstream ss;
    XMLResultFormatter formatter;
    formatter.format(phaseTwoResult, ss);
    m_sResult = ss.str();
    FX_DEBUG("End format result");
}
void HTTPSearchService::handleQuery(const Statement& state,
                                EvHttpRequestContext* pCtx) const
{
    IndexReaderPtr pIndexReader = m_searchRes.getIndexReader();
    FIRTEX_ASSERT2(pIndexReader.isNotNull());

    try
    {
        TimeProbe probe;
        probe.start();

        QueryParser parser(pIndexReader->getAnalyzerMapper(),
                           m_searchRes.getDefaultField());

        IndexSearcher searcher(pIndexReader);
        QueryHitsPtr pHits = searcher.search(state, parser);

        QueryResult result;

        if (pHits.isNotNull())
        {
            FieldSelectClausePtr pFieldClause = state.getFieldSelectClause();
            QueryClausePtr pQueryClause = state.getQueryClause();
            if (pFieldClause.isNotNull() && pQueryClause.isNotNull())
            {
                QueryPtr pQuery = parser.parse(pQueryClause->getQueryString());
                FIRTEX_ASSERT2(pQuery.isNotNull());

                FieldSelector selector(pIndexReader->getDocSchema());
                
                for (size_t i = 0; i < pFieldClause->getFieldCount(); ++i)
                {
                    const FieldSelectClause::SnippetParam& param = pFieldClause->getField(i);
                    FieldFilterPtr pFieldFilter;
                    if (param.snippet)
                    {
                        SnippetGenerator* pSnippetGen = new SnippetGenerator();
                        pFieldFilter.reset(pSnippetGen);
                        
                        if (!pSnippetGen->init(pQuery, parser.getAnalyzerMapper(), param.field,
                                        param.preTag, param.postTag, param.separator))
                        {
                            FX_LOG(ERROR, "Init snippet generator for field: [%s] FAILED", param.field.c_str());
                            sendErrorMessage("Init snippet generator for field: " + 
                                    param.field + " FAILED", pCtx);
                            return;
                        }                        
                    }

                    if (!selector.addField(param.field, pFieldFilter))
                    {
                        FX_LOG(ERROR, "Invalid field: [%s]", param.field.c_str());
                    }
                }
                result.init(selector, pIndexReader, *pHits);
            }
            else
            {
                result.init(pIndexReader, *pHits);
            }
        }

        probe.stop();
        result.setTimeCost(probe.elapsed() / 1000);
        FX_QUERY_TRACE(INFO, result.getTracer(), "search phase time [%d]",
                       (int32_t)result.getTimeCost());

        stringstream ss;
        XMLResultFormatter formatter;
        formatter.format(result, ss);
        sendResponse(ss.str(), pCtx);
    }
    catch(const FirteXException& e)
    {
        FX_LOG(ERROR, "Handle request FAILED: [%s], reason: [%s]",
               pCtx->getQuery().c_str(), e.what().c_str());
        sendErrorMessage("Handle request failed", pCtx);
    }
}
void XMLResultFormatter::format(const QueryResult& result,
                                std::stringstream& ss)
{
    m_xmlDoc.clear();

    m_xmlDoc.addDeclarationNode(result.getEncoding());
    XMLNodeWrapperPtr pRootNode = m_xmlDoc.appendNode(
            XMLDocumentWrapper::NODE_ELEMENT, "result");
    if (result.hasError())
    {
        formatError(pRootNode, result.getErrorMsg());
    }
    else 
    {
        XMLNodeWrapperPtr resultNode = pRootNode->appendNode(
                XMLDocumentWrapper::NODE_ELEMENT, "hits");
    
        string str;
        NumberFormatter::append(str, (int32_t)result.size());
        resultNode->appendAttribute("number_hits", str);

        str.clear();
        NumberFormatter::append(str, result.getTotalHits());
        resultNode->appendAttribute("total_hits", str);

        str.clear();
        NumberFormatter::append(str, result.getTimeCost());
        resultNode->appendAttribute("cost", str);

        QueryResult::Iterator it = result.iterator();
        while (it.hasNext())
        {
            XMLNodeWrapperPtr pHitNode = resultNode->appendNode(
                    XMLDocumentWrapper::NODE_ELEMENT, "hit");

            const ResultDocPtr& pResDoc = it.next();

            if (result.hasShardId())
            {
                str.clear();
                NumberFormatter::append(str, pResDoc->getShardId());
                pHitNode->appendNode(XMLDocumentWrapper::NODE_ELEMENT,
                        "shardid", str);
            }

            if (result.hasDocId())
            {
                str.clear();
                NumberFormatter::append(str, pResDoc->getDocId());
                pHitNode->appendNode(XMLDocumentWrapper::NODE_ELEMENT,
                        "docid", str);
            }

            if (result.hasScore())
            {
                str.clear();
                NumberFormatter::append(str, pResDoc->getScore(), 2);
                pHitNode->appendNode(XMLDocumentWrapper::NODE_ELEMENT,
                        "score", str);
            }

            if (result.hasFields())
            {
                XMLNodeWrapperPtr pFieldsNode = pHitNode->appendNode(
                        XMLDocumentWrapper::NODE_ELEMENT, "fields");

                ResultDoc::Iterator fieldIt = pResDoc->iterator();
                while (fieldIt.hasNext())
                {
                    const ResultDoc::Field& field = fieldIt.next();

                    XMLNodeWrapperPtr pCDataNode = pFieldsNode->appendNode(
                            XMLDocumentWrapper::NODE_ELEMENT, field.first);
                    pCDataNode->appendNode(XMLDocumentWrapper::NODE_CDATA,
                            "", field.second);
                }
            }
        } // end while 

        const QueryTracerPtr& pTracer = result.getTracer();
        if (pTracer)
        {
            formatTracer(pRootNode, pTracer);
        }
    }
    m_xmlDoc.print(ss);
}