Search for files

Hi,

I am using a search template as described here:
http://getkirby.com/docs/solutions/search

Now I’d like to create another template which only searches for files in the $page.
I tried this without success:

$results = $page->search($query, 'file');

Anyone knows the best way to accomplish this?

Best,
Chrs

1 Like

You should first get a collection of all things to search in. The search solution of Bastian shows how to search in all visible pages:

$site->index()->visible()->search($query, 'title|text');

So he gets all pages first by $site->index() and selects the visible pages with the visible() method.

You need to get all your files of your page first:

$page->files()

to add the search method which search in the collection of “things” you give in to it. So your function can be:

$results = $page->files()->search($query);

probably you don´t have to select the filed to search in. But I´m not really sure how to search by filenames. This is only my first idea. I haven´t used the search function before. I hope this helped anyway.

2 Likes

Thanks! With your help I got it working:

$query   = get('q');
$results = $page->files()->filterBy('filename', '*=', $query);
1 Like

Hi,
I’m trying to make work the solution described above. Not there yet.
Could you help me with this?

Here is the code for the controller:

<?php
return function($site, $pages, $page) {

  	$query   = get('q');
	$results = $page->files()->filterBy('filename', '*=', $query);

  return array(
    'query'   => $query,
    'results' => $results,
  );
};

And the search template:

<?php snippet('header') ?>

  
  

Thanks for your help!

What exactly does not work as expected?

Have you checked the source code? What sort of result do you get?

BTW: Do your files use a title field? If not, your results are probably there but you won’t see anything without checking the source.

Thank you for your reply.
I would like to search through files and display pictures and name of the pictures on a list.
First, I’m trying to get the name files to appear.

Yes, the files have a file attached with a title field.

Another problem I see here is your use of $page in the controller, because like that you only search in the current page, which in this case would be the search page that is likely not to contain any files?

Thank you you’re right…
silly mistake

I managed to display pictures through tag search that’s great.
Will look if I can combine it with search into text as well.

Thanks!

Hi,

I’m trying to search for files by looking into the text file attached to it.
I try to look through all the fields of the files.
I’ve been trying different thing and it’s not working.

//$results = $pages->files()->search($query);
would be nice but it is not working.

I managed to make this solution work, but it filters only the tag field:

<?php return function($site, $pages, $page) { $query = get('q'); $results = $pages->files()->filterBy('tags', '*=', $query); return array( 'query' => $query, 'results' => $results, ); }; Any ideas ? thank you for your help.

There is no search method for files, but you could create a custom files method.

An alternative would be to stick with filtering, but instead of using the filterBy method, use the filter() method.

Ok thank you, I will try to do a method by myself.
If anyone would have a start point for this it would be great (still beginner in php).
Thank you

You could use the Kirby search method for pages (/kirby/core/pages.php) as a starting point and adapt that to searching the file meta data instead. But as I said above, if you don’t need to many options, a filter with a callback will do as well.

Ok so in my case it could be something like this?
It doesn’t work but seems logic to me…
The thing is that I would like to look at several fields.
Thanks again for your help.

<?php

return function($site, $pages, $page) {

	$query   = get('q');
	$files = $pages->files()->filter(function($file) {
	  return $file->caption() == '$query';
	});
	$results = $pages->files()->search($query);

  return array(
    'query'   => $query,
    'results' => $results,
  );
};

Not quite. As I said above, there is no search function for files. And if you use a variable, you may not put it between quotes, otherwise it’s interpreted as a string.

<?php

return function($site, $pages, $page) {

	$query   = get('q');
	$results = $pages->files()->filter(function($file) {
	  return $file->caption() == $query;
	});

  return array(
    'query'   => $query,
    'results' => $results,
  );
}

But for a simple filter like that, you don’t need the callback.

But if you use the callback like above, you can implement more complicated logic, like loop through all fields to check if they contain the search value etc. or look for the search string in more than one field, e.g.

<?php

return function($site, $pages, $page) {

	$query   = get('q');
	$results = $pages->files()->filter(function($file) {
	  return $file->caption() == $query || in_array($query, $file->tags()->split(',');
	});

  return array(
    'query'   => $query,
    'results' => $results,
  );
}

Or maybe you want to check if the search term is a substring of the caption:

<?php

return function($site, $pages, $page) {

	$query   = get('q');
	$results = $pages->files()->filter(function($file) {
	  return str_pos($file->caption(), $query) || in_array($query, $file->tags()->split(',');
	});

  return array(
    'query'   => $query,
    'results' => $results,
  );
}
1 Like

Thank you for the help.

Yes, this is exactly what i try to achieve.

So If I get it right:

$results = $pages->files()->filter(function($file) {
return $file->caption() == $query || in_array($query, $file->tags()->split(‘,’);
});
This will look into the field named ‘caption’ of a file and check if it contains the query(‘q’) OR it will check the other field named ‘tags’. So it return a result if the search value is contained in one of these two fields.
I’m trying to make it work, kirby debug mode says there is a syntax error, unexpected ‘;’

Thank you very much for your time and help, learning a lot here.

Well, one of my favorites is leaving out parentheses… :wink:

$results = $pages->files()->filter(function($file) use($query){
return $file->caption() == $query || in_array($query, $file->tags()->split(','));
});

Edited.

Looking great!
Thank you very much for your help!
Happy to learned the right way!

I tried to implement a custom files methods only to realize that files methods only work with $page->files() files objects, while $pages->files() returns a collection object. However, you could use the following function, which is a simple port of the pages` search function:

function search($files, $query, $params = []) {

  if(is_string($params)) {
    $params = array('fields' => str::split($params, '|'));
  }

  $defaults = array(
    'minlength' => 2,
    'fields'    => array(),
    'words'     => false,
    'score'     => array()
  );

  $options     = array_merge($defaults, $params);
  $collection  = clone $files;
  $searchwords = preg_replace('/(\s)/u',',', $query);
  $searchwords = str::split($searchwords, ',', $options['minlength']);

  if(!empty($options['stopwords'])) {
    $searchwords = array_diff($searchwords, $options['stopwords']);
  }

  if(empty($searchwords)) return $collection->limit(0);

  $searchwords = array_map(function($value) use($options) {
    return $options['words'] ? '\b' . preg_quote($value) . '\b' : preg_quote($value);
  }, $searchwords);

  $preg    = '!(' . implode('|', $searchwords) . ')!i';
  $results = $collection->filter(function($file) use($query, $searchwords, $preg, $options) {

    $data = $file->meta()->toArray();
    $keys = array_keys($data);

    if(!empty($options['fields'])) {
      $keys = array_intersect($keys, $options['fields']);
    }

    $file->searchHits  = 0;
    $file->searchScore = 0;

    foreach($keys as $key) {

      $score = a::get($options['score'], $key, 1);

      // check for a match
      if($matches = preg_match_all($preg, $data[$key], $r)) {

        $file->searchHits  += $matches;
        $file->searchScore += $matches * $score;

        // check for full matches
        if($matches = preg_match_all('!' . preg_quote($query) . '!i', $data[$key], $r)) {
          $file->searchScore += $matches * $score;
        }

      }

    }

    return $file->searchHits > 0 ? true : false;

  });

  $results = $results->sortBy('searchScore', SORT_DESC);

  return $results;

}

Usage:

$query = get('q');
$files = $pages->files();
$results = search($files, $query);

tnx in your help…
$results = $page->files()->search($query);

This looks great, I’m going to test it. A big Thank You texnixe!

The function search is used as a plugin right?

Thanks