Version 4.3.6

This commit is contained in:
Neo committed 2025-12-19 05:49:12 -08:00
1 parent fe1acf984a
commit 1a0c7fd3c2
609 files changed
+14707 -5726

No files matched your search

+15
View File
@@ -125,6 +125,21 @@ class _ContentFilter
return $obj;
}
/**
* Constructor
* init() adds in review classes, comment classes, etc. But if we want to filter based on just the comment class, we need this method
*
* @param string $class The class
* @return \IPS\Content\Search\ContentFilter
*/
public static function initWithSpecificClass( $class )
{
$obj = new static;
$obj->classes = array( $class );
return $obj;
}
/**
* Only include results in containers
*
+234 -103
View File
@@ -47,7 +47,10 @@ class _Index extends \IPS\Content\Search\Index
{
$this->url->request()->delete();
$this->url->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->put( json_encode( array(
'mappings' => array(
'settings' => array(
'max_result_window' => \IPS\Settings::i()->search_index_maxresults
),
'mappings' => array(
'content' => array(
'properties' => array(
'index_class' => array( 'type' => 'keyword' ),
@@ -145,17 +148,87 @@ class _Index extends \IPS\Content\Search\Index
try
{
$class = get_class( $object );
/* Do not allow hidden comments to be flagged as latest */
if ( $object instanceof \IPS\Content\Comment and $indexData['index_is_last_comment'] and $indexData['index_item_id'] and $indexData['index_hidden'] !== 0 ) //
$existingData = NULL;
$existingIndexId = NULL;
$resetLastComment = FALSE;
try
{
$indexData['index_is_last_comment'] = false;
$r = $this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/' . $this->getIndexId( $object ) )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->get()->decodeJson();
if ( $r['found'] )
{
$existingData = $r['_source'];
$existingIndexId = $r['_id'];
}
}
catch( \Exception $e ) { }
if ( $object instanceof \IPS\Content\Comment and $existingIndexId and $existingData['index_is_last_comment'] and $indexData['index_is_last_comment'] and $indexData['index_item_id'] and $indexData['index_hidden'] !== 0 )
{
/* We do not allow hidden or needing approval comments to become flagged as the last comment as this means users without hidden view permission never see the item in an item only stream */
$indexData['index_is_last_comment'] = false;
$resetLastComment = TRUE;
}
else if ( $indexData['index_is_last_comment'] and $indexData['index_item_id'] )
{
$classes = array( $class );
/* If this is the latest comment, unflag what was set before on both item and comment */
if ( $object instanceof \IPS\Content\Comment )
{
$itemClass = $object::$itemClass;
if ( ! $itemClass::$firstCommentRequired )
{
$classes[] = $itemClass;
}
if ( isset( $itemClass::$reviewClass ) )
{
$classes[] = $itemClass::$reviewClass;
}
}
else if ( $object instanceof \IPS\Content\Item )
{
if ( isset( $class::$commentClass ) )
{
$classes[] = $class::$commentClass;
}
if ( isset( $class::$reviewClass ) )
{
$classes[] = $class::$reviewClass;
}
}
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_update_by_query' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false' ) )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
'script' => array(
'inline' => "ctx._source.index_is_last_comment = false;",
'lang' => 'painless'
),
'query' => array(
'bool' => array(
'must' => array(
array(
'terms' => array(
'index_class' => $classes
)
),
array(
'term' => array(
'index_item_id' => $indexData['index_item_id']
)
),
)
)
)
) ) );
}
/* Insert into index */
$r = $this->url
->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/' . $this->getIndexId( $object ) )
->request()
->request( \IPS\LONG_REQUEST_TIMEOUT )
->setHeaders( array( 'Content-Type' => 'application/json' ) )
->put( json_encode( $indexData ) );
@@ -174,6 +247,11 @@ class _Index extends \IPS\Content\Search\Index
{
\IPS\Log::log( $e, 'elasticsearch' );
}
if ( $resetLastComment )
{
$this->resetLastComment( array( $indexData['index_class'] ), $indexData['index_item_id'] );
}
}
}
}
@@ -198,7 +276,7 @@ class _Index extends \IPS\Content\Search\Index
$classes[] = $class::$reviewClass;
}
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_update_by_query' )->setQueryString( 'conflicts', 'proceed' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_update_by_query' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false' ) )->request( \IPS\LONG_REQUEST_TIMEOUT )->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
'script' => array(
'inline' => "ctx._source.index_views += ". intval( $count ) . ";",
'lang' => 'painless'
@@ -221,6 +299,44 @@ class _Index extends \IPS\Content\Search\Index
)
) ) );
}
/**
* Clear out any tasks associated with the search index method
*
* @return void
*/
public function clearTasks()
{
try
{
/* This request *intentionally* goes to _tasks and not (ourpath)/_tasks */
$response = $this->url->setPath( '/_tasks' )->request( \IPS\LONG_REQUEST_TIMEOUT )->setHeaders( array( 'Content-Type' => 'application/json' ) )->get()->decodeJson();
foreach( $response['nodes'] as $nodeId => $nodeData )
{
foreach( $nodeData['tasks'] as $taskId => $taskData )
{
/* We only need to worry about deleting parent tasks */
if( isset( $taskData['parent_task_id'] ) )
{
continue;
}
/* If the task is cancellable it isn't finished yet */
if( $taskData['cancellable'] === TRUE )
{
continue;
}
$this->url->setPath( '/.tasks/task' . $taskId )->request( \IPS\LONG_REQUEST_TIMEOUT )->setHeaders( array( 'Content-Type' => 'application/json' ) )->delete();
}
}
}
catch( \Exception $e )
{
\IPS\Log::log( $e, 'elasticsearch' );
}
}
/**
* Get the comment / review counts for an item
@@ -296,7 +412,7 @@ class _Index extends \IPS\Content\Search\Index
{
$updates[] = "ctx._source.index_is_last_comment = ( ctx._id == '{$lastCommentIndexId}' );";
}
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_update_by_query' )->setQueryString( 'conflicts', 'proceed' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_update_by_query' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false' ) )->request( \IPS\LONG_REQUEST_TIMEOUT )->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
'script' => array(
'inline' => implode( ' ', $updates ),
'lang' => 'painless'
@@ -356,7 +472,7 @@ class _Index extends \IPS\Content\Search\Index
if ( isset( $class::$commentClass ) )
{
$commentClass = $class::$commentClass;
$response = $this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_delete_by_query' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
$response = $this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_delete_by_query' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false' ) )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
'query' => array(
'bool' => array(
'must' => array(
@@ -379,7 +495,7 @@ class _Index extends \IPS\Content\Search\Index
if ( isset( $class::$reviewClass ) )
{
$reviewClass = $class::$reviewClass;
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_delete_by_query' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_delete_by_query' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false' ) )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
'query' => array(
'bool' => array(
'must' => array(
@@ -455,13 +571,13 @@ class _Index extends \IPS\Content\Search\Index
);
}
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_delete_by_query' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_delete_by_query' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false' ) )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
'query' => $query
) ) );
}
else
{
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_delete_by_query' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_delete_by_query' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false' ) )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
'query' => array(
'term' => array(
'index_class' => $class
@@ -558,7 +674,7 @@ class _Index extends \IPS\Content\Search\Index
if ( count( $updates ) )
{
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_update_by_query' )->setQueryString( 'conflicts', 'proceed' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_update_by_query' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false' ) )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
'script' => array(
'inline' => implode( ' ', $updates ),
'lang' => 'painless'
@@ -578,7 +694,7 @@ class _Index extends \IPS\Content\Search\Index
)
);
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_update_by_query' )->setQueryString( 'conflicts', 'proceed' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_update_by_query' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false' ) )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
'script' => array(
'inline' => "ctx._source.index_permissions.add( 'm.' + ctx._source.index_author );",
'lang' => 'painless'
@@ -595,7 +711,7 @@ class _Index extends \IPS\Content\Search\Index
{
$conditions['must'][] = array(
'term' => array(
'index_item_author' => '0'
'index_hidden' => 0
)
);
}
@@ -603,14 +719,14 @@ class _Index extends \IPS\Content\Search\Index
{
$conditions['must'][] = array(
'term' => array(
'index_item_author' => '2'
'index_hidden' => 2
)
);
}
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_update_by_query' )->setQueryString( 'conflicts', 'proceed' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_update_by_query' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false' ) )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
'script' => array(
'inline' => "ctx._source.index_permissions = " . intval( $newHiddenStatus ) . ";",
'inline' => "ctx._source.index_hidden = " . intval( $newHiddenStatus ) . ";",
'lang' => 'painless'
),
'query' => array(
@@ -706,7 +822,7 @@ class _Index extends \IPS\Content\Search\Index
{
try
{
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_delete_by_query' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_delete_by_query' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false' ) )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
'query' => array(
'range' => array(
'index_date_updated' => array(
@@ -739,7 +855,7 @@ class _Index extends \IPS\Content\Search\Index
try
{
/* Remove the flag */
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_update_by_query' )->setQueryString( 'conflicts', 'proceed' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_update_by_query' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false' ) )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
'script' => array(
'inline' => "ctx._source.index_is_last_comment = false;",
'lang' => 'painless'
@@ -763,76 +879,66 @@ class _Index extends \IPS\Content\Search\Index
) ) );
/* Get the latest comment */
$latest = $this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_search' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->get( json_encode( array(
'query' => array(
'bool' => array(
'must' => array(
array(
'terms' => array(
'index_class' => $classes
)
),
array(
'term' => array(
'index_item_id' => $indexItemId
)
),
array(
'term' => array(
'index_hidden' => 0
)
),
)
)
),
'sort' => array(
array(
'index_date_created' => 'desc'
)
),
'size' => 1
) ) )->decodeJson();
/* If we got it... */
if ( $latest['hits']['total'] )
$itemClass = NULL;
foreach ( $classes as $class )
{
$result = $latest['hits']['hits'][0];
/* Set that it is the latest comment */
$r = $this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/' . $result['_type'] . '/' . $result['_id'] . '/_update' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
'doc' => array(
'index_is_last_comment' => true
)
) ) );
/* And set the updated time on the main item (done as _update_by_query because it might not exist if the first comment is required) */
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_update_by_query' )->setQueryString( 'conflicts', 'proceed' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
'script' => array(
'inline' => "ctx._source.index_date_updated = " . intval( $latest['_source']['index_date_updated'] ) ."; ctx._source.index_date_commented = " . intval( $latest['_source']['index_date_commented'] ) .";",
'lang' => 'painless'
),
'query' => array(
'bool' => array(
'must' => array(
array(
'terms' => array(
'index_class' => $classes
)
),
array(
'term' => array(
'index_item_id' => $indexItemId
)
),
array(
'term' => array(
'index_object_id' => $indexItemId
)
),
if ( in_array( 'IPS\Content\Item', class_parents( $class ) ) )
{
$itemClass = $class;
break;
}
elseif ( isset( $class::$itemClass ) )
{
$itemClass = $class::$itemClass;
}
}
if ( $itemClass )
{
try
{
$item = $itemClass::load( $indexItemId );
if ( $lastComment = $item->comments( 1, 0, 'date', 'desc', NULL, FALSE ) )
{
/* Set that it is the latest comment */
$r = $this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/' . $this->getIndexId( $lastComment ) . '/_update' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
'doc' => array(
'index_is_last_comment' => true
)
)
)
) ) );
) ) );
/* And set the updated time on the main item (done as _update_by_query because it might not exist if the first comment is required) */
$indexDataForLastComment = $this->indexData( $lastComment );
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_update_by_query' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false' ) )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
'script' => array(
'inline' => "ctx._source.index_date_updated = " . intval( $indexDataForLastComment['index_date_updated'] ) ."; ctx._source.index_date_commented = " . intval( $indexDataForLastComment['index_date_commented'] ) .";",
'lang' => 'painless'
),
'query' => array(
'bool' => array(
'must' => array(
array(
'terms' => array(
'index_class' => $classes
)
),
array(
'term' => array(
'index_item_id' => $indexItemId
)
),
array(
'term' => array(
'index_object_id' => $indexItemId
)
),
)
)
)
) ) );
}
}
catch ( \OutOfRangeException $e ) { }
}
}
catch ( \Exception $e )
@@ -852,23 +958,45 @@ class _Index extends \IPS\Content\Search\Index
{
try
{
$results = $this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_search' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->get( json_encode( array(
'query' => array(
'bool' => array(
'filter' => array(
array(
'terms' => array(
'index_item_index_id' => $itemIndexIds
),
/* Set the query */
$query = array(
'bool' => array(
'filter' => array(
array(
'terms' => array(
'index_item_index_id' => $itemIndexIds
),
array(
'term' => array(
'index_author' => $member->member_id
)
),
array(
'term' => array(
'index_author' => $member->member_id
)
)
)
)
);
/* Get the count */
$count = $this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_search' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->get( json_encode( array(
'size' => 0,
'query' => $query
) ) )->decodeJson();
if ( !$count['hits']['total'] )
{
return array();
}
/* Now get the unique item ids */
$results = $this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/_search' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->get( json_encode( array(
'aggs' => array(
'itemIds' => array(
'terms' => array(
'field' => 'index_item_index_id',
'size' => $count['hits']['total']
)
)
),
'query' => $query
) ) )->decodeJson();
}
catch ( \Exception $e )
@@ -878,9 +1006,12 @@ class _Index extends \IPS\Content\Search\Index
}
$iPostedIn = array();
foreach ( $results['hits']['hits'] as $result )
foreach ( $results['aggregations']['itemIds']['buckets'] as $result )
{
$iPostedIn[] = $result['_source']['index_item_index_id'];
if ( $result['doc_count'] )
{
$iPostedIn[] = $result['key'];
}
}
return $iPostedIn;
+29 -8
View File
@@ -906,6 +906,12 @@ class _Query extends \IPS\Content\Search\Query
*/
public function search( $term = NULL, $tags = NULL, $method = 1, $operator = NULL )
{
/* If we're looking for more results than we can fetch, we don't need to ask Elastic Search */
if( ( $this->offset + $this->resultsToGet ) >= \IPS\Settings::i()->search_index_maxresults )
{
return new \IPS\Content\Search\Results( array(), 0 );
}
$operator = $operator ?: \IPS\Settings::i()->search_default_operator;
$must = array();
$filters = $this->filters;
@@ -924,11 +930,11 @@ class _Query extends \IPS\Content\Search\Query
$term = trim( $term, '"' );
if ( $method & static::TERM_TITLES_ONLY )
{
$searchConditions[] = array( 'match_phrase' => array( 'index_title' => array( 'query' => $term ), 'analyzer' => 'standard' ) );
$searchConditions[] = array( 'match_phrase' => array( 'index_title' => array( 'query' => $term ) ) );
}
else
{
$searchConditions[] = array( 'multi_match' => array( 'query' => $term, 'fields' => array( 'index_content', $titleField ), 'type' => 'phrase', 'analyzer' => 'standard' ) );
$searchConditions[] = array( 'multi_match' => array( 'query' => $term, 'fields' => array( 'index_content', $titleField ), 'type' => 'phrase' ) );
}
}
else
@@ -995,6 +1001,7 @@ class _Query extends \IPS\Content\Search\Query
/* Peform the search */
try
{
/* Initial query */
$query = array(
'bool' => array(
'must' => $must,
@@ -1003,6 +1010,7 @@ class _Query extends \IPS\Content\Search\Query
)
);
/* Add the time decay */
if ( \IPS\Settings::i()->search_decay_factor and !$this->sort )
{
$query = array(
@@ -1017,8 +1025,9 @@ class _Query extends \IPS\Content\Search\Query
)
);
}
if ( \IPS\Settings::i()->search_elastic_self_boost and $this->member->member_id )
/* Add the self boost */
if ( \IPS\Settings::i()->search_elastic_self_boost and $this->member->member_id and !$this->sort )
{
$query = array(
'function_score' => array(
@@ -1033,25 +1042,37 @@ class _Query extends \IPS\Content\Search\Query
);
}
$return = $this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_search' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->get( json_encode( array(
/* Build the JSON and validate it. Use JSON_PARTIAL_OUTPUT_ON_ERROR in case someone has used an unencodable value as
the term, and check json_encode() didn't return FALSE (which it may do on error) as a sanity check against
sending a blank query, which would return everything in the index */
$array = array(
'query' => $query,
'sort' => $this->sort ?: array(),
'from' => $this->offset,
'size' => $this->resultsToGet,
) ) )->decodeJson();
);
$json = json_encode( $array, JSON_PARTIAL_OUTPUT_ON_ERROR );
if ( $json === FALSE )
{
return new \IPS\Content\Search\Results( array(), 0 );
}
/* Make the call! */
$return = $this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_search' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->get( $json )->decodeJson();
if ( isset( $return['error'] ) )
{
\IPS\Log::log( print_r( $return['error'], TRUE ), 'elasticsearch' );
return new \IPS\Content\Search\Results( array(), 0 );
}
/* Set results */
return new \IPS\Content\Search\Results( array_map( function( $hit ) {
$indexData = $hit['_source'];
$indexData['index_permissions'] = implode( ',', $indexData['index_permissions'] );
$indexData['index_tags'] = $indexData['index_tags'] ? implode( ',', $indexData['index_tags'] ) : NULL;
return $indexData;
}, $return['hits']['hits'] ), $return['hits']['total'] );
}, $return['hits']['hits'] ), $return['hits']['total'] <= \IPS\Settings::i()->search_index_maxresults ? $return['hits']['total'] : (int) \IPS\Settings::i()->search_index_maxresults );
}
catch ( \Exception $e )
{
+49 -5
View File
@@ -39,7 +39,7 @@ abstract class _Index extends \IPS\Patterns\Singleton
{
if ( \IPS\Settings::i()->search_method == 'elastic' )
{
static::$instance = new \IPS\Content\Search\Elastic\Index( \IPS\Http\Url::external( \IPS\Settings::i()->search_elastic_server . '/' . \IPS\Settings::i()->search_elastic_index ) );
static::$instance = new \IPS\Content\Search\Elastic\Index( \IPS\Http\Url::external( rtrim( \IPS\Settings::i()->search_elastic_server, '/' ) . '/' . \IPS\Settings::i()->search_elastic_index ) );
}
else
{
@@ -59,7 +59,7 @@ abstract class _Index extends \IPS\Patterns\Singleton
{
if ( \IPS\Settings::i()->search_method == 'elastic' )
{
return new \IPS\Content\Search\Elastic\MassIndexer( \IPS\Http\Url::external( \IPS\Settings::i()->search_elastic_server . '/' . \IPS\Settings::i()->search_elastic_index ) );
return new \IPS\Content\Search\Elastic\MassIndexer( \IPS\Http\Url::external( rtrim( \IPS\Settings::i()->search_elastic_server, '/' ) . '/' . \IPS\Settings::i()->search_elastic_index ) );
}
else
{
@@ -104,6 +104,30 @@ abstract class _Index extends \IPS\Patterns\Singleton
}
}
/**
* Index a single items comments and reviews if applicable.
*
* @param \IPS\Content\Searchable $object The item to index
* @return void
*/
public function indexSingleItem( \IPS\Content\Searchable $object )
{
try
{
$idColumn = $object::$databaseColumnId;
if ( isset( $object::$commentClass ) AND is_subclass_of( $object::$commentClass, 'IPS\Content\Searchable' ) )
{
\IPS\Task::queue( 'core', 'IndexSingleItem', array( 'class' => $object::$commentClass, 'id' => $object->$idColumn, 'title' => $object->mapped('title'), 'url' => (string) $object->url() ), 5, TRUE );
}
if ( isset( $object::$reviewClass ) AND is_subclass_of( $object::$reviewClass, 'IPS\Content\Searchable' ) )
{
\IPS\Task::queue( 'core', 'IndexSingleItem', array( 'class' => $object::$reviewClass, 'id' => $object->$idColumn, 'title' => $object->mapped('title'), 'url' => (string) $object->url() ), 5, TRUE );
}
}
catch( \OutOfRangeException $e ) {}
}
/**
* Get index data
*
@@ -138,9 +162,19 @@ abstract class _Index extends \IPS\Patterns\Singleton
$itemClass = $class::$itemClass;
if ( $itemClass::$firstCommentRequired and $object->isFirst() )
{
$title = $object->item()->searchIndexTitle();
$tags = ( $object->item() instanceof \IPS\Content\Tags and \IPS\Settings::i()->tags_enabled ) ? implode( ',', array_filter( $object->item()->tags() ) ) : NULL;
$prefix = ( $object->item() instanceof \IPS\Content\Tags and \IPS\Settings::i()->tags_enabled ) ? $object->item()->prefix() : NULL;
try
{
$item = $object->item();
}
catch( \OutOfRangeException $ex )
{
/* Comment has no working item, return */
return NULL;
}
$title = $item->searchIndexTitle();
$tags = ( $item instanceof \IPS\Content\Tags and \IPS\Settings::i()->tags_enabled ) ? implode( ',', array_filter( $item->tags() ) ) : NULL;
$prefix = ( $item instanceof \IPS\Content\Tags and \IPS\Settings::i()->tags_enabled ) ? $item->prefix() : NULL;
$isForItem = TRUE;
}
}
@@ -375,6 +409,16 @@ abstract class _Index extends \IPS\Patterns\Singleton
{
// Do nothing by default
}
/**
* Clear out any tasks associated with the search index method
*
* @return void
*/
public function clearTasks()
{
// Do nothing by default
}
/**
* Retrieve the search ID for an item
+4 -2
View File
@@ -484,8 +484,10 @@ class _Index extends \IPS\Content\Search\Index
{
if ( $cutoff )
{
\IPS\Db::i()->delete( 'core_search_index_item_map', array( 'index_item_id IN( ? )', \IPS\Db::i()->select( 'index_item_id', 'core_search_index', array( 'index_date_updated < ?', $cutoff->getTimestamp() ) ) ) );
\IPS\Db::i()->delete( 'core_search_index_tags', array( 'index_id IN( ? )', \IPS\Db::i()->select( 'index_id', 'core_search_index', array( 'index_date_updated < ?', $cutoff->getTimestamp() ) ) ) );
$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( 'core_search_index', array( 'index_date_updated < ?', $cutoff->getTimestamp() ) );
}
else
+12 -9
View File
@@ -94,7 +94,7 @@ class _Query extends \IPS\Content\Search\Query
foreach ( $contentFilters as $filter )
{
$clause = array();
if ( $type )
if ( $type and $filter->itemClass )
{
$this->itemClasses[] = $filter->itemClass;
}
@@ -116,16 +116,19 @@ class _Query extends \IPS\Content\Search\Query
$clause[] = \IPS\Db::i()->in( 'index_container_id', $filter->containerIds, $filter->containerIdFilter === FALSE );
}
$itemClass = $filter->itemClass;
if ( isset( $itemClass::$containerNodeClass ) )
if ( $filter->itemClass )
{
$containerClass = $itemClass::$containerNodeClass;
$unsearchableIds = $containerClass::unsearchableNodeIds();
if ( $unsearchableIds != NULL )
$itemClass = $filter->itemClass;
if ( isset( $itemClass::$containerNodeClass ) )
{
$clause[] = \IPS\Db::i()->in( 'index_container_id', $unsearchableIds, TRUE );
}
$containerClass = $itemClass::$containerNodeClass;
$unsearchableIds = $containerClass::unsearchableNodeIds();
if ( $unsearchableIds != NULL )
{
$clause[] = \IPS\Db::i()->in( 'index_container_id', $unsearchableIds, TRUE );
}
}
}
/* Are we excluding certain container classes? */
+1 -1
View File
@@ -54,7 +54,7 @@ abstract class _Query
{
if ( \IPS\Settings::i()->search_method == 'elastic' )
{
return new \IPS\Content\Search\Elastic\Query( $member ?: \IPS\Member::loggedIn(), \IPS\Http\Url::external( \IPS\Settings::i()->search_elastic_server . '/' . \IPS\Settings::i()->search_elastic_index ) );
return new \IPS\Content\Search\Elastic\Query( $member ?: \IPS\Member::loggedIn(), \IPS\Http\Url::external( rtrim( \IPS\Settings::i()->search_elastic_server, '/' ) . '/' . \IPS\Settings::i()->search_elastic_index ) );
}
else
{
+8 -3
View File
@@ -119,8 +119,13 @@ class _Content extends \IPS\Content\Search\Result
*/
public function addToRssFeed( \IPS\Xml\Rss $document )
{
$class = $this->indexData['index_class'];
$object = $class::load( $this->indexData['index_object_id'] );
$document->addItem( $object instanceof \IPS\Content\Comment ? $object->item()->mapped('title') : $object->mapped('title'), $object->url(), $this->indexData['index_content'], \IPS\DateTime::ts( $this->indexData['index_date_created'] ) );
try
{
$class = $this->indexData['index_class'];
$object = $class::load( $this->indexData['index_object_id'] );
$document->addItem( $object instanceof \IPS\Content\Comment ? $object->item()->searchIndexTitle() : $object->searchIndexTitle(), $object->url(), $this->indexData['index_content'], \IPS\DateTime::ts( $this->indexData['index_date_created'] ) );
}
/* If the search result was orphaned, let us continue */
catch( \OutOfRangeException $e ){}
}
}
+1 -1
View File
@@ -546,7 +546,7 @@ class _Results extends \ArrayIterator
{
$extraItem = array_pop( $extraItems );
$mergeList = array_merge( array( $urlToThing ), $extraItem->getMergeData() );
$mergeList = array_filter( array_merge( array( $urlToThing ), $extraItem->getMergeData() ) );
$listToDisplay = $mergeList;
if( count( $mergeList ) > 3 )