• Remember to address to the Bug Tracking guidelines and to follow the instructions in the post ticket template.
  • Welcome to the Chevereto user community!

    Here users from all over the world gather around to learn the latest about Chevereto and contribute with ideas to improve the software.

    Please keep in mind:

    • This community is user driven. Be polite with other users.
    • We recommend purchasing a Chevereto license to participate in this community.
    • Purchase a Community Subscription to get even faster ticket response times.
  • Chevereto Support CLST

    Support response

    Support checklist

    • Got a Something went wrong message? Read this guide and provide the actual error. Do not skip this.
    • Confirm that the server meets the System Requirements
    • Check for any available Hotfix - your issue could be already reported/fixed
    • Read documentation - It will be required to Debug and understand Errors for a faster support response

Advanced Search issues

Version
4.3.3
Website URL
https://hopesgallery.com/
PHP version
8.3.14
Database driver
MySQL
Database version
8.0.42-0ubuntu0.24.04.1
Web browser
Chrome/Windows

Other-Wisz

Chevereto Member
The Advanced Search instructs the user to ‘Put exact words or phrase in quotes’ to search for a an exact phrase but this does not work.

For an example Searches of “New York NY” bring up not only the desired results, but projects like these:
St Paul's School - School House - Concord NH
RAW Art Works - Lynn MA
1200 New Hampshire - Washington, DC - C#53215
Barneys New York - Beverly Hills CA


… none of these have the exact phase (that I searched in quotes) New York NY in their names or descriptions.

So it seems to be searching for “All the words” instead of “Exact word or phrase”.
 
I have figured out the fix for this in case it helps for later version updates.
File - app/src/Legacy/Classes/Search.php

Updated Code:
PHP:
<?php

/*
 * This file is part of Chevereto.
 *
 * (c) Rodolfo Berrios <rodolfo@chevereto.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Chevereto\Legacy\Classes;

use Exception;
use function Chevereto\Legacy\G\str_replace_first;

class Search
{
    public array $display;
    public static array $excluded = ['storage', 'ip'];
    public string $DBEngine = 'InnoDB';
    public string $wheres;
    public string $q = '';
    public string $type;
    public array $request;
    public array $requester;
    public array $binds;
    public array $op;

    public function build(): void
    {
        if (!in_array($this->type, ['images', 'albums', 'users'], true)) {
            throw new Exception('Invalid search type', 600);
        }

        $as_handle = [
            'as_q' => null,
            'as_epq' => null,
            'as_oq' => null,
            'as_eq' => null,
            'as_cat' => 'category',
        ];
        $as_handle_admin = [
            'as_stor' => 'storage',
            'as_ip' => 'ip',
        ];
        if ($this->requester['is_content_manager'] ?? false) {
            $as_handle = array_merge($as_handle, $as_handle_admin);
        }

        $this->q = str_replace('@', '', $this->q);
        foreach ($as_handle as $k => $v) {
            if (isset($this->request[$k]) && $this->request[$k] !== '') {
                if ($k === 'as_epq') {
                    $this->q .= ' "' . $this->request[$k] . '"';
                } else {
                    $this->q .= ' '
                        . (isset($v) ? ($v . ':') : '')
                        . $this->request[$k];
                }
            }
        }

        $this->q = trim(
            preg_replace(
                ['#"+#', '#\'+#'],
                ['"', '\''],
                $this->q ?? ''
            )
        );

        $exact_phrase = '';
        $is_exact_phrase_only = false;

        if (isset($this->request['as_epq']) && $this->request['as_epq'] !== '') {
            $exact_phrase = $this->request['as_epq'];
            $is_exact_phrase_only = true;
            $search_binds = [];
        } elseif (isset($this->request['q'])) {
            $clean_q = html_entity_decode(trim($this->request['q']), ENT_QUOTES | ENT_HTML5);
            if (preg_match('/^"(.*?)"$/', $clean_q, $matches)) {
                $exact_phrase = $matches[1];
                $is_exact_phrase_only = true;
                $search_binds = [];
            }
        }

        $search_op = $this->handleSearchOperators($this->q, $this->requester['is_content_manager'] ?? false);
        $this->q = '';
        foreach ($search_op as $operator) {
            $this->q .= implode(' ', $operator) . ' ';
        }

        if ($this->q !== '') {
            $this->q = trim($this->q);
            $this->q = preg_replace('/\s+/', ' ', $this->q) ?? '';
        }

        $this->q ??= '';
        $q_match = $this->q;

        $search_binds ??= [];
        $search_op_wheres = [];

        foreach ($search_op['named'] as $v) {
            $q_match = trim(preg_replace('/\s+/', ' ', str_replace($v, '', $q_match)));
            $op = explode(':', $v);
            if (!in_array($op[0], ['category', 'ip', 'storage'], true)) {
                continue;
            }

            switch ($this->type) {
                case 'albums':
                case 'users':
                case 'images':
                    if ($op[0] === 'ip') {
                        $search_binds[] = ['param' => ':ip', 'value' => str_replace_first('ip:', '', $this->q)];
                    }
                    break;
            }
        }

        if ($q_match !== '' && !$is_exact_phrase_only) {
            $q_value = $q_match;
            if ($this->DBEngine === 'InnoDB') {
                $q_value = trim($q_value, '><');
            }
            $search_binds[] = ['param' => ':q', 'value' => $q_value];
            $q_strip = preg_replace('/(-[\S]+|".+?")/u', '', $q_match);
            $search_binds[] = ['param' => ':like_q', 'value' => '%' . $q_strip . '%'];
        }

        $this->binds = $search_binds;
        $this->op = $search_op;

        switch ($this->type) {
            case 'albums':
                if ($is_exact_phrase_only) {
                    $this->binds = [[
                        'param' => ':phrase',
                        'value' => '%' . $exact_phrase . '%',
                    ]];
                    $this->wheres = 'WHERE album_name LIKE :phrase';
                } elseif (empty($search_binds)) {
                    $this->wheres = 'WHERE album_id < 0';
                } elseif (($op[0] ?? null) === 'ip') {
                    $this->wheres = 'album_creation_ip LIKE REPLACE(:ip, "*", "%")';
                } else {
                    $this->wheres = 'WHERE (
                        MATCH(`album_name`,`album_description`) AGAINST (:q)
                        OR album_name LIKE :like_q
                        OR album_description LIKE :like_q
                    )';
                }
                break;

            case 'images':
                if ($is_exact_phrase_only) {
                    $this->binds = [[
                        'param' => ':phrase',
                        'value' => '%' . $exact_phrase . '%',
                    ]];
                    $this->wheres = 'WHERE (
                        image_name LIKE :phrase
                        OR image_title LIKE :phrase
                        OR image_description LIKE :phrase
                        OR image_original_filename LIKE :phrase
                    )';
                } elseif ($q_match !== '') {
                    $this->wheres = 'WHERE (
                        MATCH(`image_name`,`image_title`,`image_description`,`image_original_filename`) AGAINST (:q IN BOOLEAN MODE)
                        OR BINARY image_name LIKE BINARY :like_q
                        OR BINARY image_title LIKE BINARY :like_q
                        OR BINARY image_description LIKE BINARY :like_q
                        OR BINARY image_original_filename LIKE BINARY :like_q
                    )';
                }
                if ($search_op_wheres !== []) {
                    $this->wheres .= ($this->wheres === '' ? 'WHERE ' : ' AND ') . implode(' AND ', $search_op_wheres);
                }
                break;

            default:
                $this->wheres = '';
                break;
        }

        $this->display = [
            'type' => $this->type,
            'q' => $this->q,
            'd' => strlen($this->q) >= 25 ? (substr($this->q, 0, 22) . '...') : $this->q,
        ];
    }

    protected function handleSearchOperators(string $q, bool $full = true): array
    {
        $operators = [
            'any' => [],
            'exact_phrases' => [],
            'excluded' => [],
            'named' => [],
        ];
        $raw_regex = [
            'named' => '[\S]+\:[\S]+',
            'quoted' => '-*[\"\']+.+[\"\']+',
            'spaced' => '\S+',
        ];
        foreach ($raw_regex as $k => $v) {
            if ($k === 'spaced') {
                $q = str_replace(',', '', $q);
            }
            if (preg_match_all('/' . $v . '/', $q, $match)) {
                foreach ($match[0] as $qMatch) {
                    switch ($k) {
                        case 'named':
                            if (!$full) {
                                $named_operator = explode(':', $qMatch);
                                if (in_array($named_operator[0], self::$excluded, false)) {
                                    continue 2;
                                }
                            }
                            $operators[$k][] = $qMatch;
                            break;
                        default:
                            if (strpos($qMatch, '-') === 0) {
                                $operators['excluded'][] = $qMatch;
                            } elseif (strpos($qMatch, '"') === 0) {
                                $operators['exact_phrases'][] = $qMatch;
                            } else {
                                $operators['any'][] = $qMatch;
                            }
                            break;
                    }
                    $q = trim(preg_replace('/\s+/', ' ', str_replace($qMatch, '', $q)));
                }
            }
        }

        return $operators;
    }
}
 
Back
Top