Beispiel #1
0
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;
}