privacore-open-source-searc.../SearchInput.cpp
Ivan Skytte Jørgensen beeddcf35d Got rid of gb-include.h
2018-07-26 17:29:51 +02:00

642 lines
17 KiB
C++

#include "SearchInput.h"
#include "Parms.h" // g_parms
#include "Pages.h" // g_msg
#include "CountryCode.h"
#include "PageResults.h"
#include "GbUtil.h"
#include "Collectiondb.h"
#include "Conf.h"
#include "Errno.h"
SearchInput::SearchInput()
: m_word_variations_config()
{
// Coverity
m_niceness = 0;
m_displayQuery = NULL;
m_cr = NULL;
m_isMasterAdmin = false;
m_isCollAdmin = false;
m_queryLangId = langUnknown;
m_format = 0;
m_START = 0;
m_coll = NULL;
m_query = NULL;
m_prepend = NULL;
m_showImages = false;
m_useCache = -1; // default from Param.cc
m_rcache = false;
m_wcache = -1; // default from Param.cc
m_debug = false;
m_displayMetas = NULL;
m_url = NULL;
m_sites = NULL;
m_plus = NULL;
m_minus = NULL;
m_link = NULL;
m_quote1 = NULL;
m_quote2 = NULL;
m_titleMaxLen = 0;
m_maxSerpScore = 0.0;
m_minSerpDocId = 0;
m_fx_qlang = nullptr;
m_fx_blang = nullptr;
m_fx_fetld = nullptr;
m_fx_country = nullptr;
m_defaultSortLang = NULL;
m_dedupURL = 0;
m_percentSimilarSummary = 0;
m_showBanned = false;
m_includeCachedCopy = 0;
m_familyFilter = false;
m_allowHighFrequencyTermCache = false;
m_minMsg3aTimeout = 0;
m_showErrors = false;
m_doSiteClustering = false;
m_doDupContentRemoval = false;
m_getDocIdScoringInfo = false;
m_hideAllClustered = false;
m_askOtherShards = false;
memset(m_queryId, 0, sizeof(m_queryId));
m_doMaxScoreAlgo = false;
m_sameLangWeight = 0.0;
m_unknownLangWeight = 0.0;
m_baseScoringParameters.clear();
m_numFlagScoreMultipliers=26;
m_numFlagRankAdjustments=26;
m_sortBy = 0;
m_filetype = NULL;
m_realMaxTop = 0;
m_numLinesInSummary = 0;
m_summaryMaxWidth = 0;
m_summaryMaxNumCharsPerLine = 0;
m_docsWanted = 0;
m_firstResultNum = 0;
m_doQueryHighlighting = false;
m_highlightQuery = NULL;
m_displayInlinks = 0;
m_docIdsOnly = 0;
m_formatStr = NULL;
m_END = 0;
}
SearchInput::~SearchInput() {
}
void SearchInput::clear () {
// set all to 0 just to avoid any inconsistencies
int32_t size = (char *)&m_END - (char *)&m_START;
memset ( &m_START , 0x00 , size );
m_sbuf1.reset();
m_sbuf2.reset();
// set these
m_numLinesInSummary = 2;
m_docsWanted = 10;
m_niceness = 0;
}
void SearchInput::copy ( class SearchInput *si ) {
memmove ( (char *)this , (char *)si , sizeof(SearchInput) );
}
bool SearchInput::set(TcpSocket *sock, HttpRequest *r, lang_t primaryQueryLanguage, const std::vector<std::pair<lang_t, double>> &language_weights) {
// store list of collection #'s to search here. usually just one.
m_collnumBuf.reset();
m_q.reset();
// zero out everything
clear();
m_hr.copy(r);
const char *coll = g_collectiondb.getDefaultColl(r->getString("c"));
//////
//
// build "m_collnumBuf" to consist of all the collnums we should
// be searching.
//
///////
// set this to the collrec of the first valid collnum we encounter
CollectionRec *cr = NULL;
// now convert list of space-separated coll names into list of collnums
const char *p = r->getString("c",NULL);
// if we had a "&c=..." in the GET request process that
if ( p ) {
do {
const char *end = p;
for ( ; *end && ! is_wspace_a(*end) ; end++ );
CollectionRec *tmpcr = g_collectiondb.getRec ( p, end-p );
// set defaults from the FIRST one
if ( tmpcr && ! cr ) {
cr = tmpcr;
}
if ( ! tmpcr ) {
g_errno = ENOCOLLREC;
log("query: missing collection %*.*s",(int)(end-p),(int)(end-p),p);
g_msg = " (error: no such collection)";
return false;
}
// add to our list
if (!m_collnumBuf.safeMemcpy(&tmpcr->m_collnum,
sizeof(collnum_t)))
return false;
// advance
p = end;
// skip to next collection name if there is one
while ( *p && is_wspace_a(*p) ) p++;
// now add it's collection # to m_collnumBuf if there
} while(*p);
}
// use default collection if none provided
if ( ! p && m_collnumBuf.length() <= 0 ) {
// get default collection rec
cr = g_collectiondb.getRec (coll);
// add to our list
if ( cr && !m_collnumBuf.safeMemcpy(&cr->m_collnum, sizeof(collnum_t))) {
return false;
}
}
/////
//
// END BUILDING m_collnumBuf
//
/////
// save the collrec
m_cr = cr;
// must have had one
if ( ! cr ) {
log("si: si. collection does not exist");
// if we comment the below out then it cores in setToDefault!
g_errno = ENOCOLLREC;
return false;
}
// and set from the http request. will set m_coll, etc.
g_parms.setToDefault ( (char *)this , OBJ_SI , cr );
///////
//
// set defaults of some things based on format language
//
//////
// get the format. "html" "json" --> FORMAT_HTML, FORMAT_JSON ...
m_format = m_hr.getReplyFormat();
// now override automatic defaults for special cases
if ( m_format != FORMAT_HTML ) {
m_doQueryHighlighting = false;
m_getDocIdScoringInfo = false;
}
// if they have a list of sites...
if ( m_sites && m_sites[0] ) {
m_doSiteClustering = false;
}
// and set from the http request. will set m_coll, etc.
g_parms.setFromRequest ( &m_hr , sock , cr , (char *)this , OBJ_SI );
m_coll = coll;
//////
//
// fix some parms
//
//////
// set m_isMasterAdmin to zero if no correct ip or password
if ( ! g_conf.isMasterAdmin ( sock , &m_hr ) ) {
m_isMasterAdmin = false;
}
// collection admin?
m_isCollAdmin = g_conf.isCollAdmin ( sock , &m_hr );
//////////////////////////////////////
//
// transform input into classes
//
//////////////////////////////////////
// . returns false and sets g_errno on error
// . sets m_qbuf1 and m_qbuf2
// . sets:
// m_sbuf1
// m_sbuf2
// m_sbuf3
// m_displayQuery
// m_qe (encoded query)
// m_rtl (right to left like hebrew)
// m_highlightQuery
if ( ! setQueryBuffers (r) ) {
log(LOG_WARN, "query: setQueryBuffers: %s",mstrerror(g_errno));
return false;
}
// set queryLangId by priority
// - configured sort language
// - detected query language
// - collection configured sort language
if (m_defaultSortLang && m_defaultSortLang[0] != '\0') {
m_queryLangId = getLangIdFromAbbr(m_defaultSortLang);
} else if (primaryQueryLanguage != langUnknown) {
m_queryLangId = primaryQueryLanguage;
} else if (cr->m_defaultSortLanguage2[0] != '\0') {
m_queryLangId = getLangIdFromAbbr(cr->m_defaultSortLanguage2);
}
log(LOG_INFO, "query: using primary query lang of %s", getLanguageAbbr(m_queryLangId));
if(!language_weights.empty()) {
//external language detection server had results
//weights cannot be negaitive, so we use -1 as a temporary flag for not-yet-set.
//the query language server may return fewer or more languages than what we support. And for some language/dialect groups
//it may return distinct weights that map to the same gb-lanaguage. eg. no=4.5, nn=3.2, no-bk=8.4, in which case we have
//to take the maximum weight.
for(int i=0; i<64; i++) //set all to not-yet-set
m_baseScoringParameters.m_languageWeights[i] = -1;
for(const auto &e : language_weights) //process the result
m_baseScoringParameters.m_languageWeights[e.first] = std::max((float)e.second, m_baseScoringParameters.m_languageWeights[e.first]);
for(int i=0; i<64; i++) //set any unset to some sensible default
if(m_baseScoringParameters.m_languageWeights[i]<0)
m_baseScoringParameters.m_languageWeights[i] = 0.01; //server returns weights in the range [0..1]
} else {
if(m_baseScoringParameters.allLanguageWeightsAreTheSame()) {
//query language server is unavailable.
for(int i=0; i<64; i++)
m_baseScoringParameters.m_languageWeights[i] = 1.0;
m_baseScoringParameters.m_languageWeights[langUnknown] = m_unknownLangWeight; //backward compatible. questionable behaviour
m_baseScoringParameters.m_languageWeights[m_queryLangId] = m_sameLangWeight;
}
//else: user specified language weights explicitly
}
int32_t maxQueryTerms = cr->m_maxQueryTerms;
// . the query to use for highlighting... can be overriden with "hq"
// . we need the language id for doing synonyms
if (m_prepend && m_prepend[0]) {
m_hqq.set(m_prepend, m_queryLangId, 1.0, 1.0, &m_word_variations_config, true, m_allowHighFrequencyTermCache, maxQueryTerms);
} else if (m_highlightQuery && m_highlightQuery[0]) {
m_hqq.set(m_highlightQuery, m_queryLangId, 1.0, 1.0, &m_word_variations_config, true, m_allowHighFrequencyTermCache, maxQueryTerms);
} else if (m_query && m_query[0]) {
m_hqq.set(m_query, m_queryLangId, 1.0, 1.0, &m_word_variations_config, true, m_allowHighFrequencyTermCache, maxQueryTerms);
}
// log it here
log(LOG_INFO, "query: got query %s (len=%i)" ,m_sbuf1.getBufStart() ,m_sbuf1.length());
// . now set from m_qbuf1, the advanced/composite query buffer
// . returns false and sets g_errno on error (?)
if ( !m_q.set(m_sbuf1.getBufStart(),
m_queryLangId,
1.0, 1.0,
&m_word_variations_config,
true, // use QUERY stopwords?
m_allowHighFrequencyTermCache,
maxQueryTerms) ) {
g_msg = " (error: query has too many operands)";
return false;
}
if ( m_q.m_truncated && m_q.m_isBoolean ) {
g_errno = EQUERYTOOBIG;
g_msg = " (error: query is too long)";
return false;
}
if ( m_hideAllClustered )
m_doSiteClustering = true;
// turn off some parms
if ( m_q.m_hasPositiveSiteField ) {
m_doSiteClustering = false;
}
if ( ! m_doSiteClustering )
m_hideAllClustered = false;
// sanity check
if(m_firstResultNum < 0) {
m_firstResultNum = 0;
}
// . if query has url: or site: term do NOT use cache by def.
// . however, if spider is off then use the cache by default
if ( m_useCache == -1 && g_conf.m_spideringEnabled ) {
if ( m_q.m_hasPositiveSiteField ) m_useCache = 0;
else if ( m_q.m_hasIpField ) m_useCache = 0;
else if ( m_q.m_hasUrlField ) m_useCache = 0;
else if ( m_sites && m_sites[0] ) m_useCache = 0;
//else if ( m_whiteListBuf.length() ) m_useCache = 0;
else if ( m_url && m_url[0] ) m_useCache = 0;
}
// if useCache is still -1 then turn it on
if ( m_useCache == -1 ) m_useCache = 1;
bool readFromCache = false;
if ( m_useCache == 1 ) readFromCache = true;
if ( !m_rcache ) readFromCache = false;
if ( m_useCache == 0 ) readFromCache = false;
// if useCache is false, don't write to cache if it was not specified
if ( m_wcache == -1 ) {
if ( m_useCache == 0 ) m_wcache = 0;
else m_wcache = 1;
}
// save it
m_rcache = readFromCache;
return true;
}
// . sets m_qbuf1[] and m_qbuf2[]
// . m_qbuf1[] is the advanced query
// . m_qbuf2[] is the query to be used for spell checking
// . returns false and set g_errno on error
bool SearchInput::setQueryBuffers ( HttpRequest *hr ) {
m_sbuf1.reset();
m_sbuf2.reset();
// prepend
const char *qp = hr->getString("prepend",NULL,NULL);
if( qp && qp[0] ) {
if ( m_sbuf1.length() ) m_sbuf1.pushChar(' ');
m_sbuf1.safePrintf( "%s", qp );
}
// boolean OR terms
bool boolq = false;
const char *any = hr->getString("any",NULL);
bool first = true;
if ( any ) {
const char *s = any;
const char *send = any + strlen(any);
if ( m_sbuf1.length() ) m_sbuf1.pushChar(' ');
if ( m_sbuf2.length() ) m_sbuf2.pushChar(' ');
while (s < send) {
while (isspace(*s) && s < send) s++;
const char *s2 = s+1;
if (*s == '\"') {
// if there's no closing quote just treat
// the end of the line as such
while (*s2 != '\"' && s2 < send) s2++;
if (s2 < send) s2++;
} else {
while (!isspace(*s2) && s2 < send) s2++;
}
if ( first ) {
m_sbuf1.safeStrcpy("(");
m_sbuf2.safeStrcpy("(");
}
if ( ! first ) {
m_sbuf1.safeStrcpy(" OR ");
m_sbuf2.safeStrcpy(" OR ");
}
first = false;
m_sbuf1.safeMemcpy ( s , s2 - s );
m_sbuf2.safeMemcpy ( s , s2 - s );
s = s2 + 1;
}
}
if ( ! first ) {
m_sbuf1.safeStrcpy(") AND ");
m_sbuf2.safeStrcpy(") AND ");
boolq = true;
}
if ( m_sortBy == 1 ) {
log(LOG_WARN, "query: m_sortBy=%d. This is currently not supported", m_sortBy);
//if ( m_sbuf1.length() ) m_sbuf1.pushChar(' ');
//m_sbuf1.safePrintf("gbsortbyint:gbspiderdate");
}
if ( m_sortBy == 2 ) {
log(LOG_WARN, "query: m_sortBy=%d. This is currently not supported", m_sortBy);
//if ( m_sbuf1.length() ) m_sbuf1.pushChar(' ');
//m_sbuf1.safePrintf("gbrevsortbyint:gbspiderdate");
}
char *ft = m_filetype;
if ( ft && strcasecmp(ft,"any")==0 ) ft = NULL;
if ( ft && ! ft[0] ) ft = NULL;
if ( ft ) {
if ( m_sbuf1.length() ) m_sbuf1.pushChar(' ');
m_sbuf1.safePrintf("filetype:%s",ft);
}
// PRE-pend gblang: term
int32_t gblang = hr->getLong("gblang",-1);
if( gblang >= 0 ) {
if ( m_sbuf1.length() ) m_sbuf1.pushChar(' ');
if ( m_sbuf2.length() ) m_sbuf2.pushChar(' ');
m_sbuf1.safePrintf( "+gblang:%" PRId32, gblang );
m_sbuf2.safePrintf( "+gblang:%" PRId32, gblang );
if ( ! boolq ) {
m_sbuf1.safeStrcpy(" |");
m_sbuf2.safeStrcpy(" |");
}
else {
m_sbuf1.safeStrcpy(" AND ");
m_sbuf2.safeStrcpy(" AND ");
}
}
// append url: term
if ( m_link && m_link[0] ) {
if ( m_sbuf1.length() ) m_sbuf1.pushChar(' ');
if ( m_sbuf2.length() ) m_sbuf2.pushChar(' ');
m_sbuf1.safeStrcpy ( "+link:");
m_sbuf2.safeStrcpy ( "+link:");
m_sbuf1.safeStrcpy ( m_link );
m_sbuf2.safeStrcpy ( m_link );
if ( ! boolq ) {
m_sbuf1.safeStrcpy(" |");
m_sbuf2.safeStrcpy(" |");
}
else {
m_sbuf1.safeStrcpy(" AND ");
m_sbuf2.safeStrcpy(" AND ");
}
}
m_sbuf1.setLabel("sisbuf1");
m_sbuf2.setLabel("sisbuf2");
// append the natural query
if ( m_query && m_query[0] ) {
if ( m_sbuf1.length() ) m_sbuf1.pushChar(' ');
m_sbuf1.safeStrcpy ( m_query );
if ( m_sbuf2.length() ) m_sbuf2.pushChar(' ');
m_sbuf2.safeStrcpy ( m_query );
}
// append quoted phrases to query
if ( m_quote1 && m_quote1[0] ) {
if ( m_sbuf1.length() ) m_sbuf1.pushChar(' ');
if ( ! boolq ) {
m_sbuf1.safeStrcpy(" +\"");
m_sbuf2.safeStrcpy(" +\"");
}
else {
m_sbuf1.safeStrcpy(" AND \"");
m_sbuf2.safeStrcpy(" AND \"");
}
m_sbuf1.safeStrcpy ( m_quote1 );
m_sbuf1.safeStrcpy("\"");
if ( m_sbuf2.length() ) m_sbuf2.pushChar(' ');
m_sbuf2.safeStrcpy ( m_quote1 );
m_sbuf2.safeStrcpy("\"");
}
if ( m_quote2 && m_quote2[0] ) {
if ( m_sbuf1.length() ) m_sbuf1.pushChar(' ');
if ( ! boolq ) {
m_sbuf1.safeStrcpy(" +\"");
m_sbuf2.safeStrcpy(" +\"");
}
else {
m_sbuf1.safeStrcpy(" AND \"");
m_sbuf2.safeStrcpy(" AND \"");
}
m_sbuf1.safeStrcpy ( m_quote2 );
m_sbuf1.safeStrcpy("\"");
if ( m_sbuf2.length() ) m_sbuf2.pushChar(' ');
m_sbuf2.safeStrcpy ( m_quote2 );
m_sbuf2.safeStrcpy("\"");
}
// append plus terms
if ( m_plus && m_plus[0] ) {
const char *s = m_plus;
const char *send = m_plus + strlen(m_plus);
if ( m_sbuf1.length() ) m_sbuf1.pushChar(' ');
if ( m_sbuf2.length() ) m_sbuf2.pushChar(' ');
while (s < send) {
while (isspace(*s) && s < send) s++;
const char *s2 = s+1;
if (*s == '\"') {
// if there's no closing quote just treat
// the end of the line as such
while (*s2 != '\"' && s2 < send) s2++;
if (s2 < send) s2++;
} else {
while (!isspace(*s2) && s2 < send) s2++;
}
if ( ! boolq ) {
m_sbuf1.safeStrcpy("+");
m_sbuf2.safeStrcpy("+");
}
else {
m_sbuf1.safeStrcpy(" AND ");
m_sbuf2.safeStrcpy(" AND ");
}
m_sbuf1.safeMemcpy ( s , s2 - s );
m_sbuf2.safeMemcpy ( s , s2 - s );
s = s2 + 1;
if (s < send) {
if ( m_sbuf1.length() ) m_sbuf1.pushChar(' ');
if ( m_sbuf2.length() ) m_sbuf2.pushChar(' ');
}
}
}
// append minus terms
if ( m_minus && m_minus[0] ) {
const char *s = m_minus;
const char *send = m_minus + strlen(m_minus);
if ( m_sbuf1.length() ) m_sbuf1.pushChar(' ');
if ( m_sbuf2.length() ) m_sbuf2.pushChar(' ');
while (s < send) {
while (isspace(*s) && s < send) s++;
const char *s2 = s+1;
if (*s == '\"') {
// if there's no closing quote just treat
// the end of the line as such
while (*s2 != '\"' && s2 < send) s2++;
if (s2 < send) s2++;
} else {
while (!isspace(*s2) && s2 < send) s2++;
}
if (s2 < send) break;
if ( ! boolq ) {
m_sbuf1.safeStrcpy("-");
m_sbuf2.safeStrcpy("-");
}
else {
m_sbuf1.safeStrcpy(" AND NOT ");
m_sbuf2.safeStrcpy(" AND NOT ");
}
m_sbuf1.safeMemcpy ( s , s2 - s );
m_sbuf2.safeMemcpy ( s , s2 - s );
s = s2 + 1;
if (s < send) {
if ( m_sbuf1.length() ) m_sbuf1.pushChar(' ');
if ( m_sbuf2.length() ) m_sbuf2.pushChar(' ');
}
}
}
// append gbkeyword:numinlinks if they have &mininlinks=X, X>0
int32_t minInlinks = m_hr.getLong("mininlinks",0);
if ( minInlinks > 0 ) {
if ( m_sbuf1.length() ) m_sbuf1.pushChar(' ');
m_sbuf1.safePrintf ( "gbkeyword:numinlinks");
}
// null terms
if ( ! m_sbuf1.nullTerm() ) return false;
if ( ! m_sbuf2.nullTerm() ) return false;
// the natural query
m_displayQuery = m_sbuf2.getBufStart();
if ( ! m_displayQuery ) m_displayQuery = "";
while ( *m_displayQuery == ' ' ) m_displayQuery++;
// urlencoded display query
urlEncode(&m_qe, m_displayQuery);
return true;
}
std::string SearchInput::getPreferredResultLanguage() {
if (m_fx_qlang && strlen(m_fx_qlang) == 2) {
return m_fx_qlang;
}
if (m_fx_blang && strlen(m_fx_blang) >= 2) {
return std::string(m_fx_blang, 2);
}
return "en";
}