my changes
This commit is contained in:
parent
28301e4312
commit
8dc1f1b048
263 changed files with 36882 additions and 4453 deletions
413
app/Library/Util/SolrSearchQuery.php
Normal file
413
app/Library/Util/SolrSearchQuery.php
Normal file
|
@ -0,0 +1,413 @@
|
|||
<?php
|
||||
namespace App\Library\Util;
|
||||
|
||||
/**
|
||||
* Encapsulates all parameter values needed to build the Solr query URL.
|
||||
*/
|
||||
class SolrSearchQuery
|
||||
{
|
||||
// currently available search types
|
||||
const SIMPLE = 'simple';
|
||||
const ADVANCED = 'advanced';
|
||||
const FACET_ONLY = 'facet_only';
|
||||
const LATEST_DOCS = 'latest';
|
||||
const ALL_DOCS = 'all_docs';
|
||||
const DOC_ID = 'doc_id';
|
||||
|
||||
const DEFAULT_START = 0;
|
||||
const DEFAULT_ROWS = 10;
|
||||
// java.lang.Integer.MAX_VALUE
|
||||
const MAX_ROWS = 2147483647;
|
||||
const DEFAULT_SORTFIELD = 'score';
|
||||
const DEFAULT_SORTORDER = 'desc';
|
||||
const SEARCH_MODIFIER_CONTAINS_ALL = "contains_all";
|
||||
const SEARCH_MODIFIER_CONTAINS_ANY = "contains_any";
|
||||
const SEARCH_MODIFIER_CONTAINS_NONE = "contains_none";
|
||||
|
||||
private $start = self::DEFAULT_START;
|
||||
private $rows = self::DEFAULT_ROWS;
|
||||
private $sortField = self::DEFAULT_SORTFIELD;
|
||||
private $sortOrder = self::DEFAULT_SORTORDER;
|
||||
private $filterQueries = array();
|
||||
private $catchAll;
|
||||
private $searchType;
|
||||
private $modifier;
|
||||
private $fieldValues = array();
|
||||
private $escapingEnabled = true;
|
||||
private $q;
|
||||
private $facetField;
|
||||
private $returnIdsOnly = false;
|
||||
private $seriesId = null;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $searchType
|
||||
*/
|
||||
public function __construct($searchType = self::SIMPLE)
|
||||
{
|
||||
//$this->invalidQCache();
|
||||
$this->q = null;
|
||||
|
||||
if ($searchType === self::SIMPLE || $searchType === self::ADVANCED || $searchType === self::ALL_DOCS) {
|
||||
$this->searchType = $searchType;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($searchType === self::FACET_ONLY) {
|
||||
$this->searchType = self::FACET_ONLY;
|
||||
$this->setRows(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($searchType === self::LATEST_DOCS) {
|
||||
$this->searchType = self::LATEST_DOCS;
|
||||
$this->sortField = 'server_date_published';
|
||||
$this->sortOrder = 'desc';
|
||||
return;
|
||||
}
|
||||
|
||||
if ($searchType === self::DOC_ID) {
|
||||
$this->searchType = self::DOC_ID;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public function getSearchType()
|
||||
{
|
||||
return $this->searchType;
|
||||
}
|
||||
|
||||
public function getFacetField()
|
||||
{
|
||||
return $this->facetField;
|
||||
}
|
||||
|
||||
public function setFacetField($facetField)
|
||||
{
|
||||
$this->facetField = $facetField;
|
||||
}
|
||||
|
||||
public function getStart()
|
||||
{
|
||||
return $this->start;
|
||||
}
|
||||
|
||||
public function setStart($start)
|
||||
{
|
||||
$this->start = $start;
|
||||
}
|
||||
|
||||
public static function getDefaultRows()
|
||||
{
|
||||
return SolrSearchQuery::getDefaultRows();
|
||||
}
|
||||
|
||||
public function getRows()
|
||||
{
|
||||
return $this->rows;
|
||||
}
|
||||
|
||||
public function setRows($rows)
|
||||
{
|
||||
$this->rows = $rows;
|
||||
}
|
||||
|
||||
public function getSortField()
|
||||
{
|
||||
return $this->sortField;
|
||||
}
|
||||
|
||||
public function setSortField($sortField)
|
||||
{
|
||||
if ($sortField === self::DEFAULT_SORTFIELD) {
|
||||
if ($this->searchType === self::ALL_DOCS) {
|
||||
// change the default sortfield for searchtype all
|
||||
// since sorting by relevance does not make any sense here
|
||||
$this->sortField = 'server_date_published';
|
||||
} else {
|
||||
$this->sortField = self::DEFAULT_SORTFIELD;
|
||||
}
|
||||
return;
|
||||
}
|
||||
$this->sortField = $sortField;
|
||||
if (strpos($sortField, 'doc_sort_order_for_seriesid_') !== 0 && strpos($sortField, 'server_date_published') !== 0) {
|
||||
// add _sort to the end of $sortField if not already done
|
||||
$suffix = '_sort';
|
||||
if (substr($sortField, strlen($sortField) - strlen($suffix)) !== $suffix) {
|
||||
$this->sortField .= $suffix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getSortOrder()
|
||||
{
|
||||
return $this->sortOrder;
|
||||
}
|
||||
|
||||
public function setSortOrder($sortOrder)
|
||||
{
|
||||
$this->sortOrder = $sortOrder;
|
||||
}
|
||||
|
||||
public function getSeriesId()
|
||||
{
|
||||
return $this->seriesId;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return array An array that contains all specified filter queries.
|
||||
*/
|
||||
public function getFilterQueries()
|
||||
{
|
||||
return $this->filterQueries;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $filterField The field that should be used in a filter query.
|
||||
* @param string $filterValue The field value that should be used in a filter query.
|
||||
*/
|
||||
public function addFilterQuery($filterField, $filterValue)
|
||||
{
|
||||
if ($filterField == 'has_fulltext') {
|
||||
$filterQuery = $filterField . ':' . $filterValue;
|
||||
} else {
|
||||
$filterQuery = '{!raw f=' . $filterField . '}' . $filterValue;
|
||||
}
|
||||
array_push($this->filterQueries, $filterQuery);
|
||||
|
||||
// we need to store the ID of the requested series here,
|
||||
// since we need it later to build the index field name
|
||||
if ($filterField === 'series_ids') {
|
||||
$this->seriesId = $filterValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $filterQueries An array of queries that should be used as filter queries.
|
||||
*/
|
||||
public function setFilterQueries($filterQueries)
|
||||
{
|
||||
$this->filterQueries = $filterQueries;
|
||||
}
|
||||
|
||||
public function getCatchAll()
|
||||
{
|
||||
return $this->catchAll;
|
||||
}
|
||||
|
||||
public function setCatchAll($catchAll)
|
||||
{
|
||||
$this->catchAll = $catchAll;
|
||||
$this->invalidQCache();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
* @param string $modifier
|
||||
*/
|
||||
public function setField($name, $value, $modifier = self::SEARCH_MODIFIER_CONTAINS_ALL)
|
||||
{
|
||||
if (!empty($value)) {
|
||||
$this->fieldValues[$name] = $value;
|
||||
$this->modifier[$name] = $modifier;
|
||||
$this->invalidQCache();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $name
|
||||
* @return Returns null if no values was specified for the given field name.
|
||||
*/
|
||||
public function getField($name)
|
||||
{
|
||||
if (array_key_exists($name, $this->fieldValues)) {
|
||||
return $this->fieldValues[$name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $fieldname
|
||||
* @return returns null if no modifier was specified for the given field name.
|
||||
*/
|
||||
public function getModifier($fieldname)
|
||||
{
|
||||
if (array_key_exists($fieldname, $this->modifier)) {
|
||||
return $this->modifier[$fieldname];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getQ()
|
||||
{
|
||||
if (is_null($this->q)) {
|
||||
// earlier cached query was marked as invalid: perform new setup of query cache
|
||||
$this->q = $this->setupQCache();
|
||||
}
|
||||
|
||||
// return cached result (caching is done here since building q is an expensive operation)
|
||||
return $this->q;
|
||||
}
|
||||
|
||||
private function setupQCache()
|
||||
{
|
||||
if ($this->searchType === self::SIMPLE) {
|
||||
if ($this->getCatchAll() === '*:*') {
|
||||
return $this->catchAll;
|
||||
}
|
||||
return $this->escape($this->getCatchAll());
|
||||
}
|
||||
if ($this->searchType === self::FACET_ONLY || $this->searchType === self::LATEST_DOCS || $this->searchType === self::ALL_DOCS) {
|
||||
return '*:*';
|
||||
}
|
||||
if ($this->searchType === self::DOC_ID) {
|
||||
return 'id:' . $this->fieldValues['id'];
|
||||
}
|
||||
return $this->buildAdvancedQString();
|
||||
}
|
||||
|
||||
private function invalidQCache()
|
||||
{
|
||||
$this->q = null;
|
||||
}
|
||||
|
||||
private function buildAdvancedQString()
|
||||
{
|
||||
$q = "{!lucene q.op=AND}";
|
||||
$first = true;
|
||||
foreach ($this->fieldValues as $fieldname => $fieldvalue) {
|
||||
if ($first) {
|
||||
$first = false;
|
||||
} else {
|
||||
$q .= ' ';
|
||||
}
|
||||
|
||||
if ($this->modifier[$fieldname] === self::SEARCH_MODIFIER_CONTAINS_ANY) {
|
||||
$q .= $this->combineSearchTerms($fieldname, $fieldvalue, 'OR');
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->modifier[$fieldname] === self::SEARCH_MODIFIER_CONTAINS_NONE) {
|
||||
$q .= '-' . $this->combineSearchTerms($fieldname, $fieldvalue, 'OR');
|
||||
continue;
|
||||
}
|
||||
|
||||
// self::SEARCH_MODIFIER_CONTAINS_ALL
|
||||
$q .= $this->combineSearchTerms($fieldname, $fieldvalue);
|
||||
}
|
||||
return $q;
|
||||
}
|
||||
|
||||
private function combineSearchTerms($fieldname, $fieldvalue, $conjunction = null)
|
||||
{
|
||||
$result = $fieldname . ':(';
|
||||
$firstTerm = true;
|
||||
$queryTerms = preg_split("/[\s]+/", $this->escape($fieldvalue), null, PREG_SPLIT_NO_EMPTY);
|
||||
foreach ($queryTerms as $queryTerm) {
|
||||
if ($firstTerm) {
|
||||
$firstTerm = false;
|
||||
} else {
|
||||
$result .= is_null($conjunction) ? " " : " $conjunction ";
|
||||
}
|
||||
$result .= $queryTerm;
|
||||
}
|
||||
$result .= ')';
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function disableEscaping()
|
||||
{
|
||||
$this->invalidQCache();
|
||||
$this->escapingEnabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape Lucene's special query characters specified in
|
||||
* http://lucene.apache.org/java/3_0_2/queryparsersyntax.html#Escaping%20Special%20Characters
|
||||
* Escaping currently ignores * and ? which are used as wildcard operators.
|
||||
* Additionally, double-quotes are not escaped and a double-quote is added to
|
||||
* the end of $query in case it contains an odd number of double-quotes.
|
||||
* @param string $query The query which needs to be escaped.
|
||||
*/
|
||||
private function escape($query)
|
||||
{
|
||||
if (!$this->escapingEnabled) {
|
||||
return $query;
|
||||
}
|
||||
$query = trim($query);
|
||||
// add one " to the end of $query if it contains an odd number of "
|
||||
if (substr_count($query, '"') % 2 == 1) {
|
||||
$query .= '"';
|
||||
}
|
||||
// escape special characters (currently ignore " \* \?) outside of ""
|
||||
$insidePhrase = false;
|
||||
$result = '';
|
||||
foreach (explode('"', $query) as $phrase) {
|
||||
if ($insidePhrase) {
|
||||
$result .= '"' . $phrase . '"';
|
||||
} else {
|
||||
$result .= preg_replace(
|
||||
'/(\+|-|&&|\|\||!|\(|\)|\{|}|\[|]|\^|~|:|\\\)/',
|
||||
'\\\$1',
|
||||
$this->lowercaseWildcardQuery($phrase)
|
||||
);
|
||||
}
|
||||
$insidePhrase = !$insidePhrase;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function lowercaseWildcardQuery($query)
|
||||
{
|
||||
// check if $query is a wildcard query
|
||||
if (strpos($query, '*') === false && strpos($query, '?') === false) {
|
||||
return $query;
|
||||
}
|
||||
// lowercase query
|
||||
return strtolower($query);
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
if ($this->searchType === self::SIMPLE) {
|
||||
return 'simple search with query ' . $this->getQ();
|
||||
}
|
||||
if ($this->searchType === self::FACET_ONLY) {
|
||||
return 'facet only search with query *:*';
|
||||
}
|
||||
if ($this->searchType === self::LATEST_DOCS) {
|
||||
return 'search for latest documents with query *:*';
|
||||
}
|
||||
if ($this->searchType === self::ALL_DOCS) {
|
||||
return 'search for all documents';
|
||||
}
|
||||
if ($this->searchType === self::DOC_ID) {
|
||||
return 'search for document id ' . $this->getQ();
|
||||
}
|
||||
return 'advanced search with query ' . $this->getQ();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param boolean $returnIdsOnly
|
||||
*/
|
||||
public function setReturnIdsOnly($returnIdsOnly)
|
||||
{
|
||||
$this->returnIdsOnly = $returnIdsOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function isReturnIdsOnly()
|
||||
{
|
||||
return $this->returnIdsOnly;
|
||||
}
|
||||
}
|
Loading…
Add table
editor.link_modal.header
Reference in a new issue