Jump to content

Selector problem with multiple keywords in multiple fields


opalepatrick
 Share

Recommended Posts

In this thread https://processwire.com/talk/topic/11754-search-not-working-as-intended/ there is a really nice solution for dealing with 3 letter words in search. However I am having a problem with selectors.

$selector .= ", title|body|collection.title|hue.title|pattern.title|pattern_type.title|usage.title~=" . $sanitizer->selectorValue(implode(' ', $parts));

The problem is that if I have, for example, a search that is looking for myfabric (in title or title.collection) yellow (in hue.title) I get zero results. If I do either separately then I get results. So the search is myfabric yellow.

 

It seems to me as if the query is saying IF EITHER OF THESE WORDS are in EITHER title OR title.collection OR hue.title then show results but not if they are in separate fields. I need it to be IF EITHER WORD is in any field. If both words are in one field then I get results as well.

 

Baffled or dense. Any help appreciated.

Link to comment
Share on other sites

The manual for selector operators  states for ~= "Contains all the words".

So your search returns only fields that have all of the $parts, e.g. "myfabric yellow".

I think you need to use the OR operator for your selector values, too (see here).

So your code would read

$selector .= ", title|body|collection.title|hue.title|pattern.title|pattern_type.title|usage.title~=" . $sanitizer->selectorValue(implode('|', $parts));

| pipe instead of blank space in the implode.

Link to comment
Share on other sites

Thanks for the reply gebeer. That was my first thought, but that widens the search so that we get ANY field with myfabric OR yellow in it.

What I would like is myfabric AND yellow in a concatenation of all the fields content.

I tried a concat field but that doesn't work on runtime.

Link to comment
Share on other sites

The sanitizer is not meant to sanitize groups of values, but only single ones. It's job is to mask out characters like the pipe just because it does have a special meaning in an selector string. Suppose I'd search for "My | Friend". You most likely wouldn't want this pipe to end up in your selector. In your case you'd want to implode the parts after sanitizing each part.

Link to comment
Share on other sites

Sorry LostKobrai. I edited my reply to make more sense. You were too fast for me. As you can see I am happy enough not using OR in the selector string as I would prefer for the search to narrow. It is the fields being searched that are my problem. Essentially I need to search ALL the contents of the page for BOTH words.

Link to comment
Share on other sites

Thanks once again LostKobrakai. That is another thing that I tried but it produced zero results for the above. combo is a cache field.

$selector .= ", combo~=" . $sanitizer->selectorValue(implode(' ', $parts)); 

I know there should be 3 results for this one

Link to comment
Share on other sites

I thought at the time that the cache field might not be picking up content that came from a Page fieldtype (like hue.title) that allows PageArray?

edit: Actually running that query for just the word yellow, I don't get the pages with the word yellow only in hue.title

Link to comment
Share on other sites

Not Autojoin afaik. I still have the same problem with this query. Any help would be appreciated. I am sure that it seems I am meandering on, but I am just trying to show what I am doing. If I appear to be missing the point or off the beaten track, then please say.

Link to comment
Share on other sites

Honestly Macrura and everyone else, I am just banging my head against a brick wall due to my lack of knowledge. So say my search is 'green vinyl', I have:

$selector = ''; 
$parts = explode(' ', $search);
$p = new PageArray();
$fs = array('hue.title','pattern.title','pattern_type.title','usage.title','title','collection.keywords');
if(count($parts)) {
		foreach ($parts as $pt) {
			foreach ($fs as $f) {
				$selector = ", collection.title!=Clearance, " . $f . "~=" . $pt;
				// echo $selector . "<br />";
				$ps = wire("pages")->find("template=fabric" . $selector);
				// echo "Page ids are: " . $ps . "<br />";
				$p->import($ps);
			}
		}
	} 

I get every page id if any word is available in any field.

What I actually need is to get both words from a concatenation of content from all of the specified fields. I need to narrow the results so that for instance, I only get results when both 'green' and 'vinyl' appear in any field but not necessarily the same field. 'green' maybe from hue.title and 'vinyl' from collection.keywords?

As mentioned previously the concat fieldtype would let me specify things like hue.title for page fieldtypes (single) but is not available at runtime. Cache fieldtype does not allow page fieldtypes. I am just getting tangled in my head and going down rabbit holes. Any help or questions to help me explain more clearly would be appreciated.

I do find it hard to believe that I cannot query all fields on a page even if they are page fieldtypes (single).

Link to comment
Share on other sites

As Macrura proposed: Run the query for each $part in isolation and in the end merge the results.

$results = array();

foreach($parts as $part){
  // Might be optimized to only retrieve IDs in the first place
  $result = $pages->find("something=$part"); 
  $results[] = $result->explode("id");
}

// Get all the id's present in all results
$intersect = array_reduce($results, function($carry, $item){
  if(is_null($carry)) return $item;
  return array_intersect($carry, $item);
});

// Get the real search results
$search_result = $pages->get("id=" . implode("|", $intersect));
Link to comment
Share on other sites

 I can't see any reason (though I don't rule out that I'm missing the obvious) why running the same selector for each word inside a single query shouldn't work as expected.

$sel = array();
$parts = explode(' ', $parts);
$selfields = "title|body|collection.title|hue.title|pattern.title|pattern_type.title|usage.title";

foreach($parts as $part) {
  $sel[] = $selfields . "*=" . $sanitizer->selectorValue($part);
}

$found = $pages->find(implode(', ', $sel));
Link to comment
Share on other sites

Thanks to you both for your replies. Just working on LostKobrakai's one first. Could you explain the logic particularly around the function with array_reduce as I am only getting a single result regardless of search? I would expect many id's in this case.

edit: Actually, the one result is because of this line

$search_result = $pages->get("id=" . implode("|", $intersect));

And it worked perfectly with $pages->find !

Thank you once again LostKobrakai and everyone who helped.

Link to comment
Share on other sites

Following up on this. I was so focussed on getting a result on multiple word search as above that I did not realise I was getting an error on single words:

Call to a member function last() on a non-object

I have checked and there is a dump that appears to be exactly the same format as the multi word search. The line above is the last piece of code before this call.

$intersect = array_reduce($results, function($carry, $item){
if(is_null($carry)) return $item;
return array_intersect($carry, $item);
});

$search_result = $pages->get("id=" . implode("|", $intersect));

foreach($search_results as $m) { if($m->images->last()){ $img = $m->images->last()->width(300); } }

As I say, this works for more than one word but not for one.

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
 Share

×
×
  • Create New...