Version 4.5.0

This commit is contained in:
Neo committed 2025-12-19 05:51:58 -08:00
1 parent 1a0c7fd3c2
commit f79dcf067a
3791 files changed
+248032 -76372

No files matched your search

+98 -15
View File
@@ -11,7 +11,7 @@
namespace IPS\Content\Search\Mysql;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -56,7 +56,7 @@ class _Index extends \IPS\Content\Search\Index
{
/* Get the index data */
$indexData = $this->indexData( $object );
$class = get_class( $object );
$class = \get_class( $object );
/* If we got the data... */
if( $indexData )
@@ -167,7 +167,7 @@ class _Index extends \IPS\Content\Search\Index
}
/* Add add them back if we have any */
if ( count( $tags ) )
if ( \count( $tags ) )
{
foreach( $tags as $tag )
{
@@ -229,7 +229,7 @@ class _Index extends \IPS\Content\Search\Index
public function getIndexId( \IPS\Content\Searchable $object )
{
$databaseColumnId = $object::$databaseColumnId;
return \IPS\Db::i()->select( 'index_id', 'core_search_index', array( 'index_class=? AND index_object_id=?', get_class( $object ),$object->$databaseColumnId ) )->first();
return \IPS\Db::i()->select( 'index_id', 'core_search_index', array( 'index_class=? AND index_object_id=?', \get_class( $object ),$object->$databaseColumnId ) )->first();
}
/**
@@ -240,7 +240,7 @@ class _Index extends \IPS\Content\Search\Index
*/
public function removeFromSearchIndex( \IPS\Content\Searchable $object )
{
$class = get_class( $object );
$class = \get_class( $object );
$idColumn = $class::$databaseColumnId;
/* Tags */
@@ -266,7 +266,7 @@ class _Index extends \IPS\Content\Search\Index
try
{
$this->resetLastComment( $classes, $object->mapped('item') );
$this->resetLastComment( $classes, $object->mapped('item'), $object->$idColumn );
}
catch( \Exception $ex ) { }
@@ -300,6 +300,41 @@ class _Index extends \IPS\Content\Search\Index
\IPS\Db::i()->delete( 'core_search_index', array( 'index_class=? AND index_item_id=?', $reviewClass, $object->$idColumn ) );
}
}
/**
* Direct removal from the search index - only used when we don't need to perform ancillary cleanup (i.e. orphaned data)
*
* @param string $class Class
* @param int $id ID
* @return void
*/
public function directIndexRemoval( $class, $id )
{
/* Tags */
$this->_deleteTagsFromIndex( $class, $id );
\IPS\Db::i()->delete( 'core_search_index', array( 'index_class=? AND index_object_id=?', $class, $id ) );
/* If this was a comment, we really need to reset the index_is_last_comment flag if it was set */
if ( is_subclass_of( $class, 'IPS\Content\Item' ) )
{
\IPS\Db::i()->delete( 'core_search_index_item_map', array( 'index_class=? AND index_item_id=?', $class, $id ) );
}
if ( isset( $class::$commentClass ) )
{
$commentClass = $class::$commentClass;
$this->_deleteTagsFromIndex( $commentClass, $id );
\IPS\Db::i()->delete( 'core_search_index', array( 'index_class=? AND index_item_id=?', $commentClass, $id ) );
}
if ( isset( $class::$reviewClass ) )
{
$reviewClass = $class::$reviewClass;
$this->_deleteTagsFromIndex( $reviewClass, $id );
\IPS\Db::i()->delete( 'core_search_index', array( 'index_class=? AND index_item_id=?', $reviewClass, $id ) );
}
}
/**
* Return the index IDs associated with this class and $id
@@ -319,7 +354,7 @@ class _Index extends \IPS\Content\Search\Index
$ids = FALSE;
}
if ( is_array( $ids ) and count( $ids ) < 1000 )
if ( \is_array( $ids ) and \count( $ids ) < 1000 )
{
\IPS\Db::i()->delete( 'core_search_index_tags', array( \IPS\Db::i()->in( 'index_id', $ids ) ) );
}
@@ -328,6 +363,52 @@ class _Index extends \IPS\Content\Search\Index
\IPS\Db::i()->delete( 'core_search_index_tags', array( 'index_id IN( ? )', \IPS\Db::i()->select( 'index_id', 'core_search_index', array( 'index_class=? AND index_item_id=?', $class, $id ) ) ) );
}
}
/**
* Removes all content for a specific application from the index (for example, when uninstalling).
*
* @param \IPS\Application $application The application
* @return void
*/
public function removeApplicationContent( \IPS\Application $application )
{
$classes = array();
foreach ( $application->extensions( 'core', 'ContentRouter' ) as $router )
{
foreach( $router->classes AS $class )
{
if ( is_subclass_of( $class, 'IPS\Content\Searchable' ) )
{
$classes[] = $class;
if ( isset( $class::$commentClass ) )
{
$commentClass = $class::$commentClass;
if ( is_subclass_of( $commentClass, 'IPS\Content\Searchable' ) )
{
$classes[] = $commentClass;
}
}
if ( isset( $class::$reviewClass ) )
{
$reviewClass = $class::$reviewClass;
if ( is_subclass_of( $reviewClass, 'IPS\Content\Searchable' ) )
{
$classes[] = $reviewClass;
}
}
}
}
}
$where = array( \IPS\Db::i()->in( 'index_class', $classes ) );
\IPS\Db::i()->delete( 'core_search_index_item_map', $where );
\IPS\Db::i()->delete( 'core_search_index_tags', array( 'index_id IN( ? )', \IPS\Db::i()->select( 'index_id', 'core_search_index', $where ) ) );
\IPS\Db::i()->delete( 'core_search_index', $where );
}
/**
* Removes all content for a classs
@@ -394,7 +475,7 @@ class _Index extends \IPS\Content\Search\Index
{
$update['index_container_id'] = $newContainer;
if ( $itemClass = ( in_array( 'IPS\Content\Item', class_parents( $class ) ) ? $class : $class::$itemClass ) and $containerClass = $itemClass::$containerNodeClass and \IPS\IPS::classUsesTrait( $containerClass, 'IPS\Content\ClubContainer' ) and $clubIdColumn = $containerClass::clubIdColumn() )
if ( $itemClass = ( \in_array( 'IPS\Content\Item', class_parents( $class ) ) ? $class : $class::$itemClass ) and $containerClass = $itemClass::$containerNodeClass and \IPS\IPS::classUsesTrait( $containerClass, 'IPS\Content\ClubContainer' ) and $clubIdColumn = $containerClass::clubIdColumn() )
{
try
{
@@ -415,7 +496,7 @@ class _Index extends \IPS\Content\Search\Index
$update['index_item_author'] = $newItemAuthorId;
}
if ( count( $update ) )
if ( \count( $update ) )
{
\IPS\Db::i()->update( 'core_search_index', $update, $where );
}
@@ -423,7 +504,7 @@ class _Index extends \IPS\Content\Search\Index
{
$addAuthorToPermissionsWhere = $where;
$addAuthorToPermissionsWhere[] = array( 'index_author<>0' );
\IPS\Db::i()->update( 'core_search_index', "index_permissions = CONCAT( index_permissions, ',m.', index_author )", $addAuthorToPermissionsWhere );
\IPS\Db::i()->update( 'core_search_index', "index_permissions = CONCAT( index_permissions, ',m', index_author )", $addAuthorToPermissionsWhere );
}
if ( $newHiddenStatus !== NULL )
@@ -486,8 +567,8 @@ class _Index extends \IPS\Content\Search\Index
{
$prefix = \IPS\Db::i()->prefix;
\IPS\Db::i()->query( "DELETE {$prefix}core_search_index_item_map FROM {$prefix}core_search_index_item_map, {$prefix}core_search_index WHERE {$prefix}core_search_index_item_map.index_item_id={$prefix}core_search_index.index_item_id AND index_date_updated < " . $cutoff->getTimestamp() );
\IPS\Db::i()->query( "DELETE {$prefix}core_search_index_tags FROM {$prefix}core_search_index_tags, {$prefix}core_search_index WHERE {$prefix}core_search_index_tags.index_id={$prefix}core_search_index.index_id AND index_date_updated < " . $cutoff->getTimestamp() );
\IPS\Db::i()->delete( array( 'core_search_index_item_map', 'core_search_index' ), array( "core_search_index_item_map.index_item_id=core_search_index.index_item_id AND (core_search_index.index_id=core_search_index.index_item_index_id AND index_date_updated < ?)", $cutoff->getTimestamp() ), NULL, NULL, NULL, 'core_search_index_item_map' );
\IPS\Db::i()->delete( array( 'core_search_index_tags', 'core_search_index' ), array( "core_search_index_tags.index_id=core_search_index.index_id AND (core_search_index.index_id=core_search_index.index_item_index_id AND index_date_updated < ?)", $cutoff->getTimestamp() ), NULL, NULL, NULL, 'core_search_index_tags' );
\IPS\Db::i()->delete( 'core_search_index', array( 'index_date_updated < ?', $cutoff->getTimestamp() ) );
}
else
@@ -503,9 +584,10 @@ class _Index extends \IPS\Content\Search\Index
*
* @param array $classes The classes (when first post is required, this is typically just \IPS\forums\Topic\Post but for others, it will be both item and comment classes)
* @param int|NULL $indexItemId The index item ID
* @param int|NULL $ignoreId ID to ignore because it is being removed
* @return void
*/
public function resetLastComment( $classes, $indexItemId )
public function resetLastComment( $classes, $indexItemId, $ignoreId = NULL )
{
\IPS\Db::i()->update( 'core_search_index', array( 'index_is_last_comment' => 0 ), array( \IPS\Db::i()->in( 'index_class', $classes ) . ' AND index_item_id=?', $indexItemId ) );
@@ -537,14 +619,15 @@ class _Index extends \IPS\Content\Search\Index
*
* @param array $itemIndexIds Item index IDs
* @param \IPS\Member $member The member
* @param int|NULL $limit Number of results to return
* @return array
*/
public function hashesWithPermission( array $hashes, \IPS\Member $member )
public function hashesWithPermission( array $hashes, \IPS\Member $member, $limit = NULL )
{
return iterator_to_array( \IPS\Db::i()->select( 'index_class_type_id_hash', array( 'core_search_index', 'si' ), array(
array( "( si.index_permissions = '*' OR " . \IPS\Db::i()->findInSet( 'si.index_permissions', \IPS\Member::loggedIn()->permissionArray() ) . ' AND si.index_hidden=0 )' ),
array( \IPS\Db::i()->in( 'si.index_class_type_id_hash', $hashes ) )
) )->setKeyField('index_class_type_id_hash') );
), NULL, $limit )->setKeyField('index_class_type_id_hash') );
}
/**
+91 -68
View File
@@ -11,7 +11,7 @@
namespace IPS\Content\Search\Mysql;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -72,7 +72,13 @@ class _Query extends \IPS\Content\Search\Query
* @see filterByItemsIPostedIn()
*/
protected $filterByItemsIPostedIn = FALSE;
/**
* @brief Filter by unread items?
* @see filterByUnread()
*/
protected $filterByUnread = FALSE;
/**
* Filter by multiple content types
*
@@ -100,7 +106,7 @@ class _Query extends \IPS\Content\Search\Query
}
/* Set the class */
if ( count( $filter->classes ) > 1 )
if ( \count( $filter->classes ) > 1 )
{
$clause[] = \IPS\Db::i()->in( 'index_class', $filter->classes );
}
@@ -134,7 +140,14 @@ class _Query extends \IPS\Content\Search\Query
/* Are we excluding certain container classes? */
if( $filter->containerClasses !== NULL )
{
$clause[] = \IPS\Db::i()->in( 'index_container_class', $filter->containerClasses );
if( $filter->containerClassExclusions !== NULL )
{
$clause[] = '(' . \IPS\Db::i()->in( 'index_container_class', $filter->containerClasses ) . ' OR ' . \IPS\Db::i()->in( 'index_class', $filter->containerClassExclusions ) . ')';
}
else
{
$clause[] = \IPS\Db::i()->in( 'index_container_class', $filter->containerClasses );
}
}
/* Set the item IDs */
@@ -157,19 +170,19 @@ class _Query extends \IPS\Content\Search\Query
if ( $filter->minimumComments )
{
$this->select[ $class::$databaseTable . '_comments' ] = $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['num_comments'];
$clause[] = $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['num_comments'] . '>=' . intval( $filter->minimumComments );
$clause[] = $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['num_comments'] . '>=' . \intval( $filter->minimumComments );
}
if ( $filter->minimumReviews )
{
$this->select[ $class::$databaseTable . '_reviews' ] = $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['num_reviews'];
$clause[] = $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['num_reviews'] . '>=' . intval( $filter->minimumReviews );
$clause[] = $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['num_reviews'] . '>=' . \intval( $filter->minimumReviews );
}
if ( $filter->minimumViews )
{
$this->select[ $class::$databaseTable . '_views' ] = $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['views'];
$clause[] = $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['views'] . '>=' . intval( $filter->minimumViews );
$clause[] = $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnMap['views'] . '>=' . \intval( $filter->minimumViews );
}
}
@@ -186,7 +199,7 @@ class _Query extends \IPS\Content\Search\Query
}
/* Put it together */
if ( count( $clause ) > 1 )
if ( \count( $clause ) > 1 )
{
$filters[] = '( ' . implode( ' AND ', $clause ) . ' )';
}
@@ -211,7 +224,7 @@ class _Query extends \IPS\Content\Search\Query
*/
public function filterByAuthor( $author )
{
if ( is_array( $author ) )
if ( \is_array( $author ) )
{
$this->where[] = array( \IPS\Db::i()->in( 'index_author', $author ) );
}
@@ -236,7 +249,7 @@ class _Query extends \IPS\Content\Search\Query
{
$this->where[] = 'index_club_id IS NULL';
}
if ( is_array( $club ) )
if ( \is_array( $club ) )
{
$this->where[] = array( \IPS\Db::i()->in( 'index_club_id', $club ) );
}
@@ -244,6 +257,26 @@ class _Query extends \IPS\Content\Search\Query
{
$this->where[] = array( 'index_club_id=?', $club instanceof \IPS\Member\Club ? $club->id : $club );
}
/* Get the list of valid classes */
foreach ( \IPS\Application::allExtensions( 'core', 'ContentRouter', FALSE ) as $object )
{
foreach ( $object->classes as $class )
{
if ( \in_array( 'IPS\Content\Item', class_parents( $class ) ) )
{
$classesChecked[] = $class;
}
}
}
/* Give content item classes a chance to inspect and manipulate filters */
$filters = array();
foreach( $classesChecked as $itemClass )
{
$itemClass::searchEngineFiltering( $filters, $this );
}
return $this;
}
@@ -261,14 +294,7 @@ class _Query extends \IPS\Content\Search\Query
if ( $count )
{
if ( $count > 500 )
{
$this->where[] = array( '( index_author=? OR ( index_class=? AND index_container_id=? ) )', $member->member_id, 'IPS\core\Statuses\Status', $member->member_id );
}
else
{
$this->where[] = array( '( index_author=? OR (' . \IPS\Db::i()->in( 'index_id', iterator_to_array( \IPS\Db::i()->select( 'index_id', 'core_search_index', array( 'index_class=? AND index_container_id=?', 'IPS\core\Statuses\Status', $member->member_id ) ) ) ) . ') )', $member->member_id );
}
$this->where[] = array( '( index_author=? OR ( index_class=? AND index_container_id=? ) )', $member->member_id, 'IPS\core\Statuses\Status', $member->member_id );
$filterResult = $this;
}
@@ -282,7 +308,7 @@ class _Query extends \IPS\Content\Search\Query
{
foreach ( $object->classes as $class )
{
if ( in_array( 'IPS\Content\Item', class_parents( $class ) ) )
if ( \in_array( 'IPS\Content\Item', class_parents( $class ) ) )
{
$classesChecked[] = $class;
}
@@ -428,7 +454,7 @@ class _Query extends \IPS\Content\Search\Query
{
$followedItems[ $follow['follow_area'] ][] = $follow['follow_rel_id'];
}
else if( in_array( $follow['follow_area'], $containerCase ) )
else if( \in_array( $follow['follow_area'], $containerCase ) )
{
$followedContainers[ $follow['follow_area'] ][] = $follow['follow_rel_id'];
}
@@ -463,13 +489,13 @@ class _Query extends \IPS\Content\Search\Query
{
$followed = iterator_to_array( \IPS\Db::i()->select( 'follow_rel_id', 'core_follow', array( 'follow_app=? AND follow_area=? AND follow_member_id=?', 'core', 'member', $this->member->member_id ), 'follow_rel_id asc', array( 0, 501 ) ) );
if ( count( $followed ) == 501 )
if ( \count( $followed ) == 501 )
{
/* Assume we have loads of matches, so do a full query */
$where[] = 'index_author IN(?)';
$params[] = \IPS\Db::i()->select( 'follow_rel_id', 'core_follow', array( 'follow_app=? AND follow_area=? AND follow_member_id=?', 'core', 'member', $this->member->member_id ) );
}
else if ( count( $followed ) )
else if ( \count( $followed ) )
{
/* IN is not a SIN. It's been a long day */
$where[] = \IPS\Db::i()->in( 'index_author', $followed );
@@ -488,7 +514,7 @@ class _Query extends \IPS\Content\Search\Query
}
/* Put it all together */
if ( count( $where ) )
if ( \count( $where ) )
{
$this->where[] = array_merge( array( '( ' . implode( ' OR ', $where ) . ' )' ), $params );
}
@@ -570,11 +596,13 @@ class _Query extends \IPS\Content\Search\Query
}
}
if ( count( $where ) )
if ( \count( $where ) )
{
/* Put it all together */
$this->where[] = array_merge( array( '( ' . implode( ' OR ', $where ) . ' )' ), $params );
}
$this->filterByUnread = TRUE;
}
/**
@@ -595,7 +623,7 @@ class _Query extends \IPS\Content\Search\Query
$where = array();
$unreadWheres = array();
$containerIds = array();
$containerClass = ( $class::$containerNodeClass ) ? $class::$containerNodeClass : NULL;
$containerClass = isset( $class::$containerNodeClass ) ? $class::$containerNodeClass : NULL;
foreach( $resetTimes as $containerId => $timestamp )
{
@@ -615,7 +643,7 @@ class _Query extends \IPS\Content\Search\Query
$items = $this->member->markersItems( $application, \IPS\Content\Item::makeMarkerKey( $containerId ) );
if ( count( $items ) )
if ( \count( $items ) )
{
foreach( $items as $mid => $mtime )
{
@@ -625,10 +653,10 @@ class _Query extends \IPS\Content\Search\Query
in it's old location, with the previously 'read' time. In this circumstance, we need
to only use more recent read time, otherwise the topic may be incorrectly included
in the results */
if ( in_array( $mid, $markers ) )
if ( \in_array( $mid, $markers ) )
{
$_key = array_search( $mid, $markers );
$_mtime = intval( mb_substr( $_key, 0, mb_strpos( $_key, '.' ) ) );
$_mtime = \intval( mb_substr( $_key, 0, mb_strpos( $_key, '.' ) ) );
if ( $_mtime < $mtime )
{
unset( $markers[ $_key ] );
@@ -646,25 +674,25 @@ class _Query extends \IPS\Content\Search\Query
}
}
if( count( $containerIds ) )
if( \count( $containerIds ) )
{
$unreadWheres[] = "( index_date_updated > " . intval( $this->member->marked_site_read ) . " AND ( index_container_id NOT IN(" . implode( ',', $containerIds ) . ") ) )";
$unreadWheres[] = "( index_date_updated > " . \intval( $this->member->marked_site_read ) . " AND ( index_container_id NOT IN(" . implode( ',', $containerIds ) . ") ) )";
}
else
{
$unreadWheres[] = "( index_date_updated > " . intval( $this->member->marked_site_read ) . ")";
$unreadWheres[] = "( index_date_updated > " . \intval( $this->member->marked_site_read ) . ")";
}
if( count( $unreadWheres ) )
if( \count( $unreadWheres ) )
{
$where[] = array( "(" . implode( " OR ", $unreadWheres ) . ")" );
}
if ( count( $markers ) )
if ( \count( $markers ) )
{
/* Avoid packet issues */
krsort( $markers );
$useIds = array_flip( array_slice( $markers, 0, 500, TRUE ) );
$useIds = array_flip( \array_slice( $markers, 0, 500, TRUE ) );
$select = '';
$from = '';
$notIn = array();
@@ -675,7 +703,7 @@ class _Query extends \IPS\Content\Search\Query
{
if ( isset( $class::$databaseColumnMap[ $k ] ) )
{
if ( is_array( $class::$databaseColumnMap[ $k ] ) )
if ( \is_array( $class::$databaseColumnMap[ $k ] ) )
{
foreach ( $class::$databaseColumnMap[ $k ] as $v )
{
@@ -688,7 +716,7 @@ class _Query extends \IPS\Content\Search\Query
}
}
}
$dateColumnExpression = count( $dateColumns ) > 1 ? ( 'GREATEST(' . implode( ',', $dateColumns ) . ')' ) : array_pop( $dateColumns );
$dateColumnExpression = \count( $dateColumns ) > 1 ? ( 'GREATEST(' . implode( ',', $dateColumns ) . ')' ) : array_pop( $dateColumns );
foreach( \IPS\Db::i()->select( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnId. ' as _id, ' . $dateColumnExpression . ' as _date', $class::$databaseTable, \IPS\Db::i()->in( $class::$databasePrefix . $class::$databaseColumnId, array_keys( $useIds ) ) ) as $row )
{
@@ -697,12 +725,12 @@ class _Query extends \IPS\Content\Search\Query
if ( $useIds[ $row['_id'] ] >= $row['_date'] )
{
/* Still read */
$notIn[] = intval( $row['_id'] );
$notIn[] = \intval( $row['_id'] );
}
}
}
if ( count( $notIn ) )
if ( \count( $notIn ) )
{
$where[] = array( "( index_item_id NOT IN (" . implode( ',', $notIn ) . ") )" );
}
@@ -759,11 +787,11 @@ class _Query extends \IPS\Content\Search\Query
*/
public function setHiddenFilter( $statuses )
{
if ( is_null( $statuses ) )
if ( \is_null( $statuses ) )
{
$this->hiddenClause = NULL;
}
if ( is_array( $statuses ) )
if ( \is_array( $statuses ) )
{
$this->hiddenClause = array( \IPS\Db::i()->in( 'index_hidden', $statuses ) );
}
@@ -856,7 +884,7 @@ class _Query extends \IPS\Content\Search\Query
$tagIds = iterator_to_array( \IPS\Db::i()->select( 'index_id', 'core_search_index_tags', array( \IPS\Db::i()->in( 'index_tag', $tags ) ), 'index_id asc', array( 0, 501 ) ) );
/* Now, if we have 501 results, then we have to assume there are more, so the join is required */
if ( count( $tagIds ) == 501 )
if ( \count( $tagIds ) == 501 )
{
$tagWhere = array( $itemsOnlyTagSearch . 'index_item_index_id IN ( ? )', \IPS\Db::i()->select( 'index_id', 'core_search_index_tags', array( \IPS\Db::i()->in( 'index_tag', $tags ) ) ) );
}
@@ -937,16 +965,16 @@ class _Query extends \IPS\Content\Search\Query
}
}
$results = iterator_to_array( \IPS\Db::i()->select( 'index_item_id, index_class', array( 'core_search_index_item_map', 'sub' ), array( array( 'index_author_id=' . intval( $this->member->member_id ) . ' AND ' . \IPS\Db::i()->in( 'index_class', $classes ) ) ), 'index_item_id desc', array( 0, 501 ) ) );
$results = iterator_to_array( \IPS\Db::i()->select( 'index_item_id, index_class', array( 'core_search_index_item_map', 'sub' ), array( array( 'index_author_id=' . \intval( $this->member->member_id ) . ' AND ' . \IPS\Db::i()->in( 'index_class', $classes ) ) ), 'index_item_id desc', array( 0, 501 ) ) );
if ( count( $results ) == 501 )
if ( \count( $results ) == 501 )
{
/* Assume we have loads of matches, so do a full query */
$subClause = array( array( 'sub.index_item_id=main.index_item_id AND index_author_id=' . intval( $this->member->member_id ) . ' AND ' . $this->makeEmbeddedIfClauseForSearchMapTable('sub.index_class') ) );
$subClause = array( array( 'sub.index_item_id=main.index_item_id AND index_author_id=' . \intval( $this->member->member_id ) . ' AND ' . $this->makeEmbeddedIfClauseForSearchMapTable('sub.index_class') ) );
$query = \IPS\Db::i()->select( 'index_item_id', array( 'core_search_index_item_map', 'sub' ), $subClause );
$where[] = array( 'EXISTS(?)', $query );
}
else if ( count( $results ) )
else if ( \count( $results ) )
{
$classIds = array();
foreach( $results as $result )
@@ -1027,7 +1055,7 @@ class _Query extends \IPS\Content\Search\Query
}
}
if ( count( $stack ) )
if ( \count( $stack ) )
{
$if = '';
foreach( $stack as $commentClass => $class )
@@ -1035,7 +1063,7 @@ class _Query extends \IPS\Content\Search\Query
$if .= " IF( main.index_class='" . \IPS\Db::i()->escape_string( $commentClass ) . "', '" . \IPS\Db::i()->escape_string( $class ) . "', ";
}
$if .= 'main.index_class' . str_repeat( ' )', count( $stack ) );
$if .= 'main.index_class' . str_repeat( ' )', \count( $stack ) );
}
return $column . '=' . ( $if == '' ? $column : $if );
@@ -1066,25 +1094,14 @@ class _Query extends \IPS\Content\Search\Query
foreach ( static::termAsWordsArray( $term ) as $word )
{
/* Operators are not allowed */
$word = preg_replace( '/^([\+\-~<>]){1,}/', '', $word );
$word = preg_replace( '/([\+\-~\*<>]){2,}$/', '$1', $word );
/* +* and +- are not allowed anywhere in the word */
$word = str_replace( array( '+*', '+-', '+' ), '', $word );
/* Trailing + or -s are not allowed */
$word = rtrim( $word, '+-' );
/* Nor is @ or parenthesis (while paranthesis can be used to group words and apply operators to the group,
(e.g. "+apple +(>turnover <strudel)") - it's unlikely a user intends this behaviour) */
$word = str_replace( array( '(', ')' ), '', str_replace( '@', ' ', $word ) );
$word = preg_replace( '/[+\-><\(\)~*\"@%]+/', '', $word );
if ( $word )
{
$words[] = $word;
}
}
$booleanTerm = implode( ' +', $words );
/* Return */
@@ -1119,7 +1136,7 @@ class _Query extends \IPS\Content\Search\Query
foreach ( static::termAsWordsArray( $term ) as $word )
{
/* Add the default operator */
if ( $defaultOperator and !in_array( mb_substr( $word, 0, 1 ), array( '+', '-', '~', '<', '>' ) ) )
if ( $defaultOperator and !\in_array( mb_substr( $word, 0, 1 ), array( '+', '-', '~', '<', '>' ) ) )
{
/* Clear out leading * symbols so we don't end up with +*a** */
$word = $defaultOperator . ltrim( $word, '*' );
@@ -1179,11 +1196,11 @@ class _Query extends \IPS\Content\Search\Query
if ( $prepared )
{
/* Force newest as of 4.2.6 to test search results */
if ( count( $likeWhere ) and count( $words ) )
if ( \count( $likeWhere ) and \count( $words ) )
{
$return = array( implode( ' AND ', $likeWhere ) . " AND MATCH({$columns}) AGAINST (? IN BOOLEAN MODE)", $term );
}
else if ( count( $likeWhere ) )
else if ( \count( $likeWhere ) )
{
$return = array( implode( ' AND ', $likeWhere ) );
}
@@ -1195,7 +1212,7 @@ class _Query extends \IPS\Content\Search\Query
else
{
/* Force newest as of 4.2.6 to test search results */
if ( count( $likeWhere ) )
if ( \count( $likeWhere ) )
{
$return[] = implode( ' AND ', $likeWhere );
}
@@ -1231,7 +1248,13 @@ class _Query extends \IPS\Content\Search\Query
/* Set order clause */
$order = $this->order;
/* This forces MySQL to strongly hint a better index to the optimiser for MySQL 5.7+, even though sorting will not be affected */
if ( $this->filterByUnread and ( ! mb_stristr( \IPS\Db::i()->server_info, '-MariaDB' ) and \IPS\Db::i()->server_version >= 50700 ) )
{
$order .= ", index_date_updated";
}
/* But we're sorting by relevancy, we need to actually select that value with our fancy algorithm */
if ( mb_substr( $this->order, 0, 9 ) === 'calcscore' )
{
@@ -1240,7 +1263,7 @@ class _Query extends \IPS\Content\Search\Query
{
if ( \IPS\Settings::i()->search_title_boost )
{
$titleField = '(' . static::matchClause( 'index_title', $term, $operator === static::OPERATOR_AND ? '+' : '', FALSE ) . '*' . intval( \IPS\Settings::i()->search_title_boost ) . ')'; // The title score times multiplier
$titleField = '(' . static::matchClause( 'index_title', $term, $operator === static::OPERATOR_AND ? '+' : '', FALSE ) . '*' . \intval( \IPS\Settings::i()->search_title_boost ) . ')'; // The title score times multiplier
}
else
{
@@ -1394,11 +1417,11 @@ class _Query extends \IPS\Content\Search\Query
{
if ( $row['Variable_name'] === $minVariable )
{
\IPS\Data\Store::i()->mysqlMinWord = intval( $row['Value'] );
\IPS\Data\Store::i()->mysqlMinWord = \intval( $row['Value'] );
}
elseif ( $row['Variable_name'] === $maxVariable )
{
\IPS\Data\Store::i()->mysqlMaxWord = intval( $row['Value'] );
\IPS\Data\Store::i()->mysqlMaxWord = \intval( $row['Value'] );
}
}
}