From 687c5b557778885a58902abc0643db7017e2f082 Mon Sep 17 00:00:00 2001 From: Aaron Schulz Date: Wed, 27 Nov 2013 10:39:50 -0800 Subject: [PATCH] Optimized LocalRepo::findFiles() Change-Id: Icea9c416c973a6b83c53e5bc201aed4528e9dd34 --- includes/filerepo/FileRepo.php | 4 +- includes/filerepo/LocalRepo.php | 84 +++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/includes/filerepo/FileRepo.php b/includes/filerepo/FileRepo.php index 8611238d8e..466abe88df 100644 --- a/includes/filerepo/FileRepo.php +++ b/includes/filerepo/FileRepo.php @@ -473,7 +473,9 @@ class FileRepo { * $findItem = array( 'title' => $title, 'private' => true ); * $findBatch = array( $findItem ); * $repo->findFiles( $findBatch ); - * @return array + * + * No title should appear in $items twice, as the result use titles as keys + * @return array (Map of file names => File objects) for matches */ public function findFiles( array $items ) { $result = array(); diff --git a/includes/filerepo/LocalRepo.php b/includes/filerepo/LocalRepo.php index 49c2b8fa0e..4bbcc7d84f 100644 --- a/includes/filerepo/LocalRepo.php +++ b/includes/filerepo/LocalRepo.php @@ -229,6 +229,90 @@ class LocalRepo extends FileRepo { return $id; } + public function findFiles( array $items ) { + $finalFiles = array(); // map of (DB key => corresponding File) for matches + + $searchSet = array(); // map of (DB key => normalized search params) + foreach ( $items as $item ) { + $title = is_array( $item ) + ? File::normalizeTitle( $item['title'] ) + : File::normalizeTitle( $item ); + if ( $title ) { // valid title + $searchSet[$title->getDbKey()] = ( is_array( $item ) ? $item : array() ); + } + } + + $fileMatchesSearch = function( File $file, array $search ) { + // Note: file name comparison done elsewhere (to handle redirects) + return ( + $file->exists() && + ( + ( empty( $search['time'] ) && !$file->isOld() ) || + ( !empty( $search['time'] ) && $search['time'] === $file->getTimestamp() ) + ) && + ( !empty( $search['private'] ) || !$file->isDeleted( File::DELETED_FILE ) ) && + $file->userCan( File::DELETED_FILE ) + ); + }; + + $repo = $this; + $applyMatchingFiles = function( ResultWrapper $res, &$searchSet, &$finalFiles ) + use ( $repo, $fileMatchesSearch ) + { + foreach ( $res as $row ) { + $possFile = $repo->newFileFromRow( $row ); + $dbKey = $possFile->getName(); + // There must have been a search for this DB Key + if ( $fileMatchesSearch( $possFile, $searchSet[$dbKey] ) ) { + $finalFiles[$dbKey] = $possFile; + unset( $searchSet[$dbKey] ); + } + } + }; + + $dbr = $this->getSlaveDB(); + + // Query image table + $imgNames = array_keys( $searchSet ); + if ( count( $imgNames ) ) { + $res = $dbr->select( 'image', + LocalFile::selectFields(), array( 'img_name' => $imgNames ), __METHOD__ ); + $applyMatchingFiles( $res, $searchSet, $finalFiles ); + } + + // Query old image table + $oiConds = array(); // WHERE clause array for each file + foreach ( $searchSet as $dbKey => $search ) { + if ( isset( $search['params']['time'] ) ) { + $oiConds[] = $dbr->makeList( array( 'oi_name' => $dbKey, + 'oi_timestamp' => $dbr->timestamp( $search['params']['time'] ) ), LIST_AND ); + } + } + if ( count( $oiConds ) ) { + $res = $dbr->select( 'oldimage', + OldLocalFile::selectFields(), $dbr->makeList( $oiConds, LIST_OR ), __METHOD__ ); + $applyMatchingFiles( $res, $searchSet, $finalFiles ); + } + + // Check for redirects... + foreach ( $searchSet as $dbKey => $search ) { + if ( !empty( $search['ignoreRedirect'] ) ) { + continue; + } + $title = File::normalizeTitle( $dbKey ); + $redir = $this->checkRedirect( $title ); // hopefully hits memcached + if ( $redir && $redir->getNamespace() == NS_FILE ) { + $possFile = $this->newFile( $redir ); + if ( $possFile && $fileMatchesSearch( $possFile, $search ) ) { + $possFile->redirectedFrom( $title->getDBkey() ); + $finalFiles[$dbKey] = $possFile; + } + } + } + + return $finalFiles; + } + /** * Get an array or iterator of file objects for files that have a given * SHA-1 content hash. -- 2.20.1