From d740ffe465b25550de74695e6584d9c89f5a0466 Mon Sep 17 00:00:00 2001 From: Matthieu Gautier Date: Wed, 9 Mar 2022 14:45:22 +0100 Subject: [PATCH] Introduce SearchInfo. SearchInfo is a small helper structure to store information about the queried search. It regroup already existing information (`patternString`, geo query, ...) in one structure. It is also used as key in the cache instead of using a generated string. --- src/server/internalServer.cpp | 192 ++++++++++++++++++---------------- src/server/internalServer.h | 48 ++++++++- 2 files changed, 146 insertions(+), 94 deletions(-) diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index 6d2ac4a39..54f0c311a 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -109,6 +109,55 @@ unsigned int getCacheLength(const char* name, unsigned int defaultVal) { } } // unnamed namespace +SearchInfo::SearchInfo(const std::string& pattern) + : pattern(pattern), + geoQuery() +{} + +SearchInfo::SearchInfo(const std::string& pattern, GeoQuery geoQuery) + : pattern(pattern), + geoQuery(geoQuery) +{} + +SearchInfo::SearchInfo(const RequestContext& request) + : pattern(request.get_optional_param("pattern", "")), + geoQuery(), + bookName(request.get_optional_param("content", "")) +{ + /* Retrive geo search */ + try { + auto latitude = request.get_argument("latitude"); + auto longitude = request.get_argument("longitude"); + auto distance = request.get_argument("distance"); + geoQuery = GeoQuery(latitude, longitude, distance); + } catch(const std::out_of_range&) {} + catch(const std::invalid_argument&) {} + + if (!geoQuery && pattern.empty()) { + throw std::invalid_argument("No query provided."); + } +} + +zim::Query SearchInfo::getZimQuery(bool verbose) const { + zim::Query query; + if(pattern.empty()) { + // Execute geo-search + if (verbose) { + cout << "Performing geo query `" << geoQuery.distance << "&(" << geoQuery.latitude << ";" << geQuery.longitude << ")'" << endl; + } + query.setQuery(""); + query.setGeorange(geoQuery.latitude, geoQuery.longitude, geoQuery.distance); + } else { + // Execute Ft search + if (verbose) { + cout << "Performing query `" << pattern << "'" << endl; + } + query.setQuery(pattern); + } + return query; +} + + static IdNameMapper defaultNameMapper; static MHD_Result staticHandlerCallback(void* cls, @@ -499,116 +548,75 @@ std::unique_ptr InternalServer::handle_search(const RequestContext& re printf("** running handle_search\n"); } - std::string patternString; try { - patternString = request.get_argument("pattern"); - } catch (const std::out_of_range&) {} + auto searchInfo = SearchInfo(request); - /* Retrive geo search */ - bool has_geo_query = false; - float latitude = 0; - float longitude = 0; - float distance = 0; - try { - latitude = request.get_argument("latitude"); - longitude = request.get_argument("longitude"); - distance = request.get_argument("distance"); - has_geo_query = true; - } catch(const std::out_of_range&) {} - catch(const std::invalid_argument&) {} + std::string bookId; + std::shared_ptr archive; + if (!searchInfo.bookName.empty()) { + try { + bookId = mp_nameMapper->getIdForName(searchInfo.bookName); + archive = mp_library->getArchiveById(bookId); + } catch (const std::out_of_range&) { + auto data = get_default_data(); + data.set("pattern", encodeDiples(searchInfo.pattern)); + data.set("root", m_root); + auto response = ContentResponse::build(*this, RESOURCE::templates::no_search_result_html, data, "text/html; charset=utf-8"); + response->set_code(MHD_HTTP_NOT_FOUND); + return withTaskbarInfo(searchInfo.bookName, archive.get(), std::move(response)); + } + } - std::string bookName, bookId; - std::shared_ptr archive; - try { - bookName = request.get_argument("content"); - bookId = mp_nameMapper->getIdForName(bookName); - archive = mp_library->getArchiveById(bookId); - } catch (const std::out_of_range&) {} - - /* Make the search */ - if (patternString.empty() && ! has_geo_query) { - return HTTP400HtmlResponse(*this, request) - + invalidUrlMsg - + std::string("No query provided."); - } - - if (!archive && !bookName.empty()) { - auto data = get_default_data(); - data.set("pattern", encodeDiples(patternString)); - data.set("root", m_root); - auto response = ContentResponse::build(*this, RESOURCE::templates::no_search_result_html, data, "text/html; charset=utf-8"); - response->set_code(MHD_HTTP_NOT_FOUND); - return withTaskbarInfo(bookName, archive.get(), std::move(response)); - } - - std::shared_ptr searcher; - if (archive) { - searcher = searcherCache.getOrPut(bookId, [=](){ return std::make_shared(*archive);}); - } else { - for (auto& bookId: mp_library->filter(kiwix::Filter().local(true).valid(true))) { - auto currentArchive = mp_library->getArchiveById(bookId); - if (currentArchive) { - if (! searcher) { - searcher = std::make_shared(*currentArchive); - } else { - searcher->addArchive(*currentArchive); + /* Make the search */ + std::shared_ptr searcher; + if (archive) { + searcher = searcherCache.getOrPut(bookId, [=](){ return std::make_shared(*archive);}); + } else { + for (auto& bookId: mp_library->filter(kiwix::Filter().local(true).valid(true))) { + auto currentArchive = mp_library->getArchiveById(bookId); + if (currentArchive) { + if (! searcher) { + searcher = std::make_shared(*currentArchive); + } else { + searcher->addArchive(*currentArchive); + } } } } - } - auto start = 0; - try { - start = request.get_argument("start"); - } catch (const std::exception&) {} + auto start = 0; + try { + start = request.get_argument("start"); + } catch (const std::exception&) {} - auto pageLength = 25; - try { - pageLength = request.get_argument("pageLength"); - } catch (const std::exception&) {} - if (pageLength > MAX_SEARCH_LEN) { - pageLength = MAX_SEARCH_LEN; - } - if (pageLength == 0) { - pageLength = 25; - } - - /* Get the results */ - std::string queryString; - try { - zim::Query query; - if (patternString.empty()) { - // Execute geo-search - if (m_verbose.load()) { - cout << "Performing geo query `" << distance << "&(" << latitude << ";" << longitude << ")'" << endl; - } - - query.setQuery(""); - queryString = "GEO:" + to_string(latitude) + to_string(longitude) + to_string(distance); - query.setGeorange(latitude, longitude, distance); - } else { - // Execute Ft search - if (m_verbose.load()) { - cout << "Performing query `" << patternString << "'" << endl; - } - - queryString = "FT:" + removeAccents(patternString); - query.setQuery(queryString); + auto pageLength = 25; + try { + pageLength = request.get_argument("pageLength"); + } catch (const std::exception&) {} + if (pageLength > MAX_SEARCH_LEN) { + pageLength = MAX_SEARCH_LEN; + } + if (pageLength == 0) { + pageLength = 25; } - queryString = bookId + queryString; + /* Get the results */ std::shared_ptr search; - search = searchCache.getOrPut(queryString, [=](){ return make_shared(searcher->search(query));}); + search = searchCache.getOrPut(searchInfo, [=](){ return make_shared(searcher->search(searchInfo.getZimQuery(m_verbose.load())));}); SearchRenderer renderer(search->getResults(start, pageLength), mp_nameMapper, mp_library, start, search->getEstimatedMatches()); - renderer.setSearchPattern(patternString); - renderer.setSearchContent(bookName); + renderer.setSearchPattern(searchInfo.pattern); + renderer.setSearchContent(searchInfo.bookName); renderer.setProtocolPrefix(m_root + "/"); renderer.setSearchProtocolPrefix(m_root + "/search?"); renderer.setPageLength(pageLength); auto response = ContentResponse::build(*this, renderer.getHtml(), "text/html; charset=utf-8"); - return withTaskbarInfo(bookName, archive.get(), std::move(response)); + return withTaskbarInfo(searchInfo.bookName, archive.get(), std::move(response)); + } catch (const std::invalid_argument& e) { + return HTTP400HtmlResponse(*this, request) + + invalidUrlMsg + + std::string(e.what()); } catch (const std::exception& e) { std::cerr << e.what() << std::endl; return Response::build_500(*this, e.what()); diff --git a/src/server/internalServer.h b/src/server/internalServer.h index 6ccde9f8f..eb8dcc5cf 100644 --- a/src/server/internalServer.h +++ b/src/server/internalServer.h @@ -43,9 +43,53 @@ extern "C" { namespace kiwix { +struct GeoQuery { + GeoQuery() + : GeoQuery(0, 0, -1) + {} + + GeoQuery(float latitude, float longitude, float distance) + : latitude(latitude), longitude(longitude), distance(distance) + {} + float latitude; + float longitude; + float distance; + + explicit operator bool() const { + return distance >= 0; + } + + friend bool operator<(const GeoQuery& l, const GeoQuery& r) + { + return std::tie(l.latitude, l.longitude, l.distance) + < std::tie(r.latitude, r.longitude, r.distance); // keep the same order + } +}; + +class SearchInfo { + public: + SearchInfo(const std::string& pattern); + SearchInfo(const std::string& pattern, GeoQuery geoQuery); + SearchInfo(const RequestContext& request); + + zim::Query getZimQuery(bool verbose) const; + + friend bool operator<(const SearchInfo& l, const SearchInfo& r) + { + return std::tie(l.bookName, l.pattern, l.geoQuery) + < std::tie(r.bookName, r.pattern, r.geoQuery); // keep the same order + } + + public: //data + std::string pattern; + GeoQuery geoQuery; + std::string bookName; +}; + + typedef kainjow::mustache::data MustacheData; typedef ConcurrentCache> SearcherCache; -typedef ConcurrentCache> SearchCache; +typedef ConcurrentCache> SearchCache; typedef ConcurrentCache> SuggestionSearcherCache; class Entry; @@ -77,7 +121,7 @@ class InternalServer { bool start(); void stop(); std::string getAddress() { return m_addr; } - int getPort() { return m_port; } + int getPort() { return m_port; } private: // functions std::unique_ptr handle_request(const RequestContext& request);