29c3c6fc8a
- Add Meilisearch v1.6 service to docker-compose.yml - Create search indexer with batch and incremental indexing - Build search API with filters (category, duration, date, content type) - Add autocomplete API with trending query suggestions - Implement modern search UI with real-time autocomplete - Add db_search_queries table for tracking trending searches - Document setup and usage in docs/MEILISEARCH.md Features: - Blazing fast search (5-15ms typical) - Advanced filters and faceted search - Real-time autocomplete suggestions - Trending queries tracking - Responsive pagination - Production-ready with proper error handling
132 lines
3.6 KiB
PHP
132 lines
3.6 KiB
PHP
<?php
|
|
/**
|
|
* Search Autocomplete API
|
|
*
|
|
* GET /api/search_autocomplete.php?q=query
|
|
*
|
|
* Returns suggestions based on:
|
|
* 1. Video titles
|
|
* 2. Trending search queries
|
|
* 3. Categories
|
|
*/
|
|
|
|
require_once dirname(__DIR__) . '/f_core/config.boot.php';
|
|
require_once dirname(__DIR__) . '/f_core/f_classes/class.db.php';
|
|
|
|
header('Content-Type: application/json');
|
|
|
|
class AutocompleteAPI {
|
|
private $meili_host;
|
|
private $meili_key;
|
|
private $db;
|
|
|
|
public function __construct() {
|
|
$this->meili_host = getenv('MEILI_HOST') ?: 'http://meilisearch:7700';
|
|
$this->meili_key = getenv('MEILI_MASTER_KEY') ?: 'changeme_meilisearch_master_key';
|
|
$this->db = new VDatabase();
|
|
}
|
|
|
|
public function autocomplete() {
|
|
$query = $_GET['q'] ?? '';
|
|
|
|
if (strlen($query) < 2) {
|
|
return $this->success([]);
|
|
}
|
|
|
|
$suggestions = [];
|
|
|
|
// Get video title suggestions from Meilisearch
|
|
$video_suggestions = $this->getVideoSuggestions($query);
|
|
|
|
// Get trending query suggestions
|
|
$trending_suggestions = $this->getTrendingSuggestions($query);
|
|
|
|
// Merge and deduplicate
|
|
$suggestions = array_merge($video_suggestions, $trending_suggestions);
|
|
$suggestions = array_unique($suggestions);
|
|
|
|
// Limit to 10 suggestions
|
|
$suggestions = array_slice($suggestions, 0, 10);
|
|
|
|
return $this->success($suggestions);
|
|
}
|
|
|
|
private function getVideoSuggestions($query) {
|
|
$params = [
|
|
'q' => $query,
|
|
'limit' => 5,
|
|
'attributesToRetrieve' => ['title'],
|
|
'filter' => 'privacy = "public"'
|
|
];
|
|
|
|
$response = $this->apiRequest('POST', '/indexes/videos/search', $params);
|
|
|
|
if (!$response || empty($response['hits'])) {
|
|
return [];
|
|
}
|
|
|
|
return array_map(function($hit) {
|
|
return $hit['title'];
|
|
}, $response['hits']);
|
|
}
|
|
|
|
private function getTrendingSuggestions($query) {
|
|
$query_escaped = $this->db->escape($query);
|
|
|
|
$sql = "SELECT query FROM db_search_queries
|
|
WHERE query LIKE '$query_escaped%'
|
|
ORDER BY search_count DESC, last_searched DESC
|
|
LIMIT 5";
|
|
|
|
$result = $this->db->execute($sql);
|
|
|
|
if (!$result) {
|
|
return [];
|
|
}
|
|
|
|
$suggestions = [];
|
|
while ($row = $this->db->fetch($result)) {
|
|
$suggestions[] = $row['query'];
|
|
}
|
|
|
|
return $suggestions;
|
|
}
|
|
|
|
private function apiRequest($method, $endpoint, $data = null) {
|
|
$url = $this->meili_host . $endpoint;
|
|
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'Authorization: Bearer ' . $this->meili_key,
|
|
'Content-Type: application/json'
|
|
]);
|
|
|
|
if ($data !== null) {
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
|
}
|
|
|
|
$response = curl_exec($ch);
|
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($http_code >= 200 && $http_code < 300) {
|
|
return json_decode($response, true);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private function success($data) {
|
|
echo json_encode([
|
|
'success' => true,
|
|
'suggestions' => $data
|
|
]);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
$api = new AutocompleteAPI();
|
|
$api->autocomplete();
|