Jump to content

SearchEngine


teppo

Recommended Posts

On 10/14/2021 at 9:47 PM, tires said:

I chose the Operator:
~=
(i.E. All given words appear in compared value, in any order. Matches whole words. Uses “fulltext” index.)

But if i enter "everything 2021" i am not finding the page with the titel "everything is great in 2021".

Try this as Operator:

~%= or ~*=

I use this on all my sites with the AjaxSearch module, but it should work here as well.

Here you can find Ryan's blogpost: https://processwire.com/blog/posts/pw-3.0.160/

  • Like 1
Link to comment
Share on other sites

  • 1 month later...

Hi

I get a fatal error when I click on "Debugger" in module settings page.

Return value of SearchEngine\Debugger::getWords() must be of the type array, null returned

File: .../modules/SearchEngine/lib/Debugger.php:599

Sorry I just update the module and I resolved it with the 0.30.3 version.

have a nice day

  • Like 1
Link to comment
Share on other sites

  • 1 month later...

I’ve just set up searching with SearchEngine with basically all of the default options, and I’m really impressed! I‘ve turned on group-by-template, and it’s almost exactly what I want!

The template-named filtering buttons are terrific and intuitive.... I find that I’d like to render them as *plurals*, though, where the template names are *singular*. For example, I have a template *Person* that I would like to see as *People*, and another template *Journal* that I’d like to see as *Journals*.

Is there a built-in way to do this? If not, does anyone have any suggestions as to how I might go about implementing this.... Just guidance as to where I might put a switch/case statement would be acceptable, though I wouldn’t be surprised if there was a more elegant solution.

Thanks!

Link to comment
Share on other sites

Well, it looks like I should do something with `tabs_tab` — but I’m really not sure what. Is this something I can change in site->config.php? I don’t see how....

Link to comment
Share on other sites

 @gornycreative - if I understand correctly, this should work.

$searchEngine->addHookAfter('Renderer::renderTabLabel', function($event) use($types) {
    $event->return = $event->return . 's';
});

I was modifying the tabs a little more via a $types associative array, which you can see here. Just for fun I also included the hook I used to add images to the results.

Spoiler

$types = array(
    'All' => 'All',
    'Staff' => 'People',
    'Publication' => 'Publications',
    'Media' => 'Media Library',
    'Blog Post' => 'Blog Posts',
    'Enewsletter Article' => 'Enewsletter Articles',
    'Project' => 'Projects',
    'Education' => 'Education',
    'Education Program' => 'Programs',
    'Education Course' => 'Courses',
    'Page' => 'Pages'
);

$searchEngine = $modules->get('SearchEngine');

// cleanup for weird links like
// https://mysite.com/search/?q=test&t=blog_post>previous blog posts<%2Fa>).<%2Fp> <p><img title%3D
$t = $input->get('t');
$templateLabel = '';

if($t != '') {
    $template = $templates->get($t);
    if($template) {
        $templateLabel = $template->label;
    }
    else {
        // remove everything after (and including) first non-alphanumeric character
        preg_match("/[a-zA-Z0-9-_]+/", $t, $match);
        $t = $match[0];
        $template = $templates->get($t);
        if($template) {
            $templateLabel = $template->label;
        }
    }
}

if($t != '' && $t != $input->get('t')) {
    $session->redirect($page->url.'?q='.$input->get('q').'&t='.$t);
}
elseif($t != '' && !isset($types[$templateLabel])) {
    $session->redirect($page->url.'?q='.$input->get('q'));
}

$searchEngine->addHookAfter('Renderer::renderTabLabel', function($event) use($types) {
    $event->return = $types[$event->return];
});


$searchEngine->addHookAfter('Renderer::renderResult', function($event) {
    $p = $event->arguments[0];
    $image = $p->getunFormatted('pdf_images|image|images|video_images')->first();
    if($image) {
        $thumb = $image->size(0, 260, array('upscaling' => false));
        $event->return = '
        <div class="row">
            <div class="small-12 medium-4 large-5 xlarge-4 xxlarge-3 columns teaser__image">
                <a href="'.$p->url.'">
                    <img style="max-height: 300px" src = "'.$thumb->url.'" />
                </a>
            </div>
            <div class="small-12 medium-8 large-7 xlarge-8 xxlarge-9 columns teaser__content">
            ' . $event->return . '
        </div>';
    }
});

In the end I actually stopped using the built in tabs feature because even though it's really awesome, it doesn't work with multiple operators like: *=~= so I built up the tabs manually and now I can even change the operator if the user has wrapped something in double quotes to make it an exact match.

  • Like 2
Link to comment
Share on other sites

@adrian you answered @ErikMH tab question...

I was asking about the auto_desc being blank for partial word matches on the terms - but I can also see how this might get sticky unless you limited the generation to a single/first appearance. There could be dozens of places where a term partially matches on a page.

Link to comment
Share on other sites

On 7/19/2021 at 5:24 PM, gornycreative said:

This has been great. I just wanted to confirm one thing I noticed which is that _auto_desc doesn't seem to highlight/load a summary on partial word matches, although the search itself picks them up.

So for example if I search for 'business' using a partial match operator, any article search_index that includes the whole word 'business' will create a summary with the word 'business' marked... and results will show up where businesses and businessmen are in the index, but no summary with a highlighted partial word appears. The summary for these entries is blank.

Is this the intended behavior?

Sorry for missing this question — and no, it doesn't seem like intended behaviour.

I've made some adjustments to the latest version, 0.30.4, that should make things better in this regard. At least it helped with these exact terms (business/businesses/businessmen) in my limited tests. Please let me know if this doesn't work, or if it makes things somehow worse, though 🙂

  • Thanks 1
Link to comment
Share on other sites

  • 1 month later...

First, teppo, thanks  a lot for this module! It is really a great timesaver.

SearchEngine v. 0.30.5, PW 3.0.192 dev.

I struggle a bit with text translation feature (Labels, Placeholder a.s.o.). As i need to translate those text in several languages, initializing SearchEngine with $user->$language conditions statically is rather not an option.

I see the getDefaultStrings() function, but have no clue how to benefit from it. Maybe i overlook some docs. It would be very kind, if you or someone else could give me a brief overview how to set this up.

Currently i simply render SE straight out of the box (with some $config->SearchEngine = [...] tweaks before it).

Many thanks in advance, Olaf

Link to comment
Share on other sites

Meanwhile i found the way to deal with my approach for translations. Possibly it helps others, so i post it here.

Tl;dr: I have a function to enable translation of static template strings. This allows me to reference a _strings.php file as site translation file in the PW Backend. To apply this to the SearchEngine Module, you have to edit some configuration settings before rendering.

$searchEngine = $modules->get('SearchEngine');

$config->SearchEngine = [
  'render_args' => [
    'strings' => [
      'form_label' => _t('Suche', 'search'),
      'form_input_placeholder' => _t('Ihr Suchbegriff...', 'search'),
      'form_submit' => _t('Suche', 'search'),
      'results_heading' => _t('Suchergebnisse', 'search'),
      'results_summary_one' => _t('Ein Ergebnis für "%s":', 'search'),
      'results_summary_many' => _t('%2$d Ergebnisse für "%1$s":', 'search'),
      'results_summary_none' => _t('Keine Ergebnisse für "%s".', 'search'),
      'errors_heading' => _t('Hm, ihre Suchanfrage konnte nicht ausgeführt werden', 'search'),
      'error_query_missing' => _t('Bitte tragen Sie einen Suchbegriff in das Eingabefeld ein', 'search'),
      'error_query_too_short' => _t('Bitte verwenden sie mindestens %d Zeichen für den Suchbegriff.', 'search'),
    ]
  ]
]

<?= $searchEngine->renderForm(); ?>
<?= $searchEngine->renderResults(); ?>

===

Approach to translate:

_func.php


/**
 * This function enables the translation of static template strings.
 * All translations are done in `_strings.php`. More info within the
 * comments of `_strings.php`.
 *
 * @param  string $text       text to translate
 * @param  string $context    Context to allow doubles
 * @param  string $textdomain Point static textdomain
 * @return function           Function for gettext parser
 */
function _t($text, $context = 'Generic', $textdomain = '/site/templates/_strings.php') {
  return _x($text, $context, $textdomain);
}

 

_strings.php


/**
 * Static translated template strings to use project wide
 * in any templates. This spares us defining page fields
 * for every piece of code that doesn't have to be editable
 * within the admin interface. The function doesn't need to
 * execute with PHP, so we wrap it in a comment leaving
 * the code only visible to the language parser.
 *
 * Relies on function `_t` (s. `_func.php`)
 *
 * Usage:
 * 1. Define string in markup, e.g. "_t('<string>', '<context>')"
 * 2. Insert string in `_strings.php`, e.g. "_x('<string>', '<context>')";
 *    both without the surrounding double quotes. This is just to avoid 
 *    this comment is parsed.
 * 3. Choose the language you like to translate the default language strings
 * 4. Under (possibly still empty) "Site Translation Files" click on "Translate File" to get
 *    a list of translatable files. Choose the `_strings.php` and click send/save. You'll
 *    see the translatable string phrases.
 */

/*!

_x('Suche', 'search')
_x('Ihr Suchbegriff...', 'search')
_x('Suchergebnisse', 'search')
_x('Ein Ergebnis für "%s":', 'search')
_x('%2$d Ergebnisse für "%1$s":', 'search')
_x('Keine Ergebnisse für "%s".', 'search')
_x('Bitte tragen Sie einen Suchbegriff in das Eingabefeld ein.', 'search')
_x('Bitte verwenden sie mindestens %d Zeichen für den Suchbegriff.', 'search')

*/
  • Like 2
Link to comment
Share on other sites

  • 1 month later...

@teppo

Great module! I just found it after years building my own indexes (with FieldtypeConcatenate).

I have a special case where I use a Repeater Matrix inside a Repeater Matrix (building columns). I added all the fields inside the inner repeater (including the repeater itself) to the indexed fields list. But the contents won't get indexed. For simple repeaters (just one level) it works as expected.

Is there a way to add the contents of the inner Repeater Matrix to the index?

Thanks!

Link to comment
Share on other sites

  • 2 weeks later...

Just as an addition to my post above. Maybe someone benefits from it...

In a multilanguage environment the HTML5 validation prevents controll over the output on search input errors (example: when the search string is too short). It skips translated strings and always gets populated depending on the browser language settings.

To disable the HTML5 validation, you must enhance the template render args for the form with the attribute novalidate.

$config->SearchEngine = [
  'render_args' => [
    'templates' => [
      'form' => '<form id="{form_id}" class="{classes.form}" action="{form_action}" role="search" novalidate>%s</form>',
    ],
  ],
]

With that, errors are displayed within the regular HTML Elements (which respects translated strings). I don't see any drawbacks so far.

See also the comment from @teppo below!

  • Like 1
Link to comment
Share on other sites

On 4/22/2022 at 12:23 PM, olafgleba said:

In a multilanguage environment the HTML5 validation prevents controll over the output on search input errors (example: when the search string is to short). It skips translated strings and always gets populated depending on the browser language settings.

To disable the HTML5 validation, you must enhance the template render args for the form with the attribute novalidate.

$config->SearchEngine = [
  'render_args' => [
    'templates' => [
      'form' => '<form id="{form_id}" class="{classes.form}" action="{form_action}" role="search" novalidate>%s</form>',
    ],
  ],
]

With that, errors are displayed within the regular HTML Elements (which respects translated strings). I don't see any drawbacks so far.

Quick note on this: I can definitely see how this could be an issue, and if you require full control over error messages then shutting down browser errors and relying on PHP defined ones instead is a good option.

That being said, personally I tend to keep browser level validation enabled due to a few reasons:

  • Although these errors are "defaults" and it's difficult to alter them (constraint validation API is an option, but at least in my experience it's a bit heavy to use), these messages should be in the language chosen by the user. Not the language they're browsing the site in, but the language they're using at OS / browser level. Thus translating to the language of the site may, in some cases, have negative consequences.
  • Personally I like to get feedback early, and thus it feels more intuitive when errors can be displayed before sending and processing the form.
  • Finally I prefer to rely on HTML validation if/when possible for accessibility. PHP generated errors can be added in a way that makes them properly accessible, but it takes a bit of extra work, and typically requires some JS as well. At least in theory HTML level validation features should have the widest possible support 🙂

Anyway, just saying that there are valid reasons for both approaches — combining front-end and backend validation, vs. backend validation only.

  • Like 1
Link to comment
Share on other sites

On 4/13/2022 at 4:50 PM, Torsten Baldes said:

Great module! I just found it after years building my own indexes (with FieldtypeConcatenate).

Thanks, glad to hear you like it 🙂

On 4/13/2022 at 4:50 PM, Torsten Baldes said:

I have a special case where I use a Repeater Matrix inside a Repeater Matrix (building columns). I added all the fields inside the inner repeater (including the repeater itself) to the indexed fields list. But the contents won't get indexed. For simple repeaters (just one level) it works as expected.

Is there a way to add the contents of the inner Repeater Matrix to the index?

Did you add the "outer" Repeater Matrix field to indexed fields as well?

SearchEngine goes through all indexable fields (supported and configured to be indexed), and it should work fine with nested Repeater or Repeater Matrix fields, as long as every containing field is also set to be indexed. Thus if you have field rm_1 with another matrix field rm_2, you need to add both rm_1 and rm_2 to indexed fields.

Anyway, let me know if it still doesn't work and I can take a closer look!

Link to comment
Share on other sites

On 4/29/2022 at 10:49 AM, teppo said:

Quick note on this: I can definitely see how this could be an issue, and if you require full control over error messages then shutting down browser errors and relying on PHP defined ones instead is a good option. That being said, personally I tend to keep browser level validation enabled due to a few reasons: [...]

@teppo I completely agree. Thank you for your important addition.

In situations where the search section is not a essential part of the UX of a site/project (with only low budget for this part), it is possibly o.k. to use just one approach. Still combining client- and server validation is to prefer in most cases.

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

Hi @teppo

thank you for the great module! We use it for an internal project where the search function is very important.

Actually we have replaced some fields of type FieldtypeTextareas by FieldtypeCombo (both pro fields). Unfortunately FieldtypeCombo is not yet supported by SearchEngine, we have done this temporarily by the follwing lines in Indexer::___getIndexValue() at line 243:

            } else if ($field->type instanceof \ProcessWire\FieldtypeCombo) {
                return implode(
                    ' ... ',
                    array_filter(
                        array_values($page->get($field->name)->getArray()),
                        'strlen'
                    )
                );

Could you consider this in your next release of SearchEngine, please?

Best regards,

Thomas from XPORT.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...