[core] search code docs and test function
This commit is contained in:
parent
751a8f61d2
commit
ac14d1e4c6
2 changed files with 81 additions and 3 deletions
|
@ -6,6 +6,21 @@ namespace Shimmie2;
|
|||
|
||||
use GQLA\Query;
|
||||
|
||||
/**
|
||||
* A small chunk of SQL code + parameters, to be used in a larger query
|
||||
*
|
||||
* eg
|
||||
*
|
||||
* $q = new Querylet("SELECT * FROM images");
|
||||
* $q->append(new Querylet(" WHERE id = :id", ["id" => 123]));
|
||||
* $q->append(new Querylet(" AND rating = :rating", ["rating" => "safe"]));
|
||||
* $q->append(new Querylet(" ORDER BY id DESC"));
|
||||
*
|
||||
* becomes
|
||||
*
|
||||
* SELECT * FROM images WHERE id = :id AND rating = :rating ORDER BY id DESC
|
||||
* ["id" => 123, "rating" => "safe"]
|
||||
*/
|
||||
class Querylet
|
||||
{
|
||||
/**
|
||||
|
@ -25,6 +40,9 @@ class Querylet
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When somebody has searched for a tag, "cat", "cute", "-angry", etc
|
||||
*/
|
||||
class TagCondition
|
||||
{
|
||||
public function __construct(
|
||||
|
@ -34,6 +52,10 @@ class TagCondition
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When somebody has searched for a specific image property, like "rating:safe",
|
||||
* "id:123", "width:100", etc
|
||||
*/
|
||||
class ImgCondition
|
||||
{
|
||||
public function __construct(
|
||||
|
@ -45,10 +67,19 @@ class ImgCondition
|
|||
|
||||
class Search
|
||||
{
|
||||
/** @var list<string> */
|
||||
/**
|
||||
* The search code is dark and full of horrors, and it's not always clear
|
||||
* what's going on. This is a list of the steps that the search code took
|
||||
* to find the images that it returned.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
public static array $_search_path = [];
|
||||
|
||||
/**
|
||||
* Build a search query for a given set of tags and return
|
||||
* the results as a PDOStatement (raw SQL rows)
|
||||
*
|
||||
* @param list<string> $tags
|
||||
*/
|
||||
private static function find_images_internal(int $start = 0, ?int $limit = null, array $tags = []): \FFSPHP\PDOStatement
|
||||
|
@ -203,10 +234,13 @@ class Search
|
|||
/**
|
||||
* Turn a human input string into a an abstract search query
|
||||
*
|
||||
* (This is only public for testing purposes, nobody should be calling this
|
||||
* directly from outside this class)
|
||||
*
|
||||
* @param string[] $terms
|
||||
* @return array{0: TagCondition[], 1: ImgCondition[], 2: string}
|
||||
*/
|
||||
private static function terms_to_conditions(array $terms): array
|
||||
public static function terms_to_conditions(array $terms): array
|
||||
{
|
||||
global $config;
|
||||
|
||||
|
@ -234,10 +268,13 @@ class Search
|
|||
/**
|
||||
* Turn an abstract search query into an SQL Querylet
|
||||
*
|
||||
* (This is only public for testing purposes, nobody should be calling this
|
||||
* directly from outside this class)
|
||||
*
|
||||
* @param TagCondition[] $tag_conditions
|
||||
* @param ImgCondition[] $img_conditions
|
||||
*/
|
||||
private static function build_search_querylet(
|
||||
public static function build_search_querylet(
|
||||
array $tag_conditions,
|
||||
array $img_conditions,
|
||||
?string $order = null,
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace Shimmie2;
|
|||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
require_once "config.php";
|
||||
|
@ -149,6 +150,46 @@ class Index extends Extension
|
|||
foreach ($items as $item) {
|
||||
$output->writeln($item->hash);
|
||||
}
|
||||
return Command::SUCCESS;
|
||||
});
|
||||
$event->app->register('debug:search')
|
||||
->addArgument('query', InputArgument::REQUIRED)
|
||||
->addOption('count', null, InputOption::VALUE_NONE, 'Generate a count-only query')
|
||||
->addOption('page', null, InputOption::VALUE_REQUIRED, 'Page number', default: 1)
|
||||
->addOption('limit', null, InputOption::VALUE_REQUIRED, 'Number of results per page', default: 25)
|
||||
->setDescription('Show the SQL generated for a given search query')
|
||||
->setCode(function (InputInterface $input, OutputInterface $output): int {
|
||||
$search = Tag::explode($input->getArgument('query'), false);
|
||||
$page = $input->getOption('page');
|
||||
$limit = $input->getOption('limit');
|
||||
$count = $input->getOption('count');
|
||||
|
||||
[$tag_conditions, $img_conditions, $order] = Search::terms_to_conditions($search);
|
||||
if($count) {
|
||||
$order = null;
|
||||
$page = null;
|
||||
$limit = null;
|
||||
}
|
||||
|
||||
$q = Search::build_search_querylet(
|
||||
$tag_conditions,
|
||||
$img_conditions,
|
||||
$order,
|
||||
$limit,
|
||||
(int)(($page - 1) * $limit),
|
||||
);
|
||||
|
||||
$sql_str = $q->sql;
|
||||
$sql_str = preg_replace("/\s+/", " ", $sql_str);
|
||||
foreach($q->variables as $key => $val) {
|
||||
if(is_string($val)) {
|
||||
$sql_str = str_replace(":$key", "'$val'", $sql_str);
|
||||
} else {
|
||||
$sql_str = str_replace(":$key", (string)$val, $sql_str);
|
||||
}
|
||||
}
|
||||
$output->writeln(trim($sql_str));
|
||||
|
||||
return Command::SUCCESS;
|
||||
});
|
||||
}
|
||||
|
|
Reference in a new issue