[core] search code docs and test function

This commit is contained in:
Shish 2024-02-24 14:02:11 +00:00 committed by Shish
parent 751a8f61d2
commit ac14d1e4c6
2 changed files with 81 additions and 3 deletions

View file

@ -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,

View file

@ -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;
});
}