diff --git a/src/Typesense/Index.php b/src/Typesense/Index.php index 9748e85..451b8f2 100644 --- a/src/Typesense/Index.php +++ b/src/Typesense/Index.php @@ -158,6 +158,12 @@ public function getOrCreateIndex() return $collection; } + public function getTypesenseSchemaFields(): Collection + { + return collect(Arr::get($this->getOrCreateIndex()->retrieve(), 'fields', [])) + ->pluck('type', 'name'); + } + private function getDefaultFields(Searchable $entry): array { return [ diff --git a/src/Typesense/Query.php b/src/Typesense/Query.php index fa02a4e..c2dfaa9 100644 --- a/src/Typesense/Query.php +++ b/src/Typesense/Query.php @@ -19,9 +19,91 @@ public function getSearchResults($query) return $this; } + public function whereStatus($string) + { + return $this->where('status', $string); + } + + private function wheresToFilter(array $wheres): string + { + $filterBy = ''; + + $schemaFields = $this->index->getTypesenseSchemaFields(); + + foreach ($this->wheres as $where) { + if ($filterBy != '') { + $filterBy .= $where['boolean'] == 'and' ? ' && ' : ' || '; + } + + if ($where['type'] == 'Nested') { + $filterBy .= ' ( '.$this->wheresToFilter($where->query['wheres']).' ) '; + + continue; + } + + // if its not in our typesense schema, we cant filter on it + if (! $schemaType = $schemaFields->get($where['column'])) { + continue; + } + + $filterBy .= ' ( '; + + switch ($where['type']) { + case 'JsonContains': + case 'JsonOverlaps': + case 'WhereIn': + $filterBy .= $where['column'].':'.$this->transformArrayOfValuesForTypeSense($schemaType, $where['values']); + break; + + case 'JsonDoesnContain': + case 'JsonDoesntOverlap': + case 'WhereNotIn': + $filterBy .= $where['column'].':!='.$this->transformArrayOfValuesForTypeSense($schemaType, $where['values']); + break; + + default: + $filterBy .= $where['column'].':'.($where['operator'] != '=' ? $where['operator'] : '').$this->transformValueForTypeSense($schemaType, $where['value']); + break; + } + + $filterBy .= ' ) '; + + } + + return $filterBy; + } + + private function transformArrayOfValuesForTypeSense(string $schemaType, array $values): array + { + return json_encode( + collect($where['values']) + ->map(fn ($value) => $this->transformValueForTypeSense($schemaType, $values)) + ->values() + ->all() + ); + } + + private function transformValueForTypeSense(string $schemaType, mixed $value): mixed + { + return match (str_replace('[]', '', $schemaType)) { + 'int32', 'int64' => (int) $value, + 'float' => (float) $value, + 'bool' => $value ? 'true' : 'false', + default => '`'.$value.'`' + }; + } + private function getApiResults() { - return $this->index->searchUsingApi($this->query ?? '', ['per_page' => $this->perPage, 'page' => $this->page]); + $options = ['per_page' => $this->perPage, 'page' => $this->page]; + + $filterBy = $this->wheresToFilter($this->wheres); + + if ($filterBy) { + $options['filter_by'] = $filterBy; + } + + return $this->index->searchUsingApi($this->query ?? '', $options); } public function forPage($page, $perPage = null)