[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;
|
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
|
class Querylet
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -25,6 +40,9 @@ class Querylet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When somebody has searched for a tag, "cat", "cute", "-angry", etc
|
||||||
|
*/
|
||||||
class TagCondition
|
class TagCondition
|
||||||
{
|
{
|
||||||
public function __construct(
|
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
|
class ImgCondition
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
@ -45,10 +67,19 @@ class ImgCondition
|
||||||
|
|
||||||
class Search
|
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 = [];
|
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
|
* @param list<string> $tags
|
||||||
*/
|
*/
|
||||||
private static function find_images_internal(int $start = 0, ?int $limit = null, array $tags = []): \FFSPHP\PDOStatement
|
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
|
* 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
|
* @param string[] $terms
|
||||||
* @return array{0: TagCondition[], 1: ImgCondition[], 2: string}
|
* @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;
|
global $config;
|
||||||
|
|
||||||
|
@ -234,10 +268,13 @@ class Search
|
||||||
/**
|
/**
|
||||||
* Turn an abstract search query into an SQL Querylet
|
* 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 TagCondition[] $tag_conditions
|
||||||
* @param ImgCondition[] $img_conditions
|
* @param ImgCondition[] $img_conditions
|
||||||
*/
|
*/
|
||||||
private static function build_search_querylet(
|
public static function build_search_querylet(
|
||||||
array $tag_conditions,
|
array $tag_conditions,
|
||||||
array $img_conditions,
|
array $img_conditions,
|
||||||
?string $order = null,
|
?string $order = null,
|
||||||
|
|
|
@ -6,6 +6,7 @@ namespace Shimmie2;
|
||||||
|
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
|
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
require_once "config.php";
|
require_once "config.php";
|
||||||
|
@ -149,6 +150,46 @@ class Index extends Extension
|
||||||
foreach ($items as $item) {
|
foreach ($items as $item) {
|
||||||
$output->writeln($item->hash);
|
$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;
|
return Command::SUCCESS;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue