yaml::Mapping CyclopsMaster::reduce(const PendingRequest& results) { // for getPoint, we should get errors from all the dataset parts except the one that // actually contained it if (results.request["method"] == "getPoint") { for (int i=0; i<results.replies.size(); i++) { if (results.replies[i].contains("result")) { return results.replies[i]; } } } // for getPoints, we need to get all the datasets we got back (if any), and merge // them together before returning the result. If the total number of points is different // from the number of points we asked, we need to get which are the missing ones and return // an error message with that if (results.request["method"] == "getPoints") { // enclose all of this inside a try/catch block, so that if any error occurs (such as // invalid arguments) we just fall through to the normal reduce/sendReply which will // send the correct error message back to the client // FIXME: this is just a temporary hack, a correct solution would imply a more in-depth // refactoring of how arguments are checked try { if (results.request["params"].sequence().size() < 2) throw InvalidParams(); return reduceGetPoints(results.replies, results.request["params"][1].sequence()); } catch (...) {} /* DataSet resultds; for (int i=0; i<results.replies.size(); i++) { // all replies should contain result, even if only an empty dataset DataSet ds; ds.fromBase64(results.replies[i]["result"].scalar().toAscii()); resultds.appendDataSet(&ds); } if (resultds.size() == results.request["params"][1].sequence().size()) { yaml::Mapping result(results.replies[0]); result["result"] = resultds.toBase64(); return result; } // need to find the missing points QSet<QString> asked; foreach (const yaml::Node& n, results.request["params"][1].sequence()) asked << n.scalar(); QSet<QString> found = QSet<QString>::fromList(resultds.pointNames()); QStringList notfound = (asked - found).toList(); yaml::Mapping result; result.insert("id", results.request["id"]); result.insert("error", QString("Following points could not be found: ") + notfound.join(", ")); return result; */ } // first check there has been no errors, otherwise return that as a common answer for (int i=0; i<results.replies.size(); i++) { if (results.replies[i].contains("error")) { return results.replies[i]; } } // for size queries, we need to sum up the size of all parts if (results.request["method"] == "size") { int total = 0; for (int i=0; i<results.replies.size(); i++) { total += results.replies[i]["result"].scalar().toInt(); } yaml::Mapping result; result.insert("id", results.request["id"]); result.insert("result", QString::number(total)); return result; } // for chainedSearch queries, we need to merge the results now, only keeping the best ones if (results.request["method"] == "chainedSearch") { /* SearchResults allResults; for (int i=0; i<results.replies.size(); i++) { const yaml::Sequence& result = results.replies[i]["result"].sequence(); for (int j=0; j<result.size(); j++) { allResults << Result(result[j][0].scalar(), result[j][1].scalar().toFloat()); } } sort(allResults.begin(), allResults.end(), cmpSearchResults); */ CyclopsResults allResults; for (int i=0; i<results.replies.size(); i++) { const yaml::Sequence& result = results.replies[i]["result"]["results"].sequence(); const yaml::Sequence& values = results.replies[i]["result"]["values"].sequence(); for (int j=0; j<result.size(); j++) { QList<QVariant> vals; int vsize = values[j].sequence().size(); for (int k=0; k<vsize; k++) vals << values[j][k].scalar(); allResults.append(Result(result[j][0].scalar(), result[j][1].scalar().toFloat()), vals); } } allResults.sort(); int nresults = results.request["params"][2].scalar().toInt(); int offset = results.request["params"][3].scalar().toInt(); allResults.mid(offset, qMin(allResults.size()-offset, nresults)); yaml::Mapping result = results.replies[0]; result["result"] = toYaml(allResults); // add the header only now, to avoid a conversion yaml -> QVariant -> yaml result["result"]["header"] = results.replies[0]["result"]["header"]; return result; } // for nnSearch queries, we need to merge the results now, only keeping the best ones if (results.request["method"].scalar().startsWith("nnSearch")) { SearchResults allResults; for (int i=0; i<results.replies.size(); i++) { const yaml::Sequence& result = results.replies[i]["result"].sequence(); for (int j=0; j<result.size(); j++) { allResults << Result(result[j][0].scalar(), result[j][1].scalar().toFloat()); } } sort(allResults.begin(), allResults.end(), cmpSearchResults); int npos = 3; if (results.request["method"].scalar().endsWith("Filter")) npos = 4; int nresults = results.request["params"][npos].scalar().toInt(); allResults = allResults.mid(0, qMin(allResults.size(), nresults)); yaml::Mapping result = results.replies[0]; result["result"] = toYaml(allResults); return result; } // for queries that return None, we can return the first one // for datasetNames, distanceNames and layout, we assume all results are the same and return the first one return results.replies[0]; }
bool NavOneSearchHandler::handleOneSearch( NavRequestData& rd ) { if ( !checkExpectations( rd.params, rd.reply ) ) { return false; } // The user UserUser* user = rd.session->getUser()->getUser(); uint32 startTime = TimeUtility::getCurrentTime(); uint32 now = TimeUtility::getRealTime(); bool ok = true; // AURA set< uint32 >* allowedMaps = NULL; if ( !m_thread->getMapIdsForUserRegionAccess( user, allowedMaps, now, rd.urmask ) ) { mc2log << warn << "handleOneSearch: " << "getMapIdsForUserRegionAccess failed. "; if ( TimeUtility::getCurrentTime() - startTime > 3000 ) { rd.reply->setStatusCode( NavReplyPacket::NAV_STATUS_REQUEST_TIMEOUT ); mc2log << "Timeout"; } else { rd.reply->setStatusCode( NavReplyPacket::NAV_STATUS_NOT_OK ); mc2log << "Error"; } mc2log << endl; ok = false; } delete allowedMaps; // Start parameter printing mc2log << info << "handleOneSearch:"; CompactSearch params; params.m_oneResultList = true; params.m_itemInfoFilter = ItemInfoEnums::OneSearch_All; params.m_language = rd.clientLang; mc2log << " Lang " << StringTable::getString( StringTable::getLanguageAsStringCode( rd.stringtClientLang ), StringTable::ENGLISH ); if ( getParam( 6805, params.m_round, rd.params ) ) { mc2log << " Round " << params.m_round; } if ( getParam( 6804, params.m_maxHits, rd.params ) ) { mc2log << " MAX " << params.m_maxHits; } if ( getParam( 6800, params.m_what, rd.params ) ) { mc2log << " What " << params.m_what; } if ( NavParamHelper::getMC2CoordAndAngle( rd.params.getParam( 6802 ), params.m_location.m_coord, params.m_location.m_angle ) ) { mc2log << " Pos " << params.m_location.m_coord; } if ( getParam( 6803, params.m_distance, rd.params ) ) { mc2log << " Dist " << params.m_distance; } else if ( params.m_location.isValid() ) { // default 100km for distance params.m_distance = 100000; mc2log << " Dist " << params.m_distance; } if ( ! getParam( 6806, params.m_includeInfoItem, rd.params ) ) { params.m_includeInfoItem = true; } if ( params.m_includeInfoItem ) { mc2log << " include info fields "; } // Get the sorting type params.m_sorting = SearchTypes::DistanceSort; int32 sortType; if ( getParam( 6807, sortType, rd.params ) ) { mc2log << " Sort " << sortType; if( sortType == 1 ) { params.m_sorting = SearchTypes::AlphaSort; } } // Search category const NParam* p = rd.params.getParam( 6801 ); if ( p != NULL ) { uint32 length = p->getLength(); params.m_categoryIDs.resize( length ); for ( uint32 i = 0; i < length; ++i ) { params.m_categoryIDs.push_back( p->getInt32( i ) ); } } if ( rd.reqVer >= 2 ) { if ( getParam( 6808, params.m_where, rd.params ) ) { mc2log << " Where " << params.m_where; } if ( getParam( 6809, params.m_topRegionID, rd.params ) ) { mc2log << " TopRegionID " << params.m_topRegionID; } int32 searchType; if ( getParam( 6810, searchType, rd.params ) ) { if ( searchType == 0x1 && params.m_round == 0 ) { mc2log << " Address search "; params.m_heading = 1; // Address heading } } } // strip any leading and trailing white space from input params.cleanInput(); // Validate input fields if ( ! params.validInput() ) { rd.reply->setStatusCode( NavReplyPacket::NAV_STATUS_PARAMETER_INVALID ); rd.reply->setStatusMessage( "Invalid input" ); ok = false; } // End parameter printing mc2log << endl; // Set end index from max nbr hits and start index params.m_startIndex = 0; params.m_endIndex = params.m_maxHits - 1; // Make request typedef STLUtility::AutoContainerMap< SearchParserHandler::SearchResults > SearchResults; SearchResults results; if ( ok ) { results = m_thread->getSearchHandler().compactSearch( params ); for ( SearchResults::const_iterator it = results.begin() ; it != results.end() ; ++it ) { if ( (*it).second->getSearchResultRequest()->getStatus() != StringTable::OK ) { mc2log << "handleOneSearch: compactSearch Heading " << (*it).first << " failed: "; if ( (*it).second->getSearchResultRequest()->getStatus() == StringTable::TIMEOUT_ERROR ) { mc2log << " REQUEST_TIMEOUT"; rd.reply->setStatusCode( NavReplyPacket::NAV_STATUS_REQUEST_TIMEOUT ); } else if ( (*it).second->getSearchResultRequest()->getStatus() == StringTable::OUTSIDE_ALLOWED_AREA ) { mc2log << " OUTSIDE_ALLOWED_AREA"; rd.reply->setStatusCode( NavReplyPacket::NAV_STATUS_OUTSIDE_ALLOWED_AREA ); } else { mc2log << " NOT_OK"; rd.reply->setStatusCode( NavReplyPacket::NAV_STATUS_NOT_OK ); } if ( params.m_round == 1 && (*it).second->getSearchResultRequest()->getStatus() != StringTable::OUTSIDE_ALLOWED_AREA ) { mc2log << " BUT round 1 (ExternalSearch) so we ignore"; rd.reply->setStatusCode( NavReplyPacket::NAV_STATUS_OK ); } else { ok = false; } mc2log << endl; } } } // Put reply together if ( ok ) { // one search only returns one heading in the list... SearchResults::const_iterator it = results.begin(); if ( it != results.end() ) { const SearchResultRequest* match = (*it).second->getSearchResultRequest(); const vector<VanillaMatch*>& matches = match->getMatches(); uint32 numberMatches = MIN( params.m_maxHits, matches.size() ); rd.rparams.addParam( NParam( 6900, numberMatches ) ); rd.rparams.addParam( NParam( 6901, match->getTotalNbrMatches() ) ); if ( numberMatches > 0 ) { using namespace OneSearchUtils; BinarySearchResult binaryResult; serializeResults( matches.begin(), matches.begin() + numberMatches, m_thread->getSearchHandler(), &binaryResult ); // Add SearchStringTable addDataBuffer( 6902, rd.rparams, binaryResult.m_stringTable.get() ); // Add AreaTable addDataBuffer( 6903, rd.rparams, binaryResult.m_areaTable.get() ); // Add infoItemTable addDataBuffer( 6904, rd.rparams, binaryResult.m_infoItemTable.get() ); // Add Matches addDataBuffer( 6905, rd.rparams, binaryResult.m_matchesTable.get() ); } else { // No hits. Add empty arrays rd.params.addParam( NParam( 6902 ) ); rd.params.addParam( NParam( 6903 ) ); rd.params.addParam( NParam( 6904 ) ); rd.params.addParam( NParam( 6905 ) ); } } } return ok; }