Version 4.1.2

This commit is contained in:
Neo committed 2025-12-19 05:32:19 -08:00
1 parent 3ba79295ee
commit 28bd025b35
1131 files changed
+63658 -20579

No files matched your search

+494 -128
View File
@@ -24,6 +24,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
class _Query extends \IPS\Content\Search\Query
{
/**
* @brief The SELECT clause
*/
protected $select = array( 'main' => 'main.*' );
/**
* @brief The WHERE clause
*/
protected $where = array();
@@ -49,137 +54,465 @@ class _Query extends \IPS\Content\Search\Query
protected $joins = array();
/**
* Filter by content type
* @brief Item classes included
*/
protected $itemClasses = NULL;
/**
* Filter by multiple content types
*
* @param string $class The type of content to search (including all comment/review classes)
* @param array|NULL $containers An array of container IDs to filter by, or NULL to not filter by containers
* @param int|NULL $minimumComments The minimum number of comments
* @param int|NULL $minimumReviews The minimum number of reviews
* @param int|NULL $minimumViews The minimum number of views
* @param array $contentFilters Array of \IPS\Content\Search\ContentFilter objects
* @param bool $type TRUE means only include results matching the filters, FALSE means exclude all results matching the filters
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function filterByContent( $class, $containers = NULL, $minimumComments = NULL, $minimumReviews = NULL, $minimumViews = NULL )
public function filterByContent( array $contentFilters, $type = TRUE )
{
$classes = array( $class );
if ( isset( $class::$commentClass ) )
/* Init */
$filters = array();
$params = array();
if ( $type )
{
$classes[] = $class::$commentClass;
}
if ( isset( $class::$reviewClass ) )
{
$classes[] = $class::$reviewClass;
}
$this->where[] = array( \IPS\Db::i()->in( 'index_class', $classes ) );
if ( $containers !== NULL )
{
$this->where[] = array( \IPS\Db::i()->in( 'index_container_id', $containers ) );
$this->itemClasses = array();
}
if ( $minimumComments !== NULL or $minimumReviews !== NULL or $minimumViews !== NULL )
/* Loop the filters */
foreach ( $contentFilters as $filter )
{
$this->joins[ $class::$databaseTable ] = array( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnId . '=core_search_index.index_item_id' );
$clause = array();
if ( $type )
{
$this->itemClasses[] = $filter->itemClass;
}
if ( $minimumComments !== NULL )
/* Set the class */
if ( count( $filter->classes ) > 1 )
{
$this->where[] = array( $class::$databasePrefix . $class::$databaseColumnMap['num_comments'] . '>=?', $minimumComments );
$clause[] = \IPS\Db::i()->in( 'index_class', $filter->classes );
}
if ( $minimumReviews !== NULL )
else
{
$this->where[] = array( $class::$databasePrefix . $class::$databaseColumnMap['num_reviews'] . '>=?', $minimumReviews );
$clause[] = 'index_class=?';
$params[] = array_pop( $filter->classes );
}
if ( $minimumViews !== NULL )
/* Set the containers */
if ( $filter->containerIdFilter !== NULL )
{
$this->where[] = array( $class::$databasePrefix . $class::$databaseColumnMap['views'] . '>=?', $minimumViews );
$clause[] = \IPS\Db::i()->in( 'index_container_id', $filter->containerIds, $filter->containerIdFilter === FALSE );
}
/* Set the item IDs */
if ( $filter->itemIdFilter !== NULL )
{
$clause[] = \IPS\Db::i()->in( 'index_item_id', $filter->itemIds, $filter->itemIdFilter === FALSE );
}
/* Minimum comments/reviews/views? */
if ( $filter->minimumComments or $filter->minimumReviews or $filter->minimumViews )
{
$class = $filter->itemClass;
$this->joins[] = array( 'from' => $class::$databaseTable, 'where' => $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnId . '=main.index_item_id' );
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 );
}
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 );
}
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 );
}
}
/* Only first comment? */
if ( $filter->onlyFirstComment )
{
$clause[] = "index_title IS NOT NULL";
}
/* Only last comment? */
if ( $filter->onlyLastComment )
{
$clause[] = "index_is_last_comment=1";
}
/* Put it together */
if ( count( $clause ) > 1 )
{
$filters[] = '( ' . implode( ' AND ', $clause ) . ' )';
}
else
{
$filters[] = array_pop( $clause );
}
}
/* Put it all together */
$this->where[] = array_merge( array( $type ? ( '( ' . implode( ' OR ', $filters ) . ' )' ) : ( '!( ' . implode( ' OR ', $filters ) . ' )' ) ), $params );
/* Return */
return $this;
}
/**
* Filter by content type to exclude
* Filter by author
*
* @param array $classes The types of content to exclude (not including all comment/review classes)
* @param bool $allowedContiners If TRUE, $class should be an array of allowed containers for that class - e.g. array( 'IPS\forums\Topic' => array( 1, 2 ) - otherwise, a simple array - e.g. array( 'IPS\forums\Topic' )
* @param \IPS\Member|int|array $author The author, or an array of author IDs
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function filterByExcludeContent( array $classes, $allowedContiners=FALSE )
{
if ( $allowedContiners )
public function filterByAuthor( $author )
{
if ( is_array( $author ) )
{
$where = array();
$params = array();
$excludeAll = array();
foreach ( $classes as $class => $allowed )
{
if ( count( $allowed ) )
{
$where[] = '( index_class<>? OR ' . \IPS\Db::i()->in( 'index_container_id', $allowed ) . ' )';
$params[] = $class;
}
else
{
$excludeAll[] = $class;
}
}
$finalWhere = array();
if ( count( $excludeAll ) )
{
$finalWhere[] = \IPS\Db::i()->in( 'index_class', $excludeAll, TRUE );
}
if ( count( $where ) )
{
$finalWhere[] = implode( ' OR ', $where );
}
$this->where[] = array_merge( array( implode( ' AND ', $finalWhere ) ), $params );
$this->where[] = array( \IPS\Db::i()->in( 'index_author', $author ) );
}
else
{
$this->where[] = array( \IPS\Db::i()->in( 'index_class', $classes, TRUE ) );
$this->where[] = array( 'index_author=?', $author instanceof \IPS\Member ? $author->member_id : $author );
}
return $this;
}
/**
* Filter by content item
* Filter for profile
*
* @param \IPS\Content\Item $item The item
* @param \IPS\Member $member The member whose profile is being viewed
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function filterByItem( \IPS\Content\Item $item )
public function filterForProfile( \IPS\Member $member )
{
$classes = array();
if ( isset( $item::$commentClass ) )
{
$classes[] = $item::$commentClass;
}
if ( isset( $item::$reviewClass ) )
{
$classes[] = $item::$reviewClass;
}
$this->where[] = array( \IPS\Db::i()->in( 'index_class', $classes ) );
$idColumn = $item::$databaseColumnId;
$this->where[] = array( 'index_item_id=?', $item->$idColumn );
$this->where[] = array( '( index_author=? OR ( index_class=? AND index_container_id=? ) )', $member->member_id, 'IPS\core\Statuses\Status', $member->member_id );
return $this;
}
/**
* Filter by author
* Filter by item author
*
* @param \IPS\Member $author The author
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function filterByAuthor( \IPS\Member $author )
public function filterByItemAuthor( \IPS\Member $author )
{
$this->where[] = array( 'index_author=?', $author->member_id );
$this->where[] = array( 'index_item_author=?', $author->member_id );
return $this;
return $this;
}
/**
* Filter by content the user follows
*
* @param bool $includeContainers Include content in containers the user follows?
* @param bool $includeItems Include items and comments/reviews on items the user follows?
* @param bool $includeMembers Include content posted by members the user follows?
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function filterByFollowed( $includeContainers, $includeItems, $includeMembers )
{
$where = array();
$params = array();
/* Are we including items or containers? */
if ( $includeContainers or $includeItems )
{
/* Work out what classes we need to examine */
if ( $this->itemClasses !== NULL )
{
$classes = $this->itemClasses;
}
else
{
$classes = array();
foreach ( \IPS\Application::allExtensions( 'core', 'ContentRouter', FALSE ) as $object )
{
$classes = array_merge( $object->classes, $classes );
}
}
/* Loop them */
foreach ( $classes as $class )
{
if( is_subclass_of( $class, 'IPS\Content\Followable' ) )
{
/* Construct a subquery which will get the IDs of the items of this class that we follow */
$subQueryWhere = array( array( 'follow_app=?', $class::$application ) );
if ( $includeContainers and $includeItems )
{
$subQueryWhere[] = array( '( follow_area=? OR follow_area=? )', mb_strtolower( mb_substr( $class::$containerNodeClass, mb_strrpos( $class::$containerNodeClass, '\\' ) + 1 ) ), mb_strtolower( mb_substr( $class, mb_strrpos( $class, '\\' ) + 1 ) ) );
}
elseif ( $includeItems )
{
$subQueryWhere[] = array( 'follow_area=?', mb_strtolower( mb_substr( $class, mb_strrpos( $class, '\\' ) + 1 ) ) );
}
elseif ( $includeContainers )
{
$subQueryWhere[] = array( 'follow_area=?', mb_strtolower( mb_substr( $class::$containerNodeClass, mb_strrpos( $class::$containerNodeClass, '\\' ) + 1 ) ) );
}
$subQueryWhere[] = array( 'follow_member_id=?', $this->member->member_id );
$subQuery = \IPS\Db::i()->select( 'follow_rel_id', 'core_follow', $subQueryWhere );
/* If we're only doing one class, there's no point in doing "item_class=? AND ( item_class=? AND item_id IN(?) )" - we can just do "item_class=? AND item_id IN(?)" */
if ( count( $classes ) == 1 )
{
$where[] = 'index_item_id IN(?)';
$params[] = $subQuery;
}
/* But otherwise we need to group by class, because item IDs are not unique */
else
{
/* Work out what classes this applies to - need to specify comment and review classes */
$_classes = array( $class );
if ( isset( $class::$commentClass ) )
{
$_classes[] = $class::$commentClass;
}
if ( isset( $class::$reviewClass ) )
{
$_classes[] = $class::$reviewClass;
}
/* Constuct the where */
$where[] = '( ' . \IPS\Db::i()->in( 'index_class', $_classes ) . ' AND index_item_id IN( ? ) )';
$params[] = $subQuery;
}
}
}
}
/* Are we including content posted by followed members? */
if ( $includeMembers )
{
$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 ) );
}
/* Put it all together */
if ( count( $where ) )
{
$this->where[] = array_merge( array( '( ' . implode( ' OR ', $where ) . ' )' ), $params );
}
/* And return */
return $this;
}
/**
* Filter by content the user has posted in
*
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function filterByItemsIPostedIn()
{
$this->joins[] = array(
'from' => \IPS\Db::i()->select(
'index_item_index_id',
array('core_search_index', 'sub' ),
array( array( 'index_author=' . intval( $this->member->member_id ) ) ),
NULL, NULL, NULL, NULL, \IPS\Db::SELECT_DISTINCT
),
'where' => 'sub.index_item_index_id=main.index_item_index_id',
'type' => 'STRAIGHT_JOIN'
);
return $this;
}
/**
* Filter by content the user has not read
*
* @note If applicable, it is more efficient to call filterByContent() before calling this method
* @return \IPS\Content\Search\Query (for daisy chaining)
*/
public function filterByUnread()
{
/* Work out what classes we need to examine */
if ( $this->itemClasses !== NULL )
{
$classes = $this->itemClasses;
}
else
{
$classes = array();
foreach ( \IPS\Application::allExtensions( 'core', 'ContentRouter', FALSE ) as $object )
{
$classes = array_merge( $object->classes, $classes );
}
}
/* Loop them */
$where = array();
$params = array();
foreach ( $classes as $class )
{
if( is_subclass_of( $class, 'IPS\Content\ReadMarkers' ) )
{
/* Get the actual clause */
$unreadWhere = $this->_getUnreadWhere( $class );
/* Work out what classes this applies to - need to specify comment and review classes */
$_classes = array( $class );
if ( isset( $class::$commentClass ) )
{
$_classes[] = $class::$commentClass;
}
if ( isset( $class::$reviewClass ) )
{
$_classes[] = $class::$reviewClass;
}
/* Add it to the array */
$clause = array( \IPS\Db::i()->in( 'index_class', $_classes ) );
foreach ( $unreadWhere as $_clause )
{
$clause[] = array_shift( $_clause );
$params = array_merge( $params, $_clause );
}
$where[] = '( ' . implode( ' AND ', $clause ) . ' )';
}
}
/* Put it all together */
$this->where[] = array_merge( array( '( ' . implode( ' OR ', $where ) . ' )' ), $params );
}
/**
* Get the 'unread' where SQL
*
* @param string $class Content class (\IPS\forums\Forum)
* @return array
*/
protected function _getUnreadWhere( $class )
{
$classBits = explode( "\\", $class );
$application = $classBits[1];
$resetTimes = $this->member->markersResetTimes( NULL );
$resetTimes = isset( $resetTimes[ $application ] ) ? $resetTimes[ $application ] : array();
$oldestTime = time();
$markers = array();
$excludeIds = array();
$where = array();
$unreadWheres = array();
$containerIds = array();
$containerClass = ( $class::$containerNodeClass ) ? $class::$containerNodeClass : NULL;
foreach( $resetTimes as $containerId => $timestamp )
{
/* Pages has different classes per database, but recorded as 'cms' and the container ID in the marking tables */
if ( $containerClass and method_exists( $containerClass, 'isValidContainerId' ) )
{
if ( ! $containerClass::isValidContainerId( $containerId ) )
{
continue;
}
}
$timestamp = $timestamp ?: $this->member->marked_site_read;
$containerIds[] = $containerId;
$unreadWheres[] = '( index_container_id=' . $containerId . ' AND index_date_updated > ' . (int) $timestamp . ')';
$items = $this->member->markersItems( $application, \IPS\Content\Item::makeMarkerKey( $containerId ) );
if ( count( $items ) )
{
foreach( $items as $mid => $mtime )
{
if ( $mtime > $timestamp )
{
/* If an item has been moved from one container to another, the user may have a marker
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 ) )
{
$_key = array_search( $mid, $markers );
$_mtime = intval( mb_substr( $_key, 0, mb_strpos( $_key, '.' ) ) );
if ( $_mtime < $mtime )
{
unset( $markers[ $_key ] );
}
}
$markers[ $mtime . '.' . $mid ] = $mid;
}
}
}
}
if( count( $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 ) . ")";
}
if( count( $unreadWheres ) )
{
$where[] = array( "(" . implode( " OR ", $unreadWheres ) . ")" );
}
if ( count( $markers ) )
{
/* Avoid packet issues */
krsort( $markers );
$useIds = array_flip( array_slice( $markers, 0, 500, TRUE ) );
$select = '';
$from = '';
$notIn = array();
/* What is the best date column? */
$dateColumns = array();
foreach ( array( 'updated', 'last_comment', 'last_review' ) as $k )
{
if ( isset( $class::$databaseColumnMap[ $k ] ) )
{
if ( is_array( $class::$databaseColumnMap[ $k ] ) )
{
foreach ( $class::$databaseColumnMap[ $k ] as $v )
{
$dateColumns[] = " IFNULL( " . $class::$databaseTable . '.'. $class::$databasePrefix . $v . ", 0 )";
}
}
else
{
$dateColumns[] = " IFNULL( " . $class::$databaseTable . '.'. $class::$databasePrefix . $class::$databaseColumnMap[ $k ] . ", 0 )";
}
}
}
$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 )
{
if ( isset( $useIds[ $row['_id'] ] ) )
{
if ( $useIds[ $row['_id'] ] >= $row['_date'] )
{
/* Still read */
$notIn[] = intval( $row['_id'] );
}
}
}
if ( count( $notIn ) )
{
$where[] = array( "( index_item_id NOT IN (" . implode( ',', $notIn ) . ") )" );
}
}
return $where;
}
/**
@@ -272,13 +605,21 @@ class _Query extends \IPS\Content\Search\Query
case static::ORDER_NEWEST_UPDATED:
$this->order = 'index_date_updated DESC';
break;
case static::ORDER_OLDEST_UPDATED:
$this->order = 'index_date_updated ASC';
break;
case static::ORDER_NEWEST_CREATED:
$this->order = 'index_date_created DESC';
break;
case static::ORDER_OLDEST_CREATED:
$this->order = 'index_date_updated DESC';
break;
case static::ORDER_RELEVANCY:
$this->order = NULL;
$this->order = 'calcscore DESC';
break;
}
@@ -295,75 +636,86 @@ class _Query extends \IPS\Content\Search\Query
*/
protected function _searchWhereClause( $term = NULL, $tags = NULL, $method = 1 )
{
/* Do we have a term? */
$where = array();
if ( $term !== NULL )
/* Boolean tags */
if ( $tags !== NULL )
{
if( mb_strpos( $term, ' ' ) !== FALSE AND mb_strpos( $term, '+' ) === FALSE AND mb_strpos( $term, '-' ) === FALSE AND mb_strpos( $term, '"' ) === FALSE )
$theTags = array();
foreach( $tags as $tagEntry )
{
$terms = explode( ' ', $term );
$newTerms = array();
foreach( $terms as $_term )
foreach( explode( ' ', $tagEntry ) as $_term )
{
if( mb_strlen( $_term ) > 2 )
{
$newTerms[] = "+" . $_term;
$theTags[] = "+" . $_term;
}
else
{
$newTerms[] = $_term;
$theTags[] = $_term;
}
}
$term = implode( ' ', $newTerms );
$searchTags[] = implode( ' ', $theTags );
}
}
/* Try to guess what mode will be best for this query. If it looks like a MySQL boolean mode search, use that */
if ( mb_substr( $term, 0, 1 ) === '+' or mb_substr( $term, -1, 1 ) === '*' or preg_match( '/^".*"$/', $term ) )
/* Do we have a term? */
$where = array();
if ( $term !== NULL )
{
$indexes = 'index_content,index_title';
if ( $method & static::TERM_TITLES_ONLY )
{
$mode = 'IN BOOLEAN MODE';
$indexes = 'index_title';
}
/* Otherwise, we're just using the default mode - we tried using query expansion for short queries, but it didn't work well (lots of irrelevant results) */
else
/* Default to 'AND' boolean mode search unless asked otherwise*/
if ( ! ( $method & static::TERM_OR_MODE ) and ! static::termIsPhrase( $term ) )
{
$mode = '';
$theWords = array();
foreach( static::termAsWordsArray( $term ) as $_term )
{
$potentialOperator = mb_substr( $_term, 0, 1 );
if ( ! in_array( $potentialOperator, array( '-', '+', '~' ) ) )
{
if( mb_strlen( $_term ) > 2 )
{
$theWords[] = "+" . $_term;
}
}
}
$term = implode( ' ', $theWords );
}
/* If we also have tags, create a combined where */
if ( $tags !== NULL )
{
if ( $method === static::TERM_OR_TAGS )
{
$where[] = array( "( MATCH(index_content,index_title) AGAINST (? {$mode}) OR " . \IPS\Db::i()->findInSet( 'index_tags', $tags ) . ' )', $term );
if ( $method & static::TERM_OR_TAGS )
{
$where[] = array( "( MATCH({$indexes}) AGAINST (? IN BOOLEAN MODE) ) OR " . \IPS\Db::i()->findInSet( 'index_tags', $tags ), $term );
}
else
{
$where[] = array( "MATCH(index_content,index_title) AGAINST (? {$mode})", $term );
$where[] = array( \IPS\Db::i()->findInSet( 'index_tags', $tags ) );
$where[] = array( "MATCH({$indexes}) AGAINST (? IN BOOLEAN MODE)", $term );
$where[] = \IPS\Db::i()->findInSet( 'index_tags', $tags );
}
}
/* Or just use the term */
else
{
$where[] = array( "MATCH(index_content,index_title) AGAINST (? {$mode})", $term );
$where[] = array( "MATCH({$indexes}) AGAINST (? IN BOOLEAN MODE)", $term );
}
}
/* Or do we have tags? */
elseif ( $tags !== NULL )
{
//$this->where[] = array( \IPS\Db::i()->findInSet( 'index_tags', $tags ) );
$tagWhere = array();
$params = array();
foreach( $tags as $tag )
{
$where[] = \IPS\Db::i()->findInSet( 'index_tags', $tags );
if ( $this->order = 'calcscore DESC' )
{
array_push( $params, $tag, $tag, $tag, $tag );
$tagWhere[] = "( index_tags=? OR index_tags LIKE CONCAT( ?, ',%' ) OR index_tags LIKE CONCAT( '%,', ? ) OR index_tags LIKE CONCAT( '%,', ?, ',%' ) )";
$this->setOrder( static::ORDER_NEWEST_UPDATED );
}
$where[] = array_merge( array( '(' . implode( ' OR ', $tagWhere ) . ')' ), $params );
}
/* Only get stuff we have permission for */
@@ -373,6 +725,12 @@ class _Query extends \IPS\Content\Search\Query
$where[] = $this->hiddenClause;
}
/* MOST PERTINENT search? */
if ( mb_substr( $this->order, 0, 9 ) === 'calcscore' )
{
$this->select['main'] .= ',' . "( ( MATCH (index_title) AGAINST ('" . \IPS\Db::i()->escape_string( $term ) ."' IN BOOLEAN MODE) * 5 ) + (MATCH ({$indexes}) AGAINST ('" . \IPS\Db::i()->escape_string( $term ) ."' IN BOOLEAN MODE) ) ) / POWER( ( ( UNIX_TIMESTAMP( NOW() ) - index_date_updated ) / 3600 ) + 2, 1.5 ) AS calcscore";
}
/* Return */
return $where;
}
@@ -387,13 +745,14 @@ class _Query extends \IPS\Content\Search\Query
*/
public function search( $term = NULL, $tags = NULL, $method = 1 )
{
$query = \IPS\Db::i()->select( '*', 'core_search_index', array_merge( $this->where, $this->_searchWhereClause( $term, $tags, $method ) ), $this->order, array( $this->offset, $this->resultsToGet ), NULL, NULL, \IPS\Db::SELECT_SQL_CALC_FOUND_ROWS );
foreach ( $this->joins as $table => $where )
$where = array_merge( $this->where, $this->_searchWhereClause( $term, $tags, $method ) );
$query = \IPS\Db::i()->select( implode( ', ', $this->select ), array( 'core_search_index', 'main' ), $where, $this->order, array( $this->offset, $this->resultsToGet ), NULL, NULL );
foreach ( $this->joins as $data )
{
$query->join( $table, $where );
$query->join( $data['from'], $data['where'], isset( $data['type'] ) ? $data['type'] : 'LEFT' );
}
$count = $query->count( TRUE );
$count = $this->count( $term, $tags, $method );
return new \IPS\Content\Search\Results( iterator_to_array( $query ), $count );
}
@@ -407,6 +766,13 @@ class _Query extends \IPS\Content\Search\Query
*/
public function count( $term = NULL, $tags = NULL, $method = 1 )
{
return \IPS\Db::i()->select( 'COUNT(*)', 'core_search_index', array_merge( $this->where, $this->_searchWhereClause( $term, $tags, $method ) ) )->first();
$query = \IPS\Db::i()->select( 'COUNT(*)', array( 'core_search_index', 'main' ), array_merge( $this->where, $this->_searchWhereClause( $term, $tags, $method ) ) );
foreach ( $this->joins as $data )
{
$query->join( $data['from'], $data['where'], isset( $data['type'] ) ? $data['type'] : 'LEFT' );
}
return $query->first();
}
}