feat(search): add proper DB tag searching

This commit is contained in:
David Bailey 2024-12-09 10:50:36 +01:00
parent f9cfb81079
commit 22c953793c
4 changed files with 147 additions and 20 deletions

View file

@ -80,6 +80,16 @@ interface PostdataInterface {
$order_by = 'path');
public function get_post_markdown($id);
// Returns an array of PostData information
// based on the tag search list
//
// Tag searchlist is comprised of space-separated
// tags. Each tag can have a weighting prefix,
// and some special tags exist (such as limit:N,
// order:S).
public function search_posts($taglist);
}
?>

View file

@ -3,23 +3,7 @@
require_once 'analytics_interface.php';
require_once 'db_interface.php';
function taglist_escape_tag($tag) {
return preg_replace_callback('/[\WZ]/', function($match) {
return "Z" . ord($match[0]);
}, strtolower($tag));
}
function taglist_to_sql_string($post_tags) {
$post_tags = array_unique($post_tags);
$post_tags = array_map(function($val) {
return taglist_escape_tag($val);
}, $post_tags);
asort($post_tags);
$post_tags = join(' ', $post_tags);
return $post_tags;
}
require_once 'mysql_taglist_handling.php';
class MySQLHandler
implements PostdataInterface {
@ -128,7 +112,7 @@ class MySQLHandler
$post_path,
substr_count($post_path, "/"),
$data['title'],
taglist_to_sql_string($post_tags),
TagList\create_db_str($post_tags),
$data['brief'] ?? null
];
@ -292,8 +276,8 @@ class MySQLHandler
'path DESC' => true,
'created_at' => true,
'created_at DESC' => true,
'modified_at' => true,
'modified_at DESC' => true
'updated_at' => true,
'updated_at DESC' => true
];
if(!isset($allowed_ordering[$order_by])) {
@ -342,6 +326,52 @@ class MySQLHandler
return $data['post_markdown'];
}
public function search_posts($taglist) {
$qry = "
SELECT *
FROM posts
WHERE MATCH(post_tags) AGAINST (? IN BOOLEAN MODE)
";
$search_data = TagList\create_db_search($taglist);
$order_by = $search_data['modifiers']['order_by'] ?? 'updated_at';
$limit = intval($search_data['modifiers']['limit'] ?? 20);
$offset = intval($search_data['modifiers']['offset'] ?? 0);
if($limit > 100) {
throw new Exception('Search limit above maximum (max 100 results per search)');
}
$allowed_ordering = [
'path' => true,
'path DESC' => true,
'created_at' => true,
'created_at DESC' => true,
'updated_at' => true,
'updated_at DESC' => true
];
// TODO move this to a class var
if(!isset($allowed_ordering[$order_by])) {
throw new Exception('Search order not allowed');
}
$order_by = 'post_' . $order_by;
$qry = $qry . " ORDER BY " . $order_by . " LIMIT ? OFFSET ?";
$search_results = $this->_exec($qry, "sii", $search_data['parameter_string'],
$limit, $offset)->fetch_all(MYSQLI_ASSOC);
$outdata = [];
foreach($search_results AS $post_element) {
$outdata []=
$this->process_postdata($post_element);
}
return $outdata;
}
}
?>

View file

@ -0,0 +1,76 @@
<?php
namespace TagList;
function escape_entry($tag) {
return preg_replace_callback('/[\WZ]/', function($match) {
return "Z" . ord($match[0]);
}, strtolower($tag));
}
function escape_search_entry($tag) {
preg_match("/^([\+\-]?)([^\*]*)(\*?)$/", $tag, $matches);
if(!isset($matches[1])) {
echo "Problem with tag!";
var_dump($tag);
}
return $matches[1] . escape_entry($matches[2]) . $matches[3];
}
function _str_to_raw_taglist($taglist) {
$split_list = explode(' ', $taglist);
$split_list = array_filter($split_list, function($entry) {
return !preg_match('/^\s*$/', $entry);
});
return $split_list;
}
function create_db_str($taglist) {
if(gettype($taglist) == 'string') {
$taglist = _str_to_raw_taglist($taglist);
}
$taglist = array_unique($taglist);
$taglist = array_map(function($val) {
return escape_entry($val);
}, $taglist);
asort($taglist);
$taglist = join(' ', $taglist);
return $taglist;
}
function create_db_search($taglist) {
if(gettype($taglist) == 'string') {
$taglist = _str_to_raw_taglist($taglist);
}
$search_params = [];
$search_modifiers = [];
foreach($taglist as $tag) {
if(preg_match('/^(order|limit):(.*)$/i', $tag, $match)) {
$search_modifiers[$match[1]] = $match[2];
} else {
array_push($search_params, $tag);
}
}
$search_params = array_map(function($val) {
return escape_search_entry($val);
}, $search_params);
asort($search_params);
$search_params = join(' ', $search_params);
return [
'modifiers' => $search_modifiers,
'parameter_string' => $search_params
];
}
?>

View file

@ -56,6 +56,17 @@ class PostHandler {
return $out_list;
}
public function search_posts($search_query) {
$search_results = $this->db->search_posts($search_query);
$out_list = [];
foreach($search_results as $search_result) {
array_push($out_list, new Post($this, $search_result, $this->site_defaults));
}
return $out_list;
}
}
?>