Implementation of enhancement #296 "Refactoring of the listview plugin"

This commit is contained in:
Frank Ulrich Weber 2018-11-19 17:37:10 +01:00
parent 3e6cda8755
commit da6b2cb5c1
9 changed files with 190 additions and 208 deletions

View File

@ -582,7 +582,8 @@ class tx_dlf_indexing {
$solrDoc->setField('sid', $logicalUnit['id']);
$solrDoc->setField('toplevel', in_array($logicalUnit['type'], self::$toplevel));
// There can be only one toplevel unit per uid, independently of backend configuration
$solrDoc->setField('toplevel', $logicalUnit['id'] == $doc->toplevelId ? TRUE : FALSE);
$solrDoc->setField('type', $logicalUnit['type'], self::$fields['fieldboost']['type']);

View File

@ -13,6 +13,7 @@
* List class 'tx_dlf_list' for the 'dlf' extension.
*
* @author Sebastian Meyer <sebastian.meyer@slub-dresden.de>
* @author Frank Ulrich Weber <fuw@zeutschel.de>
* @package TYPO3
* @subpackage tx_dlf
* @access public
@ -252,7 +253,7 @@ class tx_dlf_list implements ArrayAccess, Countable, Iterator, \TYPO3\CMS\Core\S
}
// If it is a fulltext search, enable highlighting and fetch the results
// If it is a fulltext search, enable highlighting.
if ($this->metadata['fulltextSearch']) {
$params['component'] = array (
@ -267,12 +268,30 @@ class tx_dlf_list implements ArrayAccess, Countable, Iterator, \TYPO3\CMS\Core\S
// Set additional query parameters.
$params['start'] = 0;
$params['rows'] = $this->solr->limit;
// Set query.
$params['query'] = 'uid:'.tx_dlf_solr::escapeQuery($record['uid']);
// All results are needed, so set the limit unreachable high.
$params['rows'] = 1000000000;
// Perform search.
// Take over existing filter queries.
$params['filterquery'] = isset($this->metadata['options']['params']['filterquery']) ? $this->metadata['options']['params']['filterquery'] : array ();
// Extend filter query to get all documents with the same uid.
foreach ($params['filterquery'] as $key=>$value) {
$params['filterquery'][$key] = array ('query' => $value['query'].' OR toplevel:true');
}
// Add filter query to get all documents with the required uid.
$params['filterquery'][] = array ('query' => 'uid:'.tx_dlf_solr::escapeQuery($record['uid']));
// Add sorting
$params['sort'] = $this->metadata['options']['params']['sort'];
// Set query
$params['query'] = $this->metadata['options']['select'].' OR toplevel:true';
// Perform search for all documents with the same uid that either fit to the search or marked as toplevel.
$selectQuery = $this->solr->service->createSelect($params);
$result = $this->solr->service->select($selectQuery);
@ -306,7 +325,7 @@ class tx_dlf_list implements ArrayAccess, Countable, Iterator, \TYPO3\CMS\Core\S
$record['metadata'] = $metadata;
} elseif (isset($record['subparts'][$resArray->id])) {
} else {
$highlightedDoc = !empty($highlighting) ? $highlighting->getResult($resArray->id) : NULL;
$highlight = !empty($highlightedDoc) ? $highlightedDoc->getField('fulltext')[0] : "";

View File

@ -1,4 +1,5 @@
<?php
/**
* (c) Kitodo. Key to digital objects e.V. <contact@kitodo.org>
*
@ -343,176 +344,63 @@ class tx_dlf_solr {
*
* @access public
*
* @param string $query: The search query
*
* @return tx_dlf_list The result list
*/
public function search($query = '') {
public function search() {
$toplevel = array ();
$checks = array ();
// Take over query parameters.
$params = $this->params;
// Restrict the fields to the required ones
$this->params['fields'] = 'uid,id,toplevel';
$params['filterquery'] = isset($params['filterquery']) ? $params['filterquery'] : array ();
// Get metadata configuration.
$result = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
'tx_dlf_metadata.index_name AS index_name',
'tx_dlf_metadata',
'tx_dlf_metadata.is_sortable=1 AND tx_dlf_metadata.pid='.intval($this->cPid).tx_dlf_helper::whereClause('tx_dlf_metadata'),
'',
'',
''
);
// Set some query parameters.
$params['start'] = 0;
$params['rows'] = 0;
$sorting = array ();
while ($resArray = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result)) {
$sorting[$resArray['index_name']] = $resArray['index_name'].'_sorting';
$this->params['fields'] .= ','.$sorting[$resArray['index_name']];
}
// Set additional query parameters.
$this->params['start'] = 0;
$this->params['rows'] = $this->limit;
// Set query.
$this->params['query'] = $query;
// Perform search.
$selectQuery = $this->service->createSelect($this->params);
// Perform search to determine the total number of hits without fetching them.
$selectQuery = $this->service->createSelect($params);
$results = $this->service->select($selectQuery);
$this->numberOfHits = $results->getNumFound();
// Keep track of relevance.
$i = 0;
// Restore query parameters
$params = $this->params;
$params['filterquery'] = isset($params['filterquery']) ? $params['filterquery'] : array ();
// Restrict the fields to the required ones.
$params['fields'] = 'uid,id';
// Extend filter query to get all documents with the same uids.
foreach ($params['filterquery'] as $key=>$value) {
$params['filterquery'][$key] = array ('query' => '{!join from=uid to=uid}'.$value['query']);
}
// Set filter query to just get toplevel documents.
$params['filterquery'][] = array ('query' => 'toplevel:true');
// Set join query to get all documents with the same uids.
$params['query'] = '{!join from=uid to=uid}'. $params['query'];
// Perform search to determine the total number of toplevel hits and fetch the required rows.
$selectQuery = $this->service->createSelect($params);
$results = $this->service->select($selectQuery);
$numberOfToplevelHits = $results->getNumFound();
// Process results.
foreach ($results as $doc) {
// Split toplevel documents from subparts.
if ($doc->toplevel) {
// Prepare document's metadata for sorting.
$docSorting = array ();
foreach ($sorting as $index_name => $solr_name) {
if (!empty($doc->$solr_name)) {
$docSorting[$index_name] = (is_array($doc->$solr_name) ? $doc->$solr_name[0] : $doc->$solr_name);
}
}
// Preserve relevance ranking.
if (!empty($toplevel[$doc->uid]['s']['relevance'])) {
$docSorting['relevance'] = $toplevel[$doc->uid]['s']['relevance'];
}
$toplevel[$doc->uid] = array (
'u' => $doc->uid,
'h' => '',
's' => $docSorting,
'p' => (!empty($toplevel[$doc->uid]['p']) ? $toplevel[$doc->uid]['p'] : array ())
);
} else {
$toplevel[$doc->uid]['p'][$doc->id] = array (
'u' => $doc->id,
'h' => ''
);
if (!in_array($doc->uid, $checks)) {
$checks[] = $doc->uid;
}
}
// Add relevance to sorting values.
if (empty($toplevel[$doc->uid]['s']['relevance'])) {
$toplevel[$doc->uid]['s']['relevance'] = str_pad($i, 6, '0', STR_PAD_LEFT);
}
$i++;
}
// Check if the toplevel documents have metadata.
foreach ($checks as $check) {
if (empty($toplevel[$check]['u'])) {
// Get information for toplevel document.
$result = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
'tx_dlf_documents.uid AS uid,tx_dlf_documents.metadata_sorting AS metadata_sorting',
'tx_dlf_documents',
'tx_dlf_documents.uid='.intval($check).tx_dlf_helper::whereClause('tx_dlf_documents'),
'',
'',
'1'
);
// Process results.
if ($GLOBALS['TYPO3_DB']->sql_num_rows($result)) {
$resArray = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result);
// Prepare document's metadata for sorting.
$sorting = unserialize($resArray['metadata_sorting']);
if (!empty($sorting['type']) && \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($sorting['type'])) {
$sorting['type'] = tx_dlf_helper::getIndexName($sorting['type'], 'tx_dlf_structures', $this->cPid);
}
if (!empty($sorting['owner']) && \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($sorting['owner'])) {
$sorting['owner'] = tx_dlf_helper::getIndexName($sorting['owner'], 'tx_dlf_libraries', $this->cPid);
}
if (!empty($sorting['collection']) && \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($sorting['collection'])) {
$sorting['collection'] = tx_dlf_helper::getIndexName($sorting['collection'], 'tx_dlf_collections', $this->cPid);
}
// Preserve relevance ranking.
if (!empty($toplevel[$check]['s']['relevance'])) {
$sorting['relevance'] = $toplevel[$check]['s']['relevance'];
}
$toplevel[$check] = array (
'u' => $resArray['uid'],
'h' => '',
's' => $sorting,
'p' => $toplevel[$check]['p']
);
} else {
// Clear entry if there is no (accessible) toplevel document.
unset ($toplevel[$check]);
}
}
$toplevel[$doc->id] = array (
'u' => $doc->uid,
'h' => '',
's' => '',
'p' => array ()
);
}
@ -530,13 +418,15 @@ class tx_dlf_solr {
'options' => array (
'source' => 'search',
'engine' => 'solr',
'select' => $query,
'select' => $this->params['query'],
'userid' => 0,
'params' => $this->params,
'core' => $this->core,
'pid' => $this->cPid,
'order' => 'relevance',
'order' => 'score',
'order.asc' => TRUE,
'numberOfHits' => $this->numberOfHits,
'numberOfToplevelHits' => $numberOfToplevelHits
)
);

View File

@ -424,6 +424,8 @@ class tx_dlf_collection extends tx_dlf_plugin {
$list->add(array_values($toplevel));
$listMetadata['options']['numberOfToplevelHits'] = count($list);
$list->metadata = $listMetadata;
$list->save();

View File

@ -14,6 +14,7 @@
*
* @author Sebastian Meyer <sebastian.meyer@slub-dresden.de>
* @author Henrik Lochmann <dev@mentalmotive.com>
* @author Frank Ulrich Weber <fuw@zeutschel.de>
* @package TYPO3
* @subpackage tx_dlf
* @access public
@ -64,7 +65,7 @@ class tx_dlf_listview extends tx_dlf_plugin {
protected function getPageBrowser() {
// Get overall number of pages.
$maxPages = intval(ceil(count($this->list) / $this->conf['limit']));
$maxPages = intval(ceil($this->list->metadata['options']['numberOfToplevelHits'] / $this->conf['limit']));
// Return empty pagebrowser if there is just one page.
if ($maxPages < 2) {
@ -378,7 +379,7 @@ class tx_dlf_listview extends tx_dlf_plugin {
// Add relevance sorting if this is a search result list.
if ($this->list->metadata['options']['source'] == 'search') {
$sorting .= '<option value="relevance"'.(($this->list->metadata['options']['order'] == 'relevance') ? ' selected="selected"' : '').'>'.$this->pi_getLL('relevance', '', TRUE).'</option>';
$sorting .= '<option value="score"'.(($this->list->metadata['options']['order'] == 'score') ? ' selected="selected"' : '').'>'.$this->pi_getLL('relevance', '', TRUE).'</option>';
}
@ -641,27 +642,92 @@ class tx_dlf_listview extends tx_dlf_plugin {
// Load the list.
$this->list = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tx_dlf_list');
// Sort the list if applicable.
if ((!empty($this->piVars['order']) && $this->piVars['order'] != $this->list->metadata['options']['order'])
|| (isset($this->piVars['asc']) && $this->piVars['asc'] != $this->list->metadata['options']['order.asc'])) {
$currentEntry = $this->piVars['pointer'] * $this->conf['limit'];
$lastEntry = ($this->piVars['pointer'] + 1) * $this->conf['limit'];
// Order list by given field.
$this->list->sort($this->piVars['order'], (boolean) $this->piVars['asc']);
// Check if it's a list of database records or Solr documents.
if ((!empty($this->list->metadata['options']['source']) && $this->list->metadata['options']['source'] == 'collection')
&& ((!empty($this->piVars['order']) && $this->piVars['order'] != $this->list->metadata['options']['order'])
|| (isset($this->piVars['asc']) && $this->piVars['asc'] != $this->list->metadata['options']['order.asc']))) {
// Update list's metadata.
// Order list by given field.
$this->list->sort($this->piVars['order'], (boolean) $this->piVars['asc']);
// Update list's metadata.
$listMetadata = $this->list->metadata;
$listMetadata['options']['order'] = $this->piVars['order'];
$listMetadata['options']['order.asc'] = (boolean) $this->piVars['asc'];
$this->list->metadata = $listMetadata;
// Save updated list.
$this->list->save();
// Reset pointer.
$this->piVars['pointer'] = 0;
} elseif (!empty($this->list->metadata['options']['source']) && $this->list->metadata['options']['source'] == 'search') {
// Update list's metadata
$listMetadata = $this->list->metadata;
$listMetadata['options']['order'] = $this->piVars['order'];
// Sort the list if applicable.
if ((!empty($this->piVars['order']) && $this->piVars['order'] != $listMetadata['options']['order'])
|| (isset($this->piVars['asc']) && $this->piVars['asc'] != $listMetadata['options']['order.asc'])) {
$listMetadata['options']['order.asc'] = (boolean) $this->piVars['asc'];
// Update list's metadata.
$listMetadata['options']['params']['sort'] = array ($this->piVars['order']."_sorting" => (boolean) $this->piVars['asc']?'asc':'desc');
$listMetadata['options']['order'] = $this->piVars['order'];
$listMetadata['options']['order.asc'] = (boolean) $this->piVars['asc'];
// Reset pointer.
$this->piVars['pointer'] = 0;
}
// Set some query parameters
$listMetadata['options']['params']['start'] = $currentEntry;
$listMetadata['options']['params']['rows'] = $this->conf['limit'];
// Search only if the query params have changed.
if ($listMetadata['options']['params'] != $this->list->metadata['options']['params']) {
// Instantiate search object.
$solr = tx_dlf_solr::getInstance($this->list->metadata['options']['core']);
if (!$solr->ready) {
if (TYPO3_DLOG) {
\TYPO3\CMS\Core\Utility\GeneralUtility::devLog('[tx_dlf_listview->main('.$content.', [data])] Apache Solr not available', $this->extKey, SYSLOG_SEVERITY_ERROR, $conf);
}
return $content;
}
// Set search parameters.
$solr->cPid = $listMetadata['options']['pid'];
$solr->params = $listMetadata['options']['params'];
// Perform search.
$this->list = $solr->search();
}
// Add list description
$listMetadata['description'] = '<p class="tx-dlf-search-numHits">'.htmlspecialchars(sprintf($this->pi_getLL('hits', ''), $this->list->metadata['options']['numberOfHits'], $this->list->metadata['options']['numberOfToplevelHits'])).'</p>';
$this->list->metadata = $listMetadata;
// Save updated list.
$this->list->save();
// Reset pointer.
$this->piVars['pointer'] = 0;
$currentEntry = 0;
$lastEntry = $this->conf['limit'];
}
@ -681,7 +747,7 @@ class tx_dlf_listview extends tx_dlf_plugin {
$subpartArray['subentry'] = $this->cObj->getSubpart($this->template, '###SUBENTRY###');
// Set some variable defaults.
if (!empty($this->piVars['pointer']) && (($this->piVars['pointer'] * $this->conf['limit']) + 1) <= count($this->list)) {
if (!empty($this->piVars['pointer']) && (($this->piVars['pointer'] * $this->conf['limit']) + 1) <= $this->list->metadata['options']['numberOfToplevelHits']) {
$this->piVars['pointer'] = max(intval($this->piVars['pointer']), 0);
@ -694,9 +760,6 @@ class tx_dlf_listview extends tx_dlf_plugin {
// Load metadata configuration.
$this->loadConfig();
$currentEntry = $this->piVars['pointer'] * $this->conf['limit'];
$lastEntry = ($this->piVars['pointer'] + 1) * $this->conf['limit'];
for ($currentEntry, $lastEntry; $currentEntry < $lastEntry; $currentEntry++) {
if (empty($this->list[$currentEntry])) {
@ -727,7 +790,10 @@ class tx_dlf_listview extends tx_dlf_plugin {
if ($currentEntry) {
$markerArray['###COUNT###'] = htmlspecialchars(sprintf($this->pi_getLL('count'), ($this->piVars['pointer'] * $this->conf['limit']) + 1, $currentEntry, count($this->list)));
$currentEntry = ($this->piVars['pointer'] * $this->conf['limit']) + 1;
$lastEntry = ($this->piVars['pointer'] * $this->conf['limit']) + $this->conf['limit'];
$markerArray['###COUNT###'] = htmlspecialchars(sprintf($this->pi_getLL('count'), $currentEntry, $lastEntry < $this->list->metadata['options']['numberOfToplevelHits'] ? $lastEntry : $this->list->metadata['options']['numberOfToplevelHits'], $this->list->metadata['options']['numberOfToplevelHits']));
} else {

View File

@ -36,6 +36,7 @@
<label index="direction.desc">descending</label>
<label index="noTitle">[no title]</label>
<label index="addBasket">Add to basket</label>
<label index="hits">%d hits found in %d documents.</label>
</languageKey>
<languageKey index="de" type="array">
<label index="tt_content.pi_flexform.sheet_general">Einstellungen</label>
@ -59,6 +60,7 @@
<label index="direction.desc">absteigend</label>
<label index="noTitle">[kein Titel]</label>
<label index="addBasket">Zum Warenkorb hinzufügen</label>
<label index="hits">Die Suche ergab %d Treffer in %d Dokumenten.</label>
</languageKey>
</data>
</T3locallang>

View File

@ -14,6 +14,7 @@
*
* @author Sebastian Meyer <sebastian.meyer@slub-dresden.de>
* @author Henrik Lochmann <dev@mentalmotive.com>
* @author Frank Ulrich Weber <fuw@zeutschel.de>
* @package TYPO3
* @subpackage tx_dlf
* @access public
@ -653,26 +654,43 @@ class tx_dlf_search extends tx_dlf_plugin {
}
// Set some query parameters.
$params['query'] = $query;
$params['start'] = 0;
$params['rows'] = 0;
$params['sort'] = array ('score' => 'desc');
// Instantiate search object.
$solr = tx_dlf_solr::getInstance($this->conf['solrcore']);
if (!$solr->ready) {
if (TYPO3_DLOG) {
\TYPO3\CMS\Core\Utility\GeneralUtility::devLog('[tx_dlf_search->main('.$content.', [data])] Apache Solr not available', $this->extKey, SYSLOG_SEVERITY_ERROR, $conf);
}
return $content;
}
// Set search parameters.
$solr->limit = max(intval($this->conf['limit']), 1);
$solr->cPid = $this->conf['pages'];
$solr->params = $params;
// Perform search.
$results = $solr->search($query);
$list = $solr->search();
$results->metadata = array (
$list->metadata = array (
'label' => $label,
'description' => '<p class="tx-dlf-search-numHits">'.htmlspecialchars(sprintf($this->pi_getLL('hits', ''), $solr->numberOfHits, count($results))).'</p>',
'thumbnail' => '',
'searchString' => $this->piVars['query'],
'fulltextSearch' => (!empty($this->piVars['fulltext']) ? '1' : '0'),
'options' => $results->metadata['options']
'options' => $list->metadata['options']
);
$results->save();
$list->save();
// Clean output buffer.
\TYPO3\CMS\Core\Utility\GeneralUtility::cleanOutputBuffers();
@ -686,13 +704,13 @@ class tx_dlf_search extends tx_dlf_plugin {
}
// Jump directly to the page view, if there is only one result and it is configured
if ($results->count() == 1 && !empty($this->conf['showSingleResult'])) {
if ($list->metadata['options']['numberOfHits'] == 1 && !empty($this->conf['showSingleResult'])) {
$linkConf['parameter'] = $this->conf['targetPidPageView'];
$additionalParams['id'] = $results->current()['uid'];
$additionalParams['highlight_word'] = preg_replace('/\s\s+/', ';', $results->metadata['searchString']);
$additionalParams['page'] = count($results[0]['subparts']) == 1 ? $results[0]['subparts'][0]['page'] : 1;
$additionalParams['id'] = $list->current()['uid'];
$additionalParams['highlight_word'] = preg_replace('/\s\s+/', ';', $list->metadata['searchString']);
$additionalParams['page'] = count($list[0]['subparts']) == 1 ? $list[0]['subparts'][0]['page'] : 1;
} else {

View File

@ -78,18 +78,6 @@
</config>
</TCEforms>
</solrcore>
<limit>
<TCEforms>
<displayCond>FIELD:pages:REQ:true</displayCond>
<exclude>1</exclude>
<label>LLL:EXT:dlf/plugins/search/locallang.xml:tt_content.pi_flexform.limit</label>
<config>
<type>input</type>
<eval>num,int</eval>
<default>50000</default>
</config>
</TCEforms>
</limit>
<extendedSlotCount>
<TCEforms>
<displayCond>FIELD:pages:REQ:true</displayCond>

View File

@ -20,7 +20,6 @@
<label index="tt_content.pi_flexform.fulltext.yes">yes</label>
<label index="tt_content.pi_flexform.fulltext.no">no</label>
<label index="tt_content.pi_flexform.solrcore">Solr Core</label>
<label index="tt_content.pi_flexform.limit">Maximum results</label>
<label index="tt_content.pi_flexform.extSearch.slotCount">Number of search slots</label>
<label index="tt_content.pi_flexform.extSearch.fields">Fields for extended search</label>
<label index="tt_content.pi_flexform.collections">Restrict search to these collections</label>
@ -47,7 +46,6 @@
<label index="search">Search</label>
<label index="for"> for "%s"</label>
<label index="in"> in "%s"</label>
<label index="hits">%d hits found in %d documents.</label>
<label index="AND">and</label>
<label index="OR">or</label>
<label index="NOT">not</label>
@ -59,7 +57,6 @@
<label index="tt_content.pi_flexform.fulltext.yes">ja</label>
<label index="tt_content.pi_flexform.fulltext.no">nein</label>
<label index="tt_content.pi_flexform.solrcore">Solr Kern</label>
<label index="tt_content.pi_flexform.limit">Maximale Ergebnismenge</label>
<label index="tt_content.pi_flexform.extSearch.slotCount">Anzahl Suchfelder</label>
<label index="tt_content.pi_flexform.extSearch.fields">Felder für erweiterte Suche</label>
<label index="tt_content.pi_flexform.collections">Suche auf diese Kollektionen einschränken</label>
@ -86,7 +83,6 @@
<label index="search">Suche</label>
<label index="for"> nach "%s"</label>
<label index="in"> in "%s"</label>
<label index="hits">Die Suche ergab %d Treffer in %d Dokumenten.</label>
<label index="AND">und</label>
<label index="OR">oder</label>
<label index="NOT">nicht</label>