2021-06-26 23:23:15 +08:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								<?php
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2022-08-16 18:27:22 +08:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								namespace BookStack\Search;
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								use BookStack\Search\Options\ExactSearchOption;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								use BookStack\Search\Options\FilterSearchOption;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								use BookStack\Search\Options\SearchOption;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								use BookStack\Search\Options\TagSearchOption;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								use BookStack\Search\Options\TermSearchOption;
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								use Illuminate\Http\Request;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								class SearchOptions
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								{
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-04 02:27:03 +08:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    /** @var SearchOptionSet<TermSearchOption> */
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 00:31:45 +08:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    public SearchOptionSet $searches;
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-04 02:27:03 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    /** @var SearchOptionSet<ExactSearchOption> */
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 00:31:45 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    public SearchOptionSet $exacts;
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-04 02:27:03 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    /** @var SearchOptionSet<TagSearchOption> */
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 00:31:45 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    public SearchOptionSet $tags;
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-04 02:27:03 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    /** @var SearchOptionSet<FilterSearchOption> */
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 00:31:45 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    public SearchOptionSet $filters;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    public function __construct()
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $this->searches = new SearchOptionSet();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $this->exacts = new SearchOptionSet();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $this->tags = new SearchOptionSet();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $this->filters = new SearchOptionSet();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    /**
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * Create a new instance from a search string.
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     */
							 | 
						
					
						
							
								
									
										
										
										
											2021-10-27 05:04:18 +08:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    public static function fromString(string $search): self
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    {
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 00:31:45 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $instance = new self();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $instance->addOptionsFromString($search);
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        return $instance;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    /**
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * Create a new instance from a request.
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * Will look for a classic string term and use that
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * Otherwise we'll use the details from an advanced search form.
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     */
							 | 
						
					
						
							
								
									
										
										
										
											2021-10-27 05:04:18 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    public static function fromRequest(Request $request): self
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    {
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:37:18 +08:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        if (!$request->has('search') && !$request->has('term')) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            return static::fromString('');
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        if ($request->has('term')) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            return static::fromString($request->get('term'));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2021-11-06 00:27:59 +08:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        $instance = new SearchOptions();
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $inputs = $request->only(['search', 'types', 'filters', 'exact', 'tags', 'extras']);
							 | 
						
					
						
							
								
									
										
										
										
											2021-11-13 02:03:44 +08:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $parsedStandardTerms = static::parseStandardTermString($inputs['search'] ?? '');
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 00:31:45 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $inputExacts = array_filter($inputs['exact'] ?? []);
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $instance->searches = SearchOptionSet::fromValueArray(array_filter($parsedStandardTerms['terms']), TermSearchOption::class);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $instance->exacts = SearchOptionSet::fromValueArray(array_filter($parsedStandardTerms['exacts']), ExactSearchOption::class);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $instance->exacts = $instance->exacts->merge(SearchOptionSet::fromValueArray($inputExacts, ExactSearchOption::class));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $instance->tags = SearchOptionSet::fromValueArray(array_filter($inputs['tags'] ?? []), TagSearchOption::class);
							 | 
						
					
						
							
								
									
										
										
										
											2021-11-13 02:03:44 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $cleanedFilters = [];
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        foreach (($inputs['filters'] ?? []) as $filterKey => $filterVal) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            if (empty($filterVal)) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                continue;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 00:31:45 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            $cleanedFilterVal = $filterVal === 'true' ? '' : $filterVal;
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            $cleanedFilters[] = new FilterSearchOption($cleanedFilterVal, $filterKey);
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							
								
									
										
										
										
											2021-11-13 02:03:44 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        if (isset($inputs['types']) && count($inputs['types']) < 4) {
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-05 21:47:00 +08:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            $cleanedFilters[] = new FilterSearchOption(implode('|', $inputs['types']), 'type');
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							
								
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $instance->filters = new SearchOptionSet($cleanedFilters);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        // Parse and merge in extras if provided
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        if (!empty($inputs['extras'])) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            $extras = static::fromString($inputs['extras']);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            $instance->searches = $instance->searches->merge($extras->searches);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            $instance->exacts = $instance->exacts->merge($extras->exacts);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            $instance->tags = $instance->tags->merge($extras->tags);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            $instance->filters = $instance->filters->merge($extras->filters);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 00:31:45 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        return $instance;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    /**
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 00:31:45 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * Decode a search string and add its contents to this instance.
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     */
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 00:31:45 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    protected function addOptionsFromString(string $searchString): void
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    {
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        /** @var array<string, SearchOption[]> $terms */
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $terms = [
							 | 
						
					
						
							
								
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            'exacts'   => [],
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            'tags'     => [],
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            'filters'  => [],
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        ];
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $patterns = [
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            'exacts'  => '/-?"((?:\\\\.|[^"\\\\])*)"/',
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            'tags'    => '/-?\[(.*?)\]/',
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            'filters' => '/-?\{(.*?)\}/',
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        ];
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $constructors = [
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            'exacts'   => fn(string $value, bool $negated) => new ExactSearchOption($value, $negated),
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            'tags'     => fn(string $value, bool $negated) => new TagSearchOption($value, $negated),
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            'filters'  => fn(string $value, bool $negated) => FilterSearchOption::fromContentString($value, $negated),
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        ];
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        // Parse special terms
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        foreach ($patterns as $termType => $pattern) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            $matches = [];
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            preg_match_all($pattern, $searchString, $matches);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            if (count($matches) > 0) {
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                foreach ($matches[1] as $index => $value) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    $negated = str_starts_with($matches[0][$index], '-');
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                    $terms[$termType][] = $constructors[$termType]($value, $negated);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                }
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                $searchString = preg_replace($pattern, '', $searchString);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2023-09-23 20:41:10 +08:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        // Unescape exacts and backslash escapes
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        foreach ($terms['exacts'] as $exact) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            $exact->value = static::decodeEscapes($exact->value);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							
								
									
										
										
										
											2023-09-20 03:09:33 +08:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        // Parse standard terms
							 | 
						
					
						
							
								
									
										
										
										
											2021-11-13 02:03:44 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $parsedStandardTerms = static::parseStandardTermString($searchString);
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 00:31:45 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $this->searches = $this->searches
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            ->merge(SearchOptionSet::fromValueArray($parsedStandardTerms['terms'], TermSearchOption::class))
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 00:31:45 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            ->filterEmpty();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $this->exacts = $this->exacts
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            ->merge(new SearchOptionSet($terms['exacts']))
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            ->merge(SearchOptionSet::fromValueArray($parsedStandardTerms['exacts'], ExactSearchOption::class))
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 00:31:45 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            ->filterEmpty();
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        // Add tags & filters
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $this->tags = $this->tags->merge(new SearchOptionSet($terms['tags']));
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $this->filters = $this->filters->merge(new SearchOptionSet($terms['filters']));
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2023-09-23 20:41:10 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    /**
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * Decode backslash escaping within the input string.
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     */
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    protected static function decodeEscapes(string $input): string
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $decoded = "";
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $escaping = false;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        foreach (str_split($input) as $char) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            if ($escaping) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                $decoded .= $char;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                $escaping = false;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            } else if ($char === '\\') {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                $escaping = true;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            } else {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                $decoded .= $char;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        return $decoded;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2021-11-13 02:03:44 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    /**
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * Parse a standard search term string into individual search terms and
							 | 
						
					
						
							
								
									
										
										
										
											2023-09-20 03:09:33 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * convert any required terms to exact matches. This is done since some
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * characters will never be in the standard index, since we use them as
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * delimiters, and therefore we convert a term to be exact if it
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * contains one of those delimiter characters.
							 | 
						
					
						
							
								
									
										
										
										
											2021-11-13 02:03:44 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     *
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * @return array{terms: array<string>, exacts: array<string>}
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     */
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    protected static function parseStandardTermString(string $termString): array
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $terms = explode(' ', $termString);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $indexDelimiters = SearchIndex::$delimiters;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $parsed = [
							 | 
						
					
						
							
								
									
										
										
										
											2021-11-13 21:28:17 +08:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								            'terms'  => [],
							 | 
						
					
						
							
								
									
										
										
										
											2021-11-13 02:03:44 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            'exacts' => [],
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        ];
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        foreach ($terms as $searchTerm) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            if ($searchTerm === '') {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                continue;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2023-09-20 03:09:33 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            $becomeExact = (strpbrk($searchTerm, $indexDelimiters) !== false);
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            $parsed[$becomeExact ? 'exacts' : 'terms'][] = $searchTerm;
							 | 
						
					
						
							
								
									
										
										
										
											2021-11-13 02:03:44 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        return $parsed;
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2023-12-11 23:55:43 +08:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								    /**
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * Set the value of a specific filter in the search options.
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     */
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    public function setFilter(string $filterName, string $filterValue = ''): void
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    {
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 00:31:45 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $this->filters = $this->filters->merge(
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            new SearchOptionSet([new FilterSearchOption($filterValue, $filterName)])
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 00:31:45 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        );
							 | 
						
					
						
							
								
									
										
										
										
											2023-12-11 23:55:43 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    /**
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * Encode this instance to a search string.
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     */
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    public function toString(): string
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    {
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $options = [
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            ...$this->searches->all(),
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            ...$this->exacts->all(),
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            ...$this->tags->all(),
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            ...$this->filters->all(),
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        ];
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $parts = array_map(fn(SearchOption $o) => $o->toString(), $options);
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2023-09-20 03:09:33 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        return implode(' ', $parts);
							 | 
						
					
						
							
								
									
										
										
										
											2020-06-27 20:29:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 00:31:45 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    /**
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * Get the search options that don't have UI controls provided for.
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * Provided back as a key => value array with the keys being expected
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     * input names for a search form, and values being the option value.
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								     */
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    public function getAdditionalOptionsString(): string
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 00:31:45 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $options = [];
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-05 21:47:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        // Handle filters without UI support
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $userFilters = ['updated_by', 'created_by', 'owned_by'];
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-05 21:47:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        $unsupportedFilters = ['is_template', 'sort_by'];
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        foreach ($this->filters->all() as $filter) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            if (in_array($filter->getKey(), $userFilters, true) && $filter->value !== null && $filter->value !== 'me') {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                $options[] = $filter;
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-05 21:47:00 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            } else if (in_array($filter->getKey(), $unsupportedFilters, true)) {
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								                $options[] = $filter;
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 00:31:45 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								            }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        }
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        // Negated items
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-04 02:38:07 +08:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								        array_push($options, ...$this->exacts->negated()->all());
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        array_push($options, ...$this->tags->negated()->all());
							 | 
						
					
						
							| 
								
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        array_push($options, ...$this->filters->negated()->all());
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 00:31:45 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 22:59:50 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								        return implode(' ', array_map(fn(SearchOption $o) => $o->toString(), $options));
							 | 
						
					
						
							
								
									
										
										
										
											2024-10-03 00:31:45 +08:00
										 
									 
								 
							 | 
							
								
							 | 
							
								
							 | 
							
							
								    }
							 | 
						
					
						
							
								
									
										
										
										
											2021-03-08 06:24:05 +08:00
										 
									 
								 
							 | 
							
								
									
										
									
								
							 | 
							
								
							 | 
							
							
								}
							 |