Advanced Search Implementation
This commit is contained in:
27
CHANGELOG.md
27
CHANGELOG.md
@@ -2,12 +2,27 @@
|
||||
|
||||
## Changes 4/12/2025
|
||||
|
||||
- **Fuse.js Integration for Indexed Real-Time Searching**
|
||||
- **Added Fuse.js Library:** Included Fuse.js via a CDN `<script>` tag to leverage its client‑side fuzzy search capabilities.
|
||||
- **Created searchFiles Helper Function:** Introduced a new function that uses Fuse.js to build an index and perform fuzzy searches over file properties (file name, uploader, and nested tag names).
|
||||
- **Transformed JSON Object to Array:** Updated the loadFileList() function to convert the returned file data into an array (if it isn’t already) and assign file names from JSON keys.
|
||||
- **Updated Rendering Functions:** Modified both renderFileTable() and renderGalleryView() to use the searchFiles() helper instead of a simple in‑array .filter(). This ensures that every search—real‑time by user input—is powered by Fuse.js’s indexed search.
|
||||
- **Enhanced Search Configuration:** Configured Fuse.js to search across multiple keys (file name, uploader, and tags) so that users can find files based on any of these properties.
|
||||
### Advanced Search Implementation
|
||||
|
||||
- **Advanced Search Toggle:**
|
||||
- Added a global toggle (`window.advancedSearchEnabled`) and a UI button to switch between basic and advanced search modes.
|
||||
- The toggle button label changes between "Advanced Search" and "Basic Search" to reflect the active mode.
|
||||
|
||||
- **Fuse.js Integration Updates:**
|
||||
- Modified the `searchFiles()` function to conditionally include the `"content"` key in the Fuse.js keys only when advanced search mode is enabled.
|
||||
- Adjusted Fuse.js options by adding `ignoreLocation: true`, adjusting the `threshold`, and optionally assigning weights (e.g., a lower weight for `name` and a higher weight for `content`) to prioritize matches in file content.
|
||||
|
||||
- **Backend (PHP) Enhancements:**
|
||||
- Updated **getFileList.php** to read the content of text-based files (e.g., `.txt`, `.html`, `.md`, etc.) using `file_get_contents()`.
|
||||
- Added a `"content"` property to the JSON response for eligible files to allow for full-text search in advanced mode.
|
||||
|
||||
### Fuse.js Integration for Indexed Real-Time Searching**
|
||||
|
||||
- **Added Fuse.js Library:** Included Fuse.js via a CDN `<script>` tag to leverage its client‑side fuzzy search capabilities.
|
||||
- **Created searchFiles Helper Function:** Introduced a new function that uses Fuse.js to build an index and perform fuzzy searches over file properties (file name, uploader, and nested tag names).
|
||||
- **Transformed JSON Object to Array:** Updated the loadFileList() function to convert the returned file data into an array (if it isn’t already) and assign file names from JSON keys.
|
||||
- **Updated Rendering Functions:** Modified both renderFileTable() and renderGalleryView() to use the searchFiles() helper instead of a simple in‑array .filter(). This ensures that every search—real‑time by user input—is powered by Fuse.js’s indexed search.
|
||||
- **Enhanced Search Configuration:** Configured Fuse.js to search across multiple keys (file name, uploader, and tags) so that users can find files based on any of these properties.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -25,11 +25,6 @@ if ($folder !== 'root') {
|
||||
|
||||
/**
|
||||
* Helper: Generate the metadata file path for a given folder.
|
||||
* For "root", returns "root_metadata.json". Otherwise, replaces slashes,
|
||||
* backslashes, and spaces with dashes and appends "_metadata.json".
|
||||
*
|
||||
* @param string $folder The folder's relative path.
|
||||
* @return string The full path to the folder's metadata file.
|
||||
*/
|
||||
function getMetadataFilePath($folder) {
|
||||
if (strtolower($folder) === 'root' || $folder === '') {
|
||||
@@ -69,7 +64,6 @@ foreach ($files as $file) {
|
||||
|
||||
// Since metadata is stored per folder, the key is simply the file name.
|
||||
$metaKey = $file;
|
||||
|
||||
$fileDateModified = filemtime($filePath) ? date(DATE_TIME_FORMAT, filemtime($filePath)) : "Unknown";
|
||||
$fileUploadedDate = isset($metadata[$metaKey]["uploaded"]) ? $metadata[$metaKey]["uploaded"] : "Unknown";
|
||||
$fileUploader = isset($metadata[$metaKey]["uploader"]) ? $metadata[$metaKey]["uploader"] : "Unknown";
|
||||
@@ -85,7 +79,8 @@ foreach ($files as $file) {
|
||||
$fileSizeFormatted = sprintf("%s bytes", number_format($fileSizeBytes));
|
||||
}
|
||||
|
||||
$fileList[] = [
|
||||
// Build the basic file entry.
|
||||
$fileEntry = [
|
||||
'name' => $file,
|
||||
'modified' => $fileDateModified,
|
||||
'uploaded' => $fileUploadedDate,
|
||||
@@ -93,6 +88,14 @@ foreach ($files as $file) {
|
||||
'uploader' => $fileUploader,
|
||||
'tags' => isset($metadata[$metaKey]['tags']) ? $metadata[$metaKey]['tags'] : []
|
||||
];
|
||||
|
||||
// Add file content for text-based files.
|
||||
if (preg_match('/\.(txt|html|htm|md|js|css|json|xml|php|py|ini|conf|log)$/i', $file)) {
|
||||
$content = file_get_contents($filePath);
|
||||
$fileEntry['content'] = $content;
|
||||
}
|
||||
|
||||
$fileList[] = $fileEntry;
|
||||
}
|
||||
|
||||
// Load global tags from createdTags.json.
|
||||
|
||||
@@ -24,6 +24,9 @@ window.itemsPerPage = window.itemsPerPage || 10;
|
||||
window.currentPage = window.currentPage || 1;
|
||||
window.viewMode = localStorage.getItem("viewMode") || "table"; // "table" or "gallery"
|
||||
|
||||
// Global flag for advanced search mode.
|
||||
window.advancedSearchEnabled = false;
|
||||
|
||||
/**
|
||||
* --- Helper Functions ---
|
||||
*/
|
||||
@@ -73,22 +76,50 @@ function buildFolderSummary(filteredFiles) {
|
||||
return `<strong>Total Files:</strong> ${totalFiles} | <strong>Total Size:</strong> ${sizeStr}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* --- Advanced Search Toggle ---
|
||||
* Toggles advanced search mode. When enabled, the search will include additional keys (e.g. "content").
|
||||
*/
|
||||
function toggleAdvancedSearch() {
|
||||
window.advancedSearchEnabled = !window.advancedSearchEnabled;
|
||||
const advancedBtn = document.getElementById("advancedSearchToggle");
|
||||
if (advancedBtn) {
|
||||
advancedBtn.textContent = window.advancedSearchEnabled ? "Basic Search" : "Advanced Search";
|
||||
}
|
||||
// Re-run the file table rendering with updated search settings.
|
||||
renderFileTable(window.currentFolder);
|
||||
}
|
||||
|
||||
/**
|
||||
* --- Fuse.js Search Helper ---
|
||||
* Uses Fuse.js to perform a fuzzy search on fileData.
|
||||
* Searches over file name, uploader, and tag names.
|
||||
* By default, searches over file name, uploader, and tag names.
|
||||
* When advanced search is enabled, it also includes the 'content' property.
|
||||
*/
|
||||
function searchFiles(searchTerm) {
|
||||
if (!searchTerm) return fileData;
|
||||
// Define search options – adjust threshold as needed.
|
||||
|
||||
// Define search keys.
|
||||
let keys = [
|
||||
{ name: 'name', weight: 0.1 },
|
||||
{ name: 'uploader', weight: 0.1 },
|
||||
{ name: 'tags.name', weight: 0.1 }
|
||||
];
|
||||
if (window.advancedSearchEnabled) {
|
||||
keys.push({ name: 'content', weight: 0.7 });
|
||||
}
|
||||
|
||||
const options = {
|
||||
keys: ['name', 'uploader', 'tags.name'],
|
||||
threshold: 0.3
|
||||
keys: keys,
|
||||
threshold: 0.4,
|
||||
minMatchCharLength: 2,
|
||||
ignoreLocation: true
|
||||
};
|
||||
|
||||
const fuse = new Fuse(fileData, options);
|
||||
// Fuse returns an array of results where each result has an "item" property.
|
||||
return fuse.search(searchTerm).map(result => result.item);
|
||||
}
|
||||
let results = fuse.search(searchTerm);
|
||||
return results.map(result => result.item);
|
||||
}
|
||||
|
||||
/**
|
||||
* --- VIEW MODE TOGGLE BUTTON & Helpers ---
|
||||
@@ -147,7 +178,7 @@ export function loadFileList(folderParam) {
|
||||
.then(data => {
|
||||
fileListContainer.innerHTML = ""; // Clear loading message.
|
||||
if (data.files && Object.keys(data.files).length > 0) {
|
||||
// In case the returned "files" is an object instead of an array, transform it.
|
||||
// If the returned "files" is an object instead of an array, transform it.
|
||||
if (!Array.isArray(data.files)) {
|
||||
data.files = Object.entries(data.files).map(([name, meta]) => {
|
||||
meta.name = name;
|
||||
@@ -162,6 +193,8 @@ export function loadFileList(folderParam) {
|
||||
if (!file.type && /\.(jpg|jpeg|png|gif|bmp|webp|svg|ico)$/i.test(file.name)) {
|
||||
file.type = "image";
|
||||
}
|
||||
// OPTIONAL: For text documents, preload content (if available from backend)
|
||||
// Example: if (/\.txt|html|md|js|css|json|xml$/i.test(file.name)) { file.content = file.content || ""; }
|
||||
return file;
|
||||
});
|
||||
fileData = data.files;
|
||||
@@ -234,11 +267,17 @@ export function renderFileTable(folder, container) {
|
||||
? "uploads/"
|
||||
: "uploads/" + folder.split("/").map(encodeURIComponent).join("/") + "/";
|
||||
|
||||
// Build the top controls and append the advanced search toggle button.
|
||||
const topControlsHTML = buildSearchAndPaginationControls({
|
||||
currentPage,
|
||||
totalPages,
|
||||
searchTerm: window.currentSearchTerm || ""
|
||||
});
|
||||
const advancedToggleHTML = `<button id="advancedSearchToggle" class="btn btn-sm btn-outline-secondary" style="margin-left: 10px;" onclick="toggleAdvancedSearch()">
|
||||
${window.advancedSearchEnabled ? "Basic Search" : "Advanced Search"}
|
||||
</button>`;
|
||||
const combinedTopHTML = topControlsHTML + advancedToggleHTML;
|
||||
|
||||
let headerHTML = buildFileTableHeader(sortOrder);
|
||||
const startIndex = (currentPage - 1) * itemsPerPageSetting;
|
||||
const endIndex = Math.min(startIndex + itemsPerPageSetting, totalFiles);
|
||||
@@ -270,7 +309,7 @@ export function renderFileTable(folder, container) {
|
||||
rowsHTML += "</tbody></table>";
|
||||
const bottomControlsHTML = buildBottomControls(itemsPerPageSetting);
|
||||
|
||||
fileListContent.innerHTML = topControlsHTML + headerHTML + rowsHTML + bottomControlsHTML;
|
||||
fileListContent.innerHTML = combinedTopHTML + headerHTML + rowsHTML + bottomControlsHTML;
|
||||
|
||||
createViewToggleButton();
|
||||
|
||||
@@ -492,4 +531,5 @@ window.changeItemsPerPage = function (newCount) {
|
||||
window.loadFileList = loadFileList;
|
||||
window.renderFileTable = renderFileTable;
|
||||
window.renderGalleryView = renderGalleryView;
|
||||
window.sortFiles = sortFiles;
|
||||
window.sortFiles = sortFiles;
|
||||
window.toggleAdvancedSearch = toggleAdvancedSearch;
|
||||
Reference in New Issue
Block a user