Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions classes/engine.php
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ public function index_starting($fullindex = false) {
* @return array A two element array, the first is the total number of availble results, the second is an array
* of documents for the current request.
*/
private function get_indexed_files($document, $start = 0, $rows = 500) {
protected function get_indexed_files($document, $start = 0, $rows = 500) {
$url = $this->get_url();
$indexeurl = $url . '/' . $this->config->index . '/_search';
$client = new \search_elastic\esrequest();
Expand Down Expand Up @@ -505,7 +505,7 @@ private function filter_indexed_files($document) {
// Filelib does not guarantee time modified is updated, so we will check important values.
$unchanged = $indexedfile->_source->modified == $files[$originalfileid]->get_timemodified()
&& strcmp($indexedfile->_source->title, $files[$originalfileid]->get_filename()) === 0
&& $indexedfile->_source->filecontenthash != $files[$originalfileid]->get_contenthash();
&& $indexedfile->_source->filecontenthash == $files[$originalfileid]->get_contenthash();

// If the file is already indexed and unchanged, we can just remove it from the files array and skip it.
if ($unchanged) {
Expand Down
76 changes: 76 additions & 0 deletions tests/engine_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -1429,6 +1429,82 @@ public function test_log_bulk_response_item_errors_id_mismatch(): void {
$this->assertEquals(0, $errors);
}

/**
* Test that unchanged file (same hash, time and name) should be skipped and
* not re-indexed and not deleted from the index.
*/
public function test_unchanged_file_is_skipped(): void {
$this->resetAfterTest();

$timemodified = time();
$contenthash = hash('sha1', 'testhash');

$file = $this->createMock(\stored_file::class);
$file->method('get_id')->willReturn(100000);
$file->method('get_timemodified')->willReturn($timemodified);
$file->method('get_contenthash')->willReturn($contenthash);
$file->method('get_filename')->willReturn('test.pdf');

$doc = $this->createMock(\core_search\document::class);
$doc->method('get_is_new')->willReturn(false);
$doc->method('get_files')->willReturn([100000 => $file]);
$doc->method('get')->willReturnMap([
['id', 'parent_doc_123'],
['areaid', 'core_mocksearch-mock_search_area'],
]);

// Simulate that elasticsearch already has this file with the same hash, time and name.
$hit = (object)[
'_type' => 'doc',
'_source' => (object)[
'id' => 100000,
'modified' => $timemodified,
'filecontenthash' => $contenthash,
'title' => 'test.pdf',
'original_id' => null,
],
];

$engine = new class () extends \search_elastic\testable_engine {
/**
* @var array Hits returned by get_indexed_files().
*/
private array $mockhits = [];

/**
* Set the hits that get_indexed_files() will return.
* @param array $hits
*/
public function set_indexed_files(array $hits): void {
$this->mockhits = $hits;
}

#[\Override]
public function get_indexed_files($document, $start = 0, $rows = 500): array {
return [count($this->mockhits), $this->mockhits];
}

/**
* Exposes private filter_indexed_files() for unit testing via reflection.
* @param \core_search\document $document $document
* @return array
*/
public function call_filter_indexed_files(\core_search\document $document): array {
$method = new \ReflectionMethod(engine::class, 'filter_indexed_files');
$method->setAccessible(true);
return $method->invoke($this, $document);
}
};
$engine->set_indexed_files([$hit]);
[$filestoreindex, $idstodelete] = $engine->call_filter_indexed_files($doc);

// Unchanged file should not be queued for re-indexing.
$this->assertArrayNotHasKey(100000, $filestoreindex);

// Unchanged file should not be scheduled for deletion.
$this->assertArrayNotHasKey(100000, $idstodelete);
}

/**
* Helper method to create a test payload from document arrays.
* @param array $docs
Expand Down
4 changes: 2 additions & 2 deletions version.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@

defined('MOODLE_INTERNAL') || die();

$plugin->version = 2026020900;
$plugin->release = '4.2.6 (Build: 20260209)'; // Build same as version.
$plugin->version = 2026051400;
$plugin->release = '4.2.6 (Build: 20260514)'; // Build same as version.
$plugin->requires = 2023042405;
$plugin->component = 'search_elastic';
$plugin->maturity = MATURITY_STABLE;
Expand Down
Loading