/** * Processes incoming search request */ public function readInSearchRequest() { global $wgCanonicalNamespaceNames, $wgExtraNamespaces, $wgContentNamespaces; $this->aOptions['searchStringRaw'] = $this->oSearchRequest->sInput; $this->aOptions['searchStringOrig'] = ExtendedSearchBase::preprocessSearchInput($this->oSearchRequest->sInput); self::$searchStringRaw = $this->aOptions['searchStringRaw']; $sCustomerId = $this->getCustomerId(); $sLogOp = ' OR '; $aFq = array(); $aBq = array(); $oRequest = RequestContext::getMain()->getRequest(); $scope = $oRequest->getVal('search_scope', BsConfig::get('MW::ExtendedSearch::DefScopeUser')) == 'title' ? 'title' : 'text'; $this->aOptions['scope'] = $scope; $vNamespace = $this->checkSearchstringForNamespace($this->aOptions['searchStringRaw'], $this->aOptions['searchStringOrig'], $aFq, BsConfig::get('MW::ExtendedSearch::ShowFacets')); $this->aOptions['searchStringWildcarded'] = SearchService::wildcardSearchstring($this->aOptions['searchStringOrig']); $this->aOptions['searchStringForStatistics'] = $this->aOptions['searchStringWildcarded']; $aSearchTitle = array('title:(' . $this->aOptions['searchStringOrig'] . ')^5', 'titleWord:(' . $this->aOptions['searchStringOrig'] . ')^2', 'titleReverse:(' . $this->aOptions['searchStringWildcarded'] . ')', 'redirects:(' . $this->aOptions['searchStringOrig'] . ')'); $aSearchText = array('textWord:(' . $this->aOptions['searchStringOrig'] . ')^2', 'textReverse:(' . $this->aOptions['searchStringWildcarded'] . ')', 'sections:(' . $this->aOptions['searchStringOrig'] . ')'); $sSearchStringTitle = implode($sLogOp, $aSearchTitle); $sSearchStringText = implode($sLogOp, $aSearchText); $this->aOptions['searchStringFinal'] = $this->aOptions['scope'] === 'title' ? $sSearchStringTitle : $sSearchStringTitle . $sLogOp . $sSearchStringText; // filter query $aFq[] = 'redirect:0'; $aFq[] = 'special:0'; $aFq[] = 'wiki:(' . $sCustomerId . ')'; // $this->aOptions['namespaces'] HAS TO BE an array with numeric indices of type string! $this->aOptions['namespaces'] = $this->oSearchRequest->aNamespaces; $aNamespaces = array_slice($wgCanonicalNamespaceNames, 2); $aNamespaces = $aNamespaces + $wgExtraNamespaces; $bTagNamespace = false; if ($vNamespace === false) { $this->aOptions['files'] = $this->oSearchRequest->bSearchFiles === true ? true : false; $oUser = RequestContext::getMain()->getUser(); if (!$oUser->getOption('searcheverything')) { if (empty($this->aOptions['namespaces']) && $this->oSearchRequest->bNoSelect === false) { $this->aOptions['namespaces'] = array(); $aOptions = $oUser->getOptions(); foreach ($aOptions as $sOpt => $sValue) { if (strpos($sOpt, 'searchNs') !== false && $sValue == true) { $this->aOptions['namespaces'][] = '' . str_replace('searchNs', '', $sOpt); } } $aAllowedTypes = explode(',', BsConfig::get('MW::ExtendedSearch::IndexFileTypes')); $aAllowedTypes = array_map('trim', $aAllowedTypes); $aSearchFilesFacet = array_intersect($this->oSearchRequest->aType, $aAllowedTypes); if (($this->aOptions['files'] === true || !empty($aSearchFilesFacet)) && $oUser->isAllowed('searchfiles')) { $this->aOptions['namespaces'][] = '999'; $this->aOptions['namespaces'][] = '998'; } } else { $bTagNamespace = true; $aTmp = array(); foreach ($this->aOptions['namespaces'] as $iNs) { if (BsNamespaceHelper::checkNamespacePermission($iNs, 'read') === true) { $aTmp[] = $iNs; } } $this->aOptions['namespaces'] = $aTmp; } } else { if (empty($this->aOptions['namespaces'])) { $aTmp = array(); foreach ($aNamespaces as $iNs) { if (BsNamespaceHelper::checkNamespacePermission($iNs, 'read') === true) { $this->aOptions['namespaces'][] = $iNs; } } } else { $bTagNamespace = true; $aTmp = array(); foreach ($this->aOptions['namespaces'] as $iNs) { if (!BsNamespaceHelper::checkNamespacePermission($iNs, 'read')) { $aTmp[] = $iNs; } } if (!empty($aTmp)) { $this->aOptions['namespaces'] = array_diff($this->aOptions['namespaces'], $aTmp); } } } } else { $bTagNamespace = true; $this->aOptions['namespaces'][] = '' . $vNamespace; } $this->aOptions['namespaces'] = array_unique($this->aOptions['namespaces']); if (!empty($this->aOptions['namespaces'])) { $aFqNamespaces = array(); foreach ($this->aOptions['namespaces'] as $sNamespace) { $aFqNamespaces[] = $sNamespace; if ($sNamespace == '999') { $filesAlreadyAddedInLoopBefore = true; } } if (!isset($filesAlreadyAddedInLoopBefore) && $this->aOptions['files'] === true && $oUser->isAllowed('searchfiles')) { $aFqNamespaces[] = '999'; } $bTagNamespace = true; $aFq[] = BsConfig::get('MW::ExtendedSearch::ShowFacets') ? '{!tag=na}namespace:("' . implode('" "', $aFqNamespaces) . '")' : 'namespace:("' . implode('" "', $aFqNamespaces) . '")'; } // $this->aOptions['cats'] = $this->oSearchRequest->sCat; // string, defaults to '' if 'search_cat' not set in REQUEST $this->aOptions['cats'] = $this->oSearchRequest->sCategories; // array of strings or empty array if (!empty($this->aOptions['cats'])) { if (isset($this->oSearchRequest->sOperator)) { switch ($this->oSearchRequest->sOperator) { case 'AND': $sLogOp = ' AND '; break; default: } } $sFqCategories = BsConfig::get('MW::ExtendedSearch::ShowFacets') ? '{!tag=ca}' : ''; $sFqCategories .= 'cat:("' . implode('"' . $sLogOp . '"', $this->aOptions['cats']) . '")'; $aFq[] = $sFqCategories; } $this->aOptions['type'] = $this->oSearchRequest->aType; if (!empty($this->aOptions['type'])) { $sFqType = BsConfig::get('MW::ExtendedSearch::ShowFacets') ? '{!tag=ty}' : ''; $sFqType .= 'type:("' . implode('"' . $sLogOp . '"', $this->aOptions['type']) . '")'; $aFq[] = $sFqType; } $this->aOptions['editor'] = $this->oSearchRequest->sEditor; if (!empty($this->aOptions['editor'])) { // there may be spaces in name of editor. solr analyses those to two // terms (editor's names) thus we wrap the name into quotation marks // todo: better: in schema.xml define field editor not to be tokenized // at whitespace // but: +editor:("Robert V" "Mathias S") is already split correctly! $sFqEditor = BsConfig::get('MW::ExtendedSearch::ShowFacets') ? '{!tag=ed}' : ''; $sFqEditor .= 'editor:("' . implode('"' . $sLogOp . '"', $this->aOptions['editor']) . '")'; $aFq[] = $sFqEditor; } // Boost query foreach ($wgContentNamespaces as $iNs) { $aBq[] = "namespace:{$iNs}^2"; } // We want that files are also seen as a content namespace $aBq[] = "namespace:999^2"; $searchLimit = BsConfig::get('MW::ExtendedSearch::LimitResults'); $this->aOptions['offset'] = $this->oSearchRequest->iOffset; $this->aOptions['order'] = $this->oSearchRequest->sOrder; $this->aOptions['asc'] = $this->oSearchRequest->sAsc; $this->aOptions['searchLimit'] = $searchLimit == 0 ? 15 : $searchLimit; $this->aOptions['titleExists'] = ExtendedSearchBase::titleExists($this->oSearchRequest->sInput, $this->aOptions); $this->aOptions['bExtendedForm'] = $this->oSearchRequest->bExtendedForm; $this->aSearchOptions['defType'] = 'edismax'; $this->aSearchOptions['fl'] = 'uid,type,title,path,namespace,cat,ts,redirects,overall_type'; $this->aSearchOptions['fq'] = $aFq; $this->aSearchOptions['sort'] = $this->aOptions['order'] . ' ' . $this->aOptions['asc']; $this->aSearchOptions['hl'] = 'on'; $this->aSearchOptions['hl.fl'] = 'titleWord, titleReverse, sections, textWord, textReverse'; $this->aSearchOptions['hl.snippets'] = BsConfig::get('MW::ExtendedSearch::HighlightSnippets'); $this->aSearchOptions['bq'] = implode(' ', $aBq); if (BsConfig::get('MW::ExtendedSearch::ShowFacets')) { $this->aSearchOptions['facet'] = 'on'; $this->aSearchOptions['facet.sort'] = 'false'; $this->aSearchOptions['facet.mincount'] = '1'; $this->aSearchOptions['facet.missing'] = 'true'; $this->aSearchOptions['facet.field'] = array(); $this->aSearchOptions['facet.field'][] = $bTagNamespace === true ? '{!ex=na}namespace' : 'namespace'; $this->aSearchOptions['facet.field'][] = isset($sFqCategories) ? '{!ex=ca}cat' : 'cat'; $this->aSearchOptions['facet.field'][] = isset($sFqType) ? '{!ex=ty}type' : 'type'; $this->aSearchOptions['facet.field'][] = isset($sFqEditor) ? '{!ex=ed}editor' : 'editor'; } }
/** * Creates the facets */ private function createFacets() { $sSiteUri = $this->oSearchUriBuilder->buildUri(SearchUriBuilder::ALL, SearchUriBuilder::MLT); // possible orders: count, name, checked // possible directions: 1 = desc, -1 = asc $aOrder = BsConfig::get('MW::SortAlph') ? array('name' => -1) : array('count' => 1); $this->setData('sortorder', $aOrder); $aBaseFacets = array('namespace' => array('param' => 'na', 'option' => 'namespaces', 'i18n' => 'bs-extendedsearch-facet-namespace', 'url' => $this->oSearchUriBuilder->buildUri(SearchUriBuilder::ALL, SearchUriBuilder::NAMESPACES | SearchUriBuilder::FILES)), 'cat' => array('param' => 'ca', 'option' => 'cats', 'i18n' => 'bs-extendedsearch-facet-category', 'url' => $this->oSearchUriBuilder->buildUri(SearchUriBuilder::ALL, SearchUriBuilder::CATS)), 'editor' => array('param' => 'ed', 'option' => 'editor', 'i18n' => 'bs-extendedsearch-facet-editors', 'url' => $this->oSearchUriBuilder->buildUri(SearchUriBuilder::ALL, SearchUriBuilder::NAMESPACES | SearchUriBuilder::EDITOR)), 'type' => array('param' => 'ty', 'option' => 'type', 'i18n' => 'bs-extendedsearch-facet-type', 'url' => $this->oSearchUriBuilder->buildUri(SearchUriBuilder::ALL, SearchUriBuilder::NAMESPACES | SearchUriBuilder::TYPE))); wfRunHooks('BSExtendedSearchBeforeCreateFacets', array(&$aBaseFacets)); foreach ($aBaseFacets as $sFacet => $aConfig) { $oFacet = new ViewSearchFacet(); if (!is_null($this->oResponse->facet_counts->facet_fields->{$sFacet})) { $oFacet->setOption('title', $aConfig['i18n']); /* alters to: * array( * 0 => array( 'checked' => true ), * 1 => array( 'count' => 15 ), * 999 => array( 'checked' => true, 'count' => 2 ) * )*/ $aFacets = array(); $aData = $this->oSearchOptions->getOption($aConfig['option']); if (!empty($aData)) { foreach ($aData as $key => $value) { $aFacets[$value]['checked'] = true; } unset($aData); } // Get all available facets $aFacetsInRespsonse = $this->oResponse->facet_counts->facet_fields->{$sFacet}; foreach ($aFacetsInRespsonse as $key => $count) { if ($key == '_empty_') { continue; } if ($sFacet === 'namespace') { if (BsNamespaceHelper::checkNamespacePermission($key, 'read') === false || $count == '0') { unset($aFacets[$key]); continue; } } elseif ($sFacet === 'type') { if ($key != 'wiki' && !$this->oContext->getUser()->isAllowed('searchfiles')) { continue; } } $aFacets[$key]['count'] = $count; } // Prepare available facets. Add some information for each facet foreach ($aFacets as $key => $attributes) { if (!isset($aFacets[$key]['count'])) { unset($aFacets[$key]); continue; } if ($sFacet === 'namespace') { if ($key == '999') { $sTitle = wfMessage('bs-extendedsearch-facet-namespace-files')->plain(); } elseif ($key == '998') { $sTitle = wfMessage('bs-extendedsearch-facet-namespace-extfiles')->plain(); } elseif ($key == '0') { $sTitle = wfMessage('bs-ns_main')->plain(); } else { $sTitle = BsNamespaceHelper::getNamespaceName($key, false); if (empty($sTitle)) { unset($aFacets[$key]); continue; } } } elseif ($sFacet === 'cat') { $sTitle = $key == 'notcategorized' ? wfMessage('bs-extendedsearch-facet-uncategorized')->plain() : $key; } elseif ($sFacet === 'editor') { $sTitle = $key === 'unknown' ? wfMessage('bs-extendedsearch-unknown')->plain() : $key; } elseif ($sFacet === 'type') { $sTitle = $key; } $aFacets[$key]['title'] = $this->getFacetTitle($sTitle); $aFacets[$key]['name'] = $this->reduceMaxFacetLength($sTitle); } uasort($aFacets, array($this, 'compareEntries')); $aFacetAll = array(); foreach ($aFacets as $key => $attributes) { $aDataSet = array(); if (isset($attributes['checked'])) { $aDataSet['checked'] = true; } if ($sFacet === 'namespace') { if ($key == '999') { $uri = $this->oSearchUriBuilder->buildUri(SearchUriBuilder::ALL, SearchUriBuilder::NAMESPACES | SearchUriBuilder::FILES); $uri .= '&search_files=' . isset($attributes['checked']) ? '0' : '1'; } else { $uri = $this->oSearchUriBuilder->buildUri(SearchUriBuilder::ALL, SearchUriBuilder::NAMESPACES); } } elseif ($sFacet === 'cat') { $uri = $this->oSearchUriBuilder->buildUri(SearchUriBuilder::ALL, SearchUriBuilder::CATS); } elseif ($sFacet === 'editor') { $uri = $this->oSearchUriBuilder->buildUri(SearchUriBuilder::ALL, SearchUriBuilder::EDITOR); } elseif ($sFacet === 'type') { $uri = $this->oSearchUriBuilder->buildUri(SearchUriBuilder::ALL, SearchUriBuilder::TYPE); } foreach ($aFacets as $namespaceUrl => $attributesUrl) { $bOwnUrlAndNotAlreadyChecked = $key == $namespaceUrl && !isset($attributesUrl['checked']); $bOtherUrlAndAlreadyChecked = $key != $namespaceUrl && isset($attributesUrl['checked']); if ($bOwnUrlAndNotAlreadyChecked || $bOtherUrlAndAlreadyChecked) { $uri .= "&{$aConfig['param']}[]=" . $namespaceUrl; } } $aDataSet['uri'] = $uri; $aDataSet['diff'] = "{$aConfig['param']}[]=" . $key; $aDataSet['name'] = $attributes['name']; $aDataSet['title'] = $attributes['title']; $aDataSet['count'] = (int) $attributes['count']; $oFacet->setData($aDataSet); $aFacetAll[] = "{$aConfig['param']}[]=" . $key; } if ($sFacet === 'namespace') { $aReqNs = $this->oSearchOptions->getOption('namespaces'); foreach ($aReqNs as $ikey => $value) { if (!array_key_exists($value, $aFacets)) { $aFacetAll[] = "{$aConfig['param']}[]=" . $value; $sSiteUri = str_replace("&{$aConfig['param']}[]={$value}", '', $sSiteUri); } } } $sFacetAll = implode('&', $aFacetAll); $oFacet->setOption('uri-facet-all-diff', $sFacetAll); } $this->vSearchResult->setFacet($oFacet); } $this->vSearchResult->setOption('siteUri', $sSiteUri); }