Version 4.5.0
This commit is contained in:
1 parent
1a0c7fd3c2
commit
f79dcf067a
3791 files changed
+248032
-76372
No files matched your search
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Elasticsearch Search Exception Class
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @since 26 Nov 2019
|
||||
*/
|
||||
|
||||
namespace IPS\Content\Search\Elastic;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Elasticsearch Search Exception Class
|
||||
*/
|
||||
class _Exception extends \DomainException
|
||||
{
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace IPS\Content\Search\Elastic;
|
||||
|
||||
/* 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;
|
||||
@@ -22,6 +22,12 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
*/
|
||||
class _Index extends \IPS\Content\Search\Index
|
||||
{
|
||||
/**
|
||||
* @brief Elasticsearch version requirements
|
||||
*/
|
||||
const MINIMUM_VERSION = '6.0.0';
|
||||
const UNSUPPORTED_VERSION = '8.0.0';
|
||||
|
||||
/**
|
||||
* @brief The server URL
|
||||
*/
|
||||
@@ -44,14 +50,23 @@ class _Index extends \IPS\Content\Search\Index
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
{
|
||||
$analyzer = \IPS\Settings::i()->search_elastic_analyzer;
|
||||
$settings = array(
|
||||
'max_result_window' => \IPS\Settings::i()->search_index_maxresults
|
||||
);
|
||||
if ( $analyzer === 'custom' )
|
||||
{
|
||||
$settings['analysis'] = json_decode( '{' . \IPS\Settings::i()->search_elastic_custom_analyzer . '}', TRUE );
|
||||
$analyzer = key( $settings['analysis']['analyzer'] );
|
||||
}
|
||||
|
||||
$this->url->request()->delete();
|
||||
$this->url->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->put( json_encode( array(
|
||||
'settings' => array(
|
||||
'max_result_window' => \IPS\Settings::i()->search_index_maxresults
|
||||
),
|
||||
|
||||
$definition = array(
|
||||
'settings' => $settings,
|
||||
'mappings' => array(
|
||||
'content' => array(
|
||||
'_doc' => array(
|
||||
'properties' => array(
|
||||
'index_class' => array( 'type' => 'keyword' ),
|
||||
'index_object_id' => array( 'type' => 'long' ),
|
||||
@@ -60,11 +75,11 @@ class _Index extends \IPS\Content\Search\Index
|
||||
'index_container_id' => array( 'type' => 'long' ),
|
||||
'index_title' => array(
|
||||
'type' => 'text',
|
||||
'analyzer' => \IPS\Settings::i()->search_elastic_analyzer,
|
||||
'analyzer' => $analyzer,
|
||||
),
|
||||
'index_content' => array(
|
||||
'type' => 'text',
|
||||
'analyzer' => \IPS\Settings::i()->search_elastic_analyzer,
|
||||
'analyzer' => $analyzer,
|
||||
),
|
||||
'index_permissions' => array( 'type' => 'keyword' ),
|
||||
'index_date_created' => array(
|
||||
@@ -88,14 +103,27 @@ class _Index extends \IPS\Content\Search\Index
|
||||
'index_is_last_comment' => array( 'type' => 'boolean' ),
|
||||
'index_club_id' => array( 'type' => 'long' ),
|
||||
'index_class_type_id_hash' => array( 'type' => 'keyword' ),
|
||||
'index_views' => array( 'type' => 'long' ),
|
||||
'index_comments' => array( 'type' => 'long' ),
|
||||
'index_reviews' => array( 'type' => 'long' ),
|
||||
'index_participants' => array( 'type' => 'long' ),
|
||||
)
|
||||
)
|
||||
)
|
||||
) ) );
|
||||
);
|
||||
|
||||
try
|
||||
{
|
||||
$response = $this->url->setQueryString( 'include_type_name', 'true' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->put( json_encode( $definition ) );
|
||||
|
||||
if( $response->httpResponseCode != 200 )
|
||||
{
|
||||
throw new \RuntimeException;
|
||||
}
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
$response = $this->url->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->put( json_encode( $definition ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,15 +175,13 @@ class _Index extends \IPS\Content\Search\Index
|
||||
{
|
||||
try
|
||||
{
|
||||
$class = get_class( $object );
|
||||
|
||||
$existingData = NULL;
|
||||
$existingIndexId = NULL;
|
||||
$resetLastComment = FALSE;
|
||||
|
||||
try
|
||||
{
|
||||
$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();
|
||||
$r = $this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_doc/' . $this->getIndexId( $object ) )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->get()->decodeJson();
|
||||
if ( $r['found'] )
|
||||
{
|
||||
$existingData = $r['_source'];
|
||||
@@ -173,65 +199,22 @@ class _Index extends \IPS\Content\Search\Index
|
||||
}
|
||||
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']
|
||||
)
|
||||
),
|
||||
)
|
||||
)
|
||||
)
|
||||
) ) );
|
||||
/* We have a new "last comment" */
|
||||
$resetLastComment = TRUE;
|
||||
}
|
||||
|
||||
/* Insert into index */
|
||||
$r = $this->url
|
||||
->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/' . $this->getIndexId( $object ) )
|
||||
->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_doc/' . $this->getIndexId( $object ) )
|
||||
->request( \IPS\LONG_REQUEST_TIMEOUT )
|
||||
->setHeaders( array( 'Content-Type' => 'application/json' ) )
|
||||
->put( json_encode( $indexData ) );
|
||||
|
||||
|
||||
if( $error = $this->getResponseError( $r ) )
|
||||
{
|
||||
throw new \IPS\Content\Search\Elastic\Exception( $error['type'] . ' ' . $error['type'] );
|
||||
}
|
||||
|
||||
/* Views / Comments / Reviews */
|
||||
if ( $object instanceof \IPS\Content\Item )
|
||||
{
|
||||
@@ -247,6 +230,10 @@ class _Index extends \IPS\Content\Search\Index
|
||||
{
|
||||
\IPS\Log::log( $e, 'elasticsearch' );
|
||||
}
|
||||
catch ( \IPS\Content\Search\Elastic\Exception $e )
|
||||
{
|
||||
\IPS\Log::log( $e, 'elasticsearch_response_error' );
|
||||
}
|
||||
|
||||
if ( $resetLastComment )
|
||||
{
|
||||
@@ -256,50 +243,6 @@ class _Index extends \IPS\Content\Search\Index
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update view count
|
||||
*
|
||||
* @param string $class Class to update
|
||||
* @param int $id ID of item
|
||||
* @param int $count Count to update
|
||||
* @throws \OutOfRangeException When table to update no longer exists
|
||||
*/
|
||||
public function updateViewCounts( $class, $id, $count )
|
||||
{
|
||||
$classes = array( $class );
|
||||
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( \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'
|
||||
),
|
||||
'query' => array(
|
||||
'bool' => array(
|
||||
'must' => array(
|
||||
array(
|
||||
'terms' => array(
|
||||
'index_class' => $classes
|
||||
)
|
||||
),
|
||||
array(
|
||||
'term' => array(
|
||||
'index_item_id' => $id
|
||||
)
|
||||
),
|
||||
)
|
||||
)
|
||||
)
|
||||
) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear out any tasks associated with the search index method
|
||||
*
|
||||
@@ -310,7 +253,14 @@ class _Index extends \IPS\Content\Search\Index
|
||||
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();
|
||||
$response = $this->url->setPath( '/_tasks' )->request( \IPS\LONG_REQUEST_TIMEOUT )->setHeaders( array( 'Content-Type' => 'application/json' ) )->get();
|
||||
|
||||
if( $error = $this->getResponseError( $response ) )
|
||||
{
|
||||
throw new \IPS\Content\Search\Elastic\Exception( $error['type'] . ' ' . $error['type'] );
|
||||
}
|
||||
|
||||
$response = $response->decodeJson();
|
||||
|
||||
foreach( $response['nodes'] as $nodeId => $nodeData )
|
||||
{
|
||||
@@ -332,6 +282,10 @@ class _Index extends \IPS\Content\Search\Index
|
||||
}
|
||||
}
|
||||
}
|
||||
catch ( \IPS\Content\Search\Elastic\Exception $e )
|
||||
{
|
||||
\IPS\Log::log( $e, 'elasticsearch_response_error' );
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
\IPS\Log::log( $e, 'elasticsearch' );
|
||||
@@ -362,7 +316,6 @@ class _Index extends \IPS\Content\Search\Index
|
||||
$participants = array_values( array_unique( $participants ) );
|
||||
|
||||
return array(
|
||||
'index_views' => $item->mapped('views'),
|
||||
'index_comments' => $item->mapped('num_comments'),
|
||||
'index_reviews' => $item->mapped('num_reviews'),
|
||||
'index_participants' => $participants
|
||||
@@ -379,7 +332,7 @@ class _Index extends \IPS\Content\Search\Index
|
||||
protected function rebuildMetaCounts( $item, $lastCommentIndexId = NULL )
|
||||
{
|
||||
$databaseColumnId = $item::$databaseColumnId;
|
||||
$class = get_class( $item );
|
||||
$class = \get_class( $item );
|
||||
$classes = array( $class );
|
||||
if ( isset( $class::$commentClass ) )
|
||||
{
|
||||
@@ -392,30 +345,36 @@ class _Index extends \IPS\Content\Search\Index
|
||||
|
||||
try
|
||||
{
|
||||
$updates = array();
|
||||
$updates = array();
|
||||
$params = array();
|
||||
foreach ( $this->metaCounts( $item ) as $k => $v )
|
||||
{
|
||||
if ( is_array( $v ) )
|
||||
if ( \is_array( $v ) )
|
||||
{
|
||||
$updates[] = "ctx._source.{$k} = [" . implode( ',', array_map( 'intval', $v ) ) . "];";
|
||||
$updates[] = "ctx._source.{$k} = params.param_{$k};";
|
||||
$params[ 'param_' . $k ] = array_map( 'intval', $v );
|
||||
}
|
||||
elseif ( is_null( $v ) )
|
||||
elseif ( \is_null( $v ) )
|
||||
{
|
||||
$updates[] = "ctx._source.{$k} = null;";
|
||||
$updates[] = "ctx._source.{$k} = params.param_{$k};";
|
||||
$params['param_' . $k ] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
$updates[] = "ctx._source.{$k} = " . intval( $v ) . ';';
|
||||
$updates[] = "ctx._source.{$k} = params.param_{$k};";
|
||||
$params['param_' . $k ] = \intval( $v );
|
||||
}
|
||||
}
|
||||
if ( !is_null( $lastCommentIndexId ) )
|
||||
if ( !\is_null( $lastCommentIndexId ) )
|
||||
{
|
||||
$updates[] = "ctx._source.index_is_last_comment = ( ctx._id == '{$lastCommentIndexId}' );";
|
||||
$updates[] = "ctx._source.index_is_last_comment = ( ctx._id == params.param_lastcommentid );";
|
||||
$params['param_lastcommentid'] = $lastCommentIndexId;
|
||||
}
|
||||
$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(
|
||||
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_doc/_update_by_query' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false', 'scroll_size' => \IPS\Settings::i()->search_index_maxresults ) )->request( \IPS\LONG_REQUEST_TIMEOUT )->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
|
||||
'script' => array(
|
||||
'inline' => implode( ' ', $updates ),
|
||||
'lang' => 'painless'
|
||||
'source' => implode( ' ', $updates ),
|
||||
'lang' => 'painless',
|
||||
'params' => $params
|
||||
),
|
||||
'query' => array(
|
||||
'bool' => array(
|
||||
@@ -450,7 +409,7 @@ class _Index extends \IPS\Content\Search\Index
|
||||
public function getIndexId( \IPS\Content\Searchable $object )
|
||||
{
|
||||
$databaseColumnId = $object::$databaseColumnId;
|
||||
return \strtolower( str_replace( '\\', '_', \substr( get_class( $object ), 4 ) ) ) . '-' . $object->$databaseColumnId;
|
||||
return \strtolower( str_replace( '\\', '_', \substr( \get_class( $object ), 4 ) ) ) . '-' . $object->$databaseColumnId;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -463,16 +422,57 @@ class _Index extends \IPS\Content\Search\Index
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/' . $this->getIndexId( $object ) )->request()->delete();
|
||||
$class = \get_class( $object );
|
||||
$idColumn = $class::$databaseColumnId;
|
||||
|
||||
$this->directIndexRemoval( $class, $object->$idColumn );
|
||||
|
||||
if ( !( $object instanceof \IPS\Content\Item ) )
|
||||
{
|
||||
$this->rebuildMetaCounts( $object->item() );
|
||||
|
||||
$itemClass = \get_class( $object->item() );
|
||||
$itemIdColumn = $itemClass::$databaseColumnId;
|
||||
|
||||
$classes = array( $itemClass );
|
||||
if ( isset( $itemClass::$commentClass ) )
|
||||
{
|
||||
$classes[] = $itemClass::$commentClass;
|
||||
}
|
||||
if ( isset( $itemClass::$reviewClass ) )
|
||||
{
|
||||
$classes[] = $itemClass::$reviewClass;
|
||||
}
|
||||
|
||||
$this->resetLastComment( $classes, $object->item()->$itemIdColumn, $object->$idColumn );
|
||||
}
|
||||
}
|
||||
catch ( \IPS\Http\Request\Exception $e )
|
||||
{
|
||||
\IPS\Log::log( $e, 'elasticsearch' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 )
|
||||
{
|
||||
try
|
||||
{
|
||||
$indexId = \strtolower( str_replace( '\\', '_', \substr( $class, 4 ) ) ) . '-' . $id;
|
||||
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_doc/' . $indexId )->request()->delete();
|
||||
|
||||
if ( $object instanceof \IPS\Content\Item )
|
||||
if ( is_subclass_of( $class, 'IPS\Content\Item' ) )
|
||||
{
|
||||
$class = get_class( $object );
|
||||
$idColumn = $class::$databaseColumnId;
|
||||
if ( isset( $class::$commentClass ) )
|
||||
{
|
||||
$commentClass = $class::$commentClass;
|
||||
$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(
|
||||
$response = $this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_doc/_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(
|
||||
@@ -483,7 +483,7 @@ class _Index extends \IPS\Content\Search\Index
|
||||
),
|
||||
array(
|
||||
'term' => array(
|
||||
'index_item_id' => $object->$idColumn
|
||||
'index_item_id' => $id
|
||||
)
|
||||
),
|
||||
)
|
||||
@@ -495,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' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false' ) )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
|
||||
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_doc/_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(
|
||||
@@ -506,7 +506,7 @@ class _Index extends \IPS\Content\Search\Index
|
||||
),
|
||||
array(
|
||||
'term' => array(
|
||||
'index_item_id' => $object->$idColumn
|
||||
'index_item_id' => $id
|
||||
)
|
||||
),
|
||||
)
|
||||
@@ -516,10 +516,6 @@ class _Index extends \IPS\Content\Search\Index
|
||||
) ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->rebuildMetaCounts( $object->item() );
|
||||
}
|
||||
}
|
||||
catch ( \IPS\Http\Request\Exception $e )
|
||||
{
|
||||
@@ -571,13 +567,13 @@ class _Index extends \IPS\Content\Search\Index
|
||||
);
|
||||
}
|
||||
|
||||
$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(
|
||||
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_doc/_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' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false' ) )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
|
||||
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_doc/_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
|
||||
@@ -642,42 +638,50 @@ class _Index extends \IPS\Content\Search\Index
|
||||
);
|
||||
}
|
||||
|
||||
$updates = array();
|
||||
$updates = array();
|
||||
$params = array();
|
||||
if ( $newPermissions !== NULL )
|
||||
{
|
||||
$updates[] = "ctx._source.index_permissions = " . json_encode( explode( ',', $newPermissions ) ) . ";";
|
||||
$updates[] = "ctx._source.index_permissions = params.params_indexpermissions;";
|
||||
$params['params_indexpermissions'] = explode( ',', $newPermissions );
|
||||
}
|
||||
if ( $newContainer )
|
||||
{
|
||||
$updates[] = "ctx._source.index_container_id = " . intval( $newContainer ) . ";";
|
||||
$updates[] = "ctx._source.index_container_id = params.params_indexcontainer;";
|
||||
$params['params_indexcontainer'] = \intval( $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
|
||||
{
|
||||
$updates[] = "ctx._source.index_club_id = " . intval( $containerClass::load( $newContainer )->$clubIdColumn ) . ";";
|
||||
$updates[] = "ctx._source.index_club_id = params.params_indexclub;";
|
||||
$params['params_indexclub'] = \intval( $containerClass::load( $newContainer )->$clubIdColumn );
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
$updates[] = "ctx._source.index_club_id = null;";
|
||||
$updates[] = "ctx._source.index_club_id = params.params_indexclub;";
|
||||
$params['params_indexclub'] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( $newItemId )
|
||||
{
|
||||
$updates[] = "ctx._source.index_item_id = " . intval( $newItemId ) . ";";
|
||||
$updates[] = "ctx._source.index_item_id = params.params_indexitem;";
|
||||
$params['params_indexitem'] = \intval( $newItemId );
|
||||
}
|
||||
if ( $newItemAuthorId )
|
||||
{
|
||||
$updates[] = "ctx._source.index_item_author = " . intval( $newItemAuthorId ) . ";";
|
||||
$updates[] = "ctx._source.index_item_author = params.params_indexauthor;";
|
||||
$params['params_indexauthor'] = \intval( $newItemAuthorId );
|
||||
}
|
||||
|
||||
if ( count( $updates ) )
|
||||
if ( \count( $updates ) )
|
||||
{
|
||||
$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(
|
||||
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_doc/_update_by_query' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false', 'scroll_size' => \IPS\Settings::i()->search_index_maxresults ) )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
|
||||
'script' => array(
|
||||
'inline' => implode( ' ', $updates ),
|
||||
'lang' => 'painless'
|
||||
'source' => implode( ' ', $updates ),
|
||||
'lang' => 'painless',
|
||||
'params' => $params
|
||||
),
|
||||
'query' => array(
|
||||
'bool' => $conditions
|
||||
@@ -694,9 +698,9 @@ class _Index extends \IPS\Content\Search\Index
|
||||
)
|
||||
);
|
||||
|
||||
$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(
|
||||
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_doc/_update_by_query' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false', 'scroll_size' => \IPS\Settings::i()->search_index_maxresults ) )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
|
||||
'script' => array(
|
||||
'inline' => "ctx._source.index_permissions.add( 'm.' + ctx._source.index_author );",
|
||||
'source' => "ctx._source.index_permissions.add( 'm' + ctx._source.index_author );",
|
||||
'lang' => 'painless'
|
||||
),
|
||||
'query' => array(
|
||||
@@ -724,10 +728,11 @@ class _Index extends \IPS\Content\Search\Index
|
||||
);
|
||||
}
|
||||
|
||||
$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(
|
||||
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_doc/_update_by_query' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false', 'scroll_size' => \IPS\Settings::i()->search_index_maxresults ) )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
|
||||
'script' => array(
|
||||
'inline' => "ctx._source.index_hidden = " . intval( $newHiddenStatus ) . ";",
|
||||
'lang' => 'painless'
|
||||
'source' => "ctx._source.index_hidden = params.newHiddenStatus;",
|
||||
'lang' => 'painless',
|
||||
'params' => array( 'newHiddenStatus' => \intval( $newHiddenStatus ) )
|
||||
),
|
||||
'query' => array(
|
||||
'bool' => $conditions
|
||||
@@ -749,11 +754,11 @@ class _Index extends \IPS\Content\Search\Index
|
||||
*/
|
||||
public static function convertConditionsToQuery( $conditions )
|
||||
{
|
||||
if ( count( $conditions ) == 1 )
|
||||
if ( \count( $conditions ) == 1 )
|
||||
{
|
||||
return $conditions[0];
|
||||
}
|
||||
elseif ( count( $conditions ) == 0 )
|
||||
elseif ( \count( $conditions ) == 0 )
|
||||
{
|
||||
return array( 'match_all' => new \StdClass );
|
||||
}
|
||||
@@ -790,7 +795,7 @@ class _Index extends \IPS\Content\Search\Index
|
||||
|
||||
try
|
||||
{
|
||||
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/content/' . $this->getIndexId( $item ) . '/_update' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
|
||||
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_doc/' . $this->getIndexId( $item ) . '/_update' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
|
||||
'doc' => $update
|
||||
) ) );
|
||||
|
||||
@@ -822,7 +827,7 @@ class _Index extends \IPS\Content\Search\Index
|
||||
{
|
||||
try
|
||||
{
|
||||
$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(
|
||||
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_doc/_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(
|
||||
@@ -848,16 +853,17 @@ 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 )
|
||||
{
|
||||
try
|
||||
{
|
||||
/* Remove the flag */
|
||||
$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(
|
||||
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_doc/_update_by_query' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false', 'scroll_size' => \IPS\Settings::i()->search_index_maxresults ) )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
|
||||
'script' => array(
|
||||
'inline' => "ctx._source.index_is_last_comment = false;",
|
||||
'source' => "ctx._source.index_is_last_comment = false;",
|
||||
'lang' => 'painless'
|
||||
),
|
||||
'query' => array(
|
||||
@@ -873,6 +879,11 @@ class _Index extends \IPS\Content\Search\Index
|
||||
'index_item_id' => $indexItemId
|
||||
)
|
||||
),
|
||||
array(
|
||||
'term' => array(
|
||||
'index_is_last_comment' => true
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -882,7 +893,7 @@ class _Index extends \IPS\Content\Search\Index
|
||||
$itemClass = NULL;
|
||||
foreach ( $classes as $class )
|
||||
{
|
||||
if ( in_array( 'IPS\Content\Item', class_parents( $class ) ) )
|
||||
if ( \in_array( 'IPS\Content\Item', class_parents( $class ) ) )
|
||||
{
|
||||
$itemClass = $class;
|
||||
break;
|
||||
@@ -898,10 +909,19 @@ class _Index extends \IPS\Content\Search\Index
|
||||
{
|
||||
$item = $itemClass::load( $indexItemId );
|
||||
|
||||
if ( $lastComment = $item->comments( 1, 0, 'date', 'desc', NULL, FALSE ) )
|
||||
$where = NULL;
|
||||
if( $ignoreId !== NULL )
|
||||
{
|
||||
$commentClass = $itemClass::$commentClass;
|
||||
$commentIdColumn = $commentClass::$databaseColumnId;
|
||||
|
||||
$where = array( $commentClass::$databaseTable . '.' . $commentClass::$databasePrefix . $commentIdColumn . '<>?', $ignoreId );
|
||||
}
|
||||
|
||||
if ( $lastComment = $item->comments( 1, 0, 'date', 'desc', NULL, FALSE, NULL, $where ) AND $lastComment instanceof \IPS\Content\Searchable )
|
||||
{
|
||||
/* 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(
|
||||
$r = $this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_doc/' . $this->getIndexId( $lastComment ) . '/_update' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->post( json_encode( array(
|
||||
'doc' => array(
|
||||
'index_is_last_comment' => true
|
||||
)
|
||||
@@ -909,10 +929,14 @@ class _Index extends \IPS\Content\Search\Index
|
||||
|
||||
/* 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(
|
||||
$this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_doc/_update_by_query' )->setQueryString( array( 'conflicts' => 'proceed', 'wait_for_completion' => 'false', 'scroll_size' => \IPS\Settings::i()->search_index_maxresults ) )->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'
|
||||
'source' => "ctx._source.index_date_updated = params.dateUpdated; ctx._source.index_date_commented = params.dateCommented;",
|
||||
'lang' => 'painless',
|
||||
'params' => array(
|
||||
'dateUpdated' => \intval( $indexDataForLastComment['index_date_updated'] ),
|
||||
'dateCommented' => \intval( $indexDataForLastComment['index_date_commented'] )
|
||||
)
|
||||
),
|
||||
'query' => array(
|
||||
'bool' => array(
|
||||
@@ -938,7 +962,7 @@ class _Index extends \IPS\Content\Search\Index
|
||||
) ) );
|
||||
}
|
||||
}
|
||||
catch ( \OutOfRangeException $e ) { }
|
||||
catch ( \OutOfRangeException $e ) {}
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
@@ -977,22 +1001,23 @@ class _Index extends \IPS\Content\Search\Index
|
||||
);
|
||||
|
||||
/* 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(
|
||||
$count = $this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_doc/_search' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->get( json_encode( array(
|
||||
'size' => 0,
|
||||
'query' => $query
|
||||
) ) )->decodeJson();
|
||||
if ( !$count['hits']['total'] )
|
||||
$total = $count['hits']['total']['value'] ?? $count['hits']['total'];
|
||||
if ( !$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(
|
||||
$results = $this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_doc/_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']
|
||||
'size' => $total
|
||||
)
|
||||
)
|
||||
),
|
||||
@@ -1020,15 +1045,16 @@ class _Index extends \IPS\Content\Search\Index
|
||||
/**
|
||||
* Given a list of "index_class_type_id_hash"s, return the ones that a given member has permission to view
|
||||
*
|
||||
* @param array $itemIndexIds Item index IDs
|
||||
* @param array $hashes Item index hashes
|
||||
* @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 )
|
||||
{
|
||||
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(
|
||||
$results = $this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_doc/_search' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->get( json_encode( array(
|
||||
'query' => array(
|
||||
'bool' => array(
|
||||
'filter' => array(
|
||||
@@ -1049,7 +1075,8 @@ class _Index extends \IPS\Content\Search\Index
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
'size' => $limit ?: 10 // If we define a limit, use that, otherwise default to 10 which is ElasticSearch's default
|
||||
) ) )->decodeJson();
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
@@ -1076,7 +1103,7 @@ 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(
|
||||
$results = $this->url->setPath( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ] . '/_doc/_search' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->get( json_encode( array(
|
||||
'size' => 1,
|
||||
'sort' => array( array( 'index_date_updated' => 'asc' ) )
|
||||
) ) )->decodeJson();
|
||||
@@ -1103,16 +1130,23 @@ class _Index extends \IPS\Content\Search\Index
|
||||
*/
|
||||
public function stemmedTerms( $terms )
|
||||
{
|
||||
$analyzer = \IPS\Settings::i()->search_elastic_analyzer;
|
||||
if ( $analyzer === 'custom' )
|
||||
{
|
||||
$analysisSettings = json_decode( '{' . \IPS\Settings::i()->search_elastic_custom_analyzer . '}', TRUE );
|
||||
$analyzer = key( $analysisSettings['analyzer'] );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$results = $this->url->setPath( '/_analyze' )->request()->setHeaders( array( 'Content-Type' => 'application/json' ) )->get( json_encode( array(
|
||||
'analyzer' => \IPS\Settings::i()->search_elastic_analyzer,
|
||||
'analyzer' => $analyzer,
|
||||
'text' => implode( ' ', $terms )
|
||||
) ) )->decodeJson();
|
||||
|
||||
if ( isset( $results['tokens'] ) )
|
||||
{
|
||||
$stemmed = array();
|
||||
$stemmed = $terms;
|
||||
foreach ( $results['tokens'] as $token )
|
||||
{
|
||||
$stemmed[] = $token['token'];
|
||||
@@ -1127,4 +1161,31 @@ class _Index extends \IPS\Content\Search\Index
|
||||
return $terms;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports filtering by views?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supportViewFiltering()
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check response to see if an error was produced
|
||||
*
|
||||
* @param \IPS\Http\Response $response Response object
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getResponseError( \IPS\Http\Response $response )
|
||||
{
|
||||
/* Log any errors */
|
||||
if( $response->httpResponseCode != 200 AND $content = $response->decodeJson() AND isset( $content['error'] ) )
|
||||
{
|
||||
return $content['error'];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace IPS\Content\Search\Elastic;
|
||||
|
||||
/* 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;
|
||||
@@ -22,6 +22,9 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
*/
|
||||
class _MassIndexer extends \IPS\Content\Search\Elastic\Index
|
||||
{
|
||||
/**
|
||||
* @brief Data to store in bulk
|
||||
*/
|
||||
protected $data = array();
|
||||
|
||||
/**
|
||||
@@ -46,7 +49,7 @@ class _MassIndexer extends \IPS\Content\Search\Elastic\Index
|
||||
$this->data[] = array(
|
||||
'index' => array(
|
||||
'_index' => trim( $this->url->data[ \IPS\Http\Url::COMPONENT_PATH ], '/' ),
|
||||
'_type' => 'content',
|
||||
'_type' => '_doc',
|
||||
'_id' => $this->getIndexId( $object ),
|
||||
)
|
||||
);
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace IPS\Content\Search\Elastic;
|
||||
|
||||
/* 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;
|
||||
@@ -174,8 +174,8 @@ class _Query extends \IPS\Content\Search\Query
|
||||
}
|
||||
}
|
||||
|
||||
/* Minimum views / comments / reviews */
|
||||
foreach ( array( 'minimumViews' => 'index_views', 'minimumComments' => 'index_comments', 'minimumReviews' => 'index_reviews' ) as $filterKey => $indexKey )
|
||||
/* Minimum comments / reviews */
|
||||
foreach ( array( 'minimumComments' => 'index_comments', 'minimumReviews' => 'index_reviews' ) as $filterKey => $indexKey )
|
||||
{
|
||||
if ( $filter->$filterKey )
|
||||
{
|
||||
@@ -204,11 +204,14 @@ class _Query extends \IPS\Content\Search\Query
|
||||
}
|
||||
|
||||
/* Put it together */
|
||||
$contentFilterConditions[] = Index::convertConditionsToQuery( $conditions );
|
||||
if( \count( $conditions ) )
|
||||
{
|
||||
$contentFilterConditions[] = Index::convertConditionsToQuery( $conditions );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Put them together */
|
||||
if ( count( $contentFilterConditions ) > 1 )
|
||||
if ( \count( $contentFilterConditions ) > 1 )
|
||||
{
|
||||
$this->filters[] = array(
|
||||
'bool' => array(
|
||||
@@ -218,11 +221,17 @@ class _Query extends \IPS\Content\Search\Query
|
||||
}
|
||||
elseif ( $type )
|
||||
{
|
||||
$this->filters[] = $contentFilterConditions[0];
|
||||
if( \count( $contentFilterConditions ) )
|
||||
{
|
||||
$this->filters[] = $contentFilterConditions[0];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->mustNot[] = $contentFilterConditions[0];
|
||||
if( \count( $contentFilterConditions ) )
|
||||
{
|
||||
$this->mustNot[] = $contentFilterConditions[0];
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -236,7 +245,7 @@ class _Query extends \IPS\Content\Search\Query
|
||||
*/
|
||||
public function filterByAuthor( $author )
|
||||
{
|
||||
if ( is_array( $author ) )
|
||||
if ( \is_array( $author ) )
|
||||
{
|
||||
$this->filters[] = array( 'terms' => array( 'index_author' => $author ) );
|
||||
}
|
||||
@@ -262,7 +271,7 @@ class _Query extends \IPS\Content\Search\Query
|
||||
'exists' => array( 'field' => 'index_club_id' )
|
||||
);
|
||||
}
|
||||
elseif ( is_array( $club ) )
|
||||
elseif ( \is_array( $club ) )
|
||||
{
|
||||
$this->filters[] = array(
|
||||
'terms' => array( 'index_club_id' => $club )
|
||||
@@ -274,6 +283,25 @@ class _Query extends \IPS\Content\Search\Query
|
||||
'term' => 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;
|
||||
}
|
||||
@@ -318,7 +346,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;
|
||||
}
|
||||
@@ -399,7 +427,7 @@ class _Query extends \IPS\Content\Search\Query
|
||||
*
|
||||
* @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 $includeContainers Include content posted by members 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 )
|
||||
@@ -477,7 +505,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'];
|
||||
}
|
||||
@@ -537,7 +565,7 @@ class _Query extends \IPS\Content\Search\Query
|
||||
}
|
||||
|
||||
/* Put it all together */
|
||||
if ( count( $conditions ) )
|
||||
if ( \count( $conditions ) )
|
||||
{
|
||||
$this->filters[] = array( 'bool' => array( 'should' => $conditions ) );
|
||||
}
|
||||
@@ -646,7 +674,7 @@ class _Query extends \IPS\Content\Search\Query
|
||||
|
||||
/* And get the times each individual item was read for later */
|
||||
$items = $this->member->markersItems( $application, \IPS\Content\Item::makeMarkerKey( $containerId ) );
|
||||
if ( count( $items ) )
|
||||
if ( \count( $items ) )
|
||||
{
|
||||
foreach( $items as $mid => $mtime )
|
||||
{
|
||||
@@ -656,10 +684,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 ] );
|
||||
@@ -704,7 +732,7 @@ class _Query extends \IPS\Content\Search\Query
|
||||
}
|
||||
|
||||
$notIn = array();
|
||||
if ( count( $markers ) )
|
||||
if ( \count( $markers ) )
|
||||
{
|
||||
$useIds = array_flip( $markers );
|
||||
|
||||
@@ -713,7 +741,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 )
|
||||
{
|
||||
@@ -726,7 +754,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 )
|
||||
{
|
||||
@@ -735,7 +763,7 @@ class _Query extends \IPS\Content\Search\Query
|
||||
if ( $useIds[ $row['_id'] ] >= $row['_date'] )
|
||||
{
|
||||
/* Still read */
|
||||
$notIn[] = intval( $row['_id'] );
|
||||
$notIn[] = \intval( $row['_id'] );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -747,7 +775,7 @@ class _Query extends \IPS\Content\Search\Query
|
||||
'filter' => $classConditions
|
||||
)
|
||||
);
|
||||
if ( count( $notIn ) )
|
||||
if ( \count( $notIn ) )
|
||||
{
|
||||
$_condition['bool']['must_not'] = array(
|
||||
'terms' => array(
|
||||
@@ -760,7 +788,7 @@ class _Query extends \IPS\Content\Search\Query
|
||||
}
|
||||
|
||||
/* Put it all together */
|
||||
if ( count( $conditions ) )
|
||||
if ( \count( $conditions ) )
|
||||
{
|
||||
$this->filters[] = array(
|
||||
'bool' => array(
|
||||
@@ -907,7 +935,7 @@ 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 )
|
||||
if( ( $this->offset + $this->resultsToGet ) > \IPS\Settings::i()->search_index_maxresults )
|
||||
{
|
||||
return new \IPS\Content\Search\Results( array(), 0 );
|
||||
}
|
||||
@@ -920,11 +948,12 @@ class _Query extends \IPS\Content\Search\Query
|
||||
if ( $term !== NULL or $tags !== NULL )
|
||||
{
|
||||
$searchConditions = array();
|
||||
$titleField = \IPS\Settings::i()->search_title_boost ? ( 'index_title^' . intval( \IPS\Settings::i()->search_title_boost ) ) : 'index_title';
|
||||
$titleField = \IPS\Settings::i()->search_title_boost ? ( 'index_title^' . \intval( \IPS\Settings::i()->search_title_boost ) ) : 'index_title';
|
||||
|
||||
/* Build the condition for the search term */
|
||||
if ( $term !== NULL )
|
||||
{
|
||||
/* If term is in "quotes" handle it as a phrase */
|
||||
if ( static::termIsPhrase( $term ) )
|
||||
{
|
||||
$term = trim( $term, '"' );
|
||||
@@ -937,6 +966,30 @@ class _Query extends \IPS\Content\Search\Query
|
||||
$searchConditions[] = array( 'multi_match' => array( 'query' => $term, 'fields' => array( 'index_content', $titleField ), 'type' => 'phrase' ) );
|
||||
}
|
||||
}
|
||||
/* If term contains * wildcard, but doesn't start with it (from elasticsearch docs: "In order to prevent extremely slow wildcard queries, a wildcard term should not start with one of the wildcards * or ?"), handle it as a wildcard search - NOTE this will not use the analyzer */
|
||||
elseif ( preg_match( '/^[^\s\*].*\*.*$/', $term ) )
|
||||
{
|
||||
if ( $method & static::TERM_TITLES_ONLY )
|
||||
{
|
||||
$searchConditions[] = array( 'wildcard' => array( 'index_title' => array( 'value' => $term ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
$searchConditions[] = array(
|
||||
'bool' => array(
|
||||
'should' => array(
|
||||
array(
|
||||
'wildcard' => array( 'index_title' => ( \IPS\Settings::i()->search_title_boost ? array( 'value' => $term, 'boost' => \intval( \IPS\Settings::i()->search_title_boost ) ) : array( 'value' => $term ) ) )
|
||||
),
|
||||
array(
|
||||
'wildcard' => array( 'index_content' => array( 'value' => $term ) )
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
/* Otherwise just do it as a match search */
|
||||
else
|
||||
{
|
||||
if ( $method & static::TERM_TITLES_ONLY )
|
||||
@@ -988,7 +1041,7 @@ class _Query extends \IPS\Content\Search\Query
|
||||
$filters[] = array( 'terms' => array( 'index_permissions' => array_merge( $this->permissionArray(), array( '*' ) ) ) );
|
||||
if ( $this->hiddenStatuses !== NULL )
|
||||
{
|
||||
if ( is_array( $this->hiddenStatuses ) )
|
||||
if ( \is_array( $this->hiddenStatuses ) )
|
||||
{
|
||||
$filters[] = array( 'terms' => array( 'index_hidden' => $this->hiddenStatuses ) );
|
||||
}
|
||||
@@ -1018,8 +1071,9 @@ class _Query extends \IPS\Content\Search\Query
|
||||
'query' => $query,
|
||||
'linear' => array(
|
||||
'index_date_updated' => array(
|
||||
'scale' => intval( \IPS\Settings::i()->search_decay_days ) . 'd',
|
||||
'decay' => number_format( \IPS\Settings::i()->search_decay_factor, 1, '.', '' )
|
||||
'scale' => \intval( \IPS\Settings::i()->search_decay_days ) . 'd',
|
||||
'decay' => number_format( \IPS\Settings::i()->search_decay_factor, 1, '.', '' ),
|
||||
'origin' => time(),
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -1034,8 +1088,12 @@ class _Query extends \IPS\Content\Search\Query
|
||||
'query' => $query,
|
||||
'script_score' => array(
|
||||
'script' => array(
|
||||
'inline' => "doc['index_author'].value == " . intval( $this->member->member_id ) . " ? ( _score * " . number_format( \IPS\Settings::i()->search_elastic_self_boost, 1, '.', '' ) . " ) : _score",
|
||||
'lang' => 'painless'
|
||||
'source' => "doc['index_author'].value == params.param_memberId ? ( _score * Float.parseFloat( params.param_booster ) ) : _score",
|
||||
'lang' => 'painless',
|
||||
'params' => array(
|
||||
'param_memberId' => \intval( $this->member->member_id ),
|
||||
'param_booster' => number_format( \IPS\Settings::i()->search_elastic_self_boost, 1, '.', '' )
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -1057,7 +1115,7 @@ class _Query extends \IPS\Content\Search\Query
|
||||
{
|
||||
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'] ) )
|
||||
@@ -1065,14 +1123,15 @@ class _Query extends \IPS\Content\Search\Query
|
||||
\IPS\Log::log( print_r( $return['error'], TRUE ), 'elasticsearch' );
|
||||
return new \IPS\Content\Search\Results( array(), 0 );
|
||||
}
|
||||
|
||||
/* Set results */
|
||||
|
||||
/* Set results */
|
||||
$total = $return['hits']['total']['value'] ?? $return['hits']['total'];
|
||||
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'] <= \IPS\Settings::i()->search_index_maxresults ? $return['hits']['total'] : (int) \IPS\Settings::i()->search_index_maxresults );
|
||||
}, $return['hits']['hits'] ), $total <= \IPS\Settings::i()->search_index_maxresults ? $total : (int) \IPS\Settings::i()->search_index_maxresults );
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user