Skip to content

Commit

Permalink
Business logic functions should be 'pure'.
Browse files Browse the repository at this point in the history
  • Loading branch information
fisharebest committed Jul 26, 2023
1 parent 31c839a commit ebb4f7d
Showing 1 changed file with 42 additions and 36 deletions.
78 changes: 42 additions & 36 deletions app/Services/SearchService.php
Expand Up @@ -70,7 +70,6 @@ class SearchService
protected const MAX_SEARCH_RESULTS = 5000;

private TreeService $tree_service;
private $like_operator = 'LIKE';

/**
* @param TreeService $tree_service
Expand All @@ -79,11 +78,6 @@ public function __construct(
TreeService $tree_service
) {
$this->tree_service = $tree_service;

// Allows to make insensitive searches in PostgreSQL
if (DB::connection()->getDriverName() === 'pgsql') {
$this->like_operator = 'ILIKE';
}
}

/**
Expand Down Expand Up @@ -496,7 +490,7 @@ public function searchPlaces(Tree $tree, string $search, int $offset = 0, int $l

// Filter each level of the hierarchy.
foreach (explode(',', $search, 9) as $level => $string) {
$query->where('p' . $level . '.p_place', $this->like_operator, '%' . addcslashes($string, '\\%_') . '%');
$query->where('p' . $level . '.p_place', $this->iLike(), '%' . addcslashes($string, '\\%_') . '%');
}

$row_mapper = static function (object $row) use ($tree): Place {
Expand Down Expand Up @@ -675,18 +669,18 @@ public function searchIndividualsAdvanced(array $trees, array $fields, array $mo
$query->where('individual_name.n_givn', '=', $field_value);
break;
case 'BEGINS':
$query->where('individual_name.n_givn', $this->like_operator, $field_value . '%');
$query->where('individual_name.n_givn', $this->iLike(), $field_value . '%');
break;
case 'CONTAINS':
$query->where('individual_name.n_givn', $this->like_operator, '%' . $field_value . '%');
$query->where('individual_name.n_givn', $this->iLike(), '%' . $field_value . '%');
break;
case 'SDX_STD':
$sdx = Soundex::russell($field_value);
if ($sdx !== '') {
$this->wherePhonetic($query, 'individual_name.n_soundex_givn_std', $sdx);
} else {
// No phonetic content? Use a substring match
$query->where('individual_name.n_givn', $this->like_operator, '%' . $field_value . '%');
$query->where('individual_name.n_givn', $this->iLike(), '%' . $field_value . '%');
}
break;
case 'SDX': // SDX uses DM by default.
Expand All @@ -696,7 +690,7 @@ public function searchIndividualsAdvanced(array $trees, array $fields, array $mo
$this->wherePhonetic($query, 'individual_name.n_soundex_givn_dm', $sdx);
} else {
// No phonetic content? Use a substring match
$query->where('individual_name.n_givn', $this->like_operator, '%' . $field_value . '%');
$query->where('individual_name.n_givn', $this->iLike(), '%' . $field_value . '%');
}
break;
}
Expand All @@ -714,15 +708,15 @@ public function searchIndividualsAdvanced(array $trees, array $fields, array $mo
case 'BEGINS':
$query->where(function (Builder $query) use ($field_value): void {
$query
->where('individual_name.n_surn', $this->like_operator, $field_value . '%')
->orWhere('individual_name.n_surname', $this->like_operator, $field_value . '%');
->where('individual_name.n_surn', $this->iLike(), $field_value . '%')
->orWhere('individual_name.n_surname', $this->iLike(), $field_value . '%');
});
break;
case 'CONTAINS':
$query->where(function (Builder $query) use ($field_value): void {
$query
->where('individual_name.n_surn', $this->like_operator, '%' . $field_value . '%')
->orWhere('individual_name.n_surname', $this->like_operator, '%' . $field_value . '%');
->where('individual_name.n_surn', $this->iLike(), '%' . $field_value . '%')
->orWhere('individual_name.n_surname', $this->iLike(), '%' . $field_value . '%');
});
break;
case 'SDX_STD':
Expand All @@ -733,8 +727,8 @@ public function searchIndividualsAdvanced(array $trees, array $fields, array $mo
// No phonetic content? Use a substring match
$query->where(function (Builder $query) use ($field_value): void {
$query
->where('individual_name.n_surn', $this->like_operator, '%' . $field_value . '%')
->orWhere('individual_name.n_surname', $this->like_operator, '%' . $field_value . '%');
->where('individual_name.n_surn', $this->iLike(), '%' . $field_value . '%')
->orWhere('individual_name.n_surname', $this->iLike(), '%' . $field_value . '%');
});
}
break;
Expand All @@ -747,8 +741,8 @@ public function searchIndividualsAdvanced(array $trees, array $fields, array $mo
// No phonetic content? Use a substring match
$query->where(function (Builder $query) use ($field_value): void {
$query
->where('individual_name.n_surn', $this->like_operator, '%' . $field_value . '%')
->orWhere('individual_name.n_surname', $this->like_operator, '%' . $field_value . '%');
->where('individual_name.n_surn', $this->iLike(), '%' . $field_value . '%')
->orWhere('individual_name.n_surname', $this->iLike(), '%' . $field_value . '%');
});
}
break;
Expand All @@ -760,7 +754,7 @@ public function searchIndividualsAdvanced(array $trees, array $fields, array $mo
case 'INDI:NAME:_HEB':
case 'INDI:NAME:_AKA':
$like = "%\n1 NAME%\n2 " . $parts[2] . ' %' . preg_quote($field_value, '/') . '%';
$query->where('individuals.i_gedcom', $this->like_operator, $like);
$query->where('individuals.i_gedcom', $this->iLike(), $like);
break;
}
} elseif (str_starts_with($field_name, 'INDI:') && str_ends_with($field_name, ':DATE')) {
Expand All @@ -785,10 +779,10 @@ public function searchIndividualsAdvanced(array $trees, array $fields, array $mo
unset($fields[$field_name]);
} elseif (str_starts_with($field_name, 'INDI:') && str_ends_with($field_name, ':PLAC')) {
// SQL can only link a place to a person/family, not to an event.
$query->where('individual_places.p_place', $this->like_operator, '%' . $field_value . '%');
$query->where('individual_places.p_place', $this->iLike(), '%' . $field_value . '%');
} elseif (str_starts_with($field_name, 'FAM:') && str_ends_with($field_name, ':PLAC')) {
// SQL can only link a place to a person/family, not to an event.
$query->where('family_places.p_place', $this->like_operator, '%' . $field_value . '%');
$query->where('family_places.p_place', $this->iLike(), '%' . $field_value . '%');
} elseif (str_starts_with($field_name, 'MOTHER:NAME:') || str_starts_with($field_name, 'FATHER:NAME:')) {
$table = str_starts_with($field_name, 'FATHER:NAME:') ? 'father_name' : 'mother_name';
switch ($parts[2]) {
Expand All @@ -798,18 +792,18 @@ public function searchIndividualsAdvanced(array $trees, array $fields, array $mo
$query->where($table . '.n_givn', '=', $field_value);
break;
case 'BEGINS':
$query->where($table . '.n_givn', $this->like_operator, $field_value . '%');
$query->where($table . '.n_givn', $this->iLike(), $field_value . '%');
break;
case 'CONTAINS':
$query->where($table . '.n_givn', $this->like_operator, '%' . $field_value . '%');
$query->where($table . '.n_givn', $this->iLike(), '%' . $field_value . '%');
break;
case 'SDX_STD':
$sdx = Soundex::russell($field_value);
if ($sdx !== '') {
$this->wherePhonetic($query, $table . '.n_soundex_givn_std', $sdx);
} else {
// No phonetic content? Use a substring match
$query->where($table . '.n_givn', $this->like_operator, '%' . $field_value . '%');
$query->where($table . '.n_givn', $this->iLike(), '%' . $field_value . '%');
}
break;
case 'SDX': // SDX uses DM by default.
Expand All @@ -819,7 +813,7 @@ public function searchIndividualsAdvanced(array $trees, array $fields, array $mo
$this->wherePhonetic($query, $table . '.n_soundex_givn_dm', $sdx);
} else {
// No phonetic content? Use a substring match
$query->where($table . '.n_givn', $this->like_operator, '%' . $field_value . '%');
$query->where($table . '.n_givn', $this->iLike(), '%' . $field_value . '%');
}
break;
}
Expand All @@ -830,18 +824,18 @@ public function searchIndividualsAdvanced(array $trees, array $fields, array $mo
$query->where($table . '.n_surn', '=', $field_value);
break;
case 'BEGINS':
$query->where($table . '.n_surn', $this->like_operator, $field_value . '%');
$query->where($table . '.n_surn', $this->iLike(), $field_value . '%');
break;
case 'CONTAINS':
$query->where($table . '.n_surn', $this->like_operator, '%' . $field_value . '%');
$query->where($table . '.n_surn', $this->iLike(), '%' . $field_value . '%');
break;
case 'SDX_STD':
$sdx = Soundex::russell($field_value);
if ($sdx !== '') {
$this->wherePhonetic($query, $table . '.n_soundex_surn_std', $sdx);
} else {
// No phonetic content? Use a substring match
$query->where($table . '.n_surn', $this->like_operator, '%' . $field_value . '%');
$query->where($table . '.n_surn', $this->iLike(), '%' . $field_value . '%');
}
break;
case 'SDX': // SDX uses DM by default.
Expand All @@ -851,7 +845,7 @@ public function searchIndividualsAdvanced(array $trees, array $fields, array $mo
$this->wherePhonetic($query, $table . '.n_soundex_surn_dm', $sdx);
} else {
// No phonetic content? Use a substring match
$query->where($table . '.n_surn', $this->like_operator, '%' . $field_value . '%');
$query->where($table . '.n_surn', $this->iLike(), '%' . $field_value . '%');
}
break;
}
Expand All @@ -861,14 +855,14 @@ public function searchIndividualsAdvanced(array $trees, array $fields, array $mo
} elseif (str_starts_with($field_name, 'FAM:')) {
// e.g. searches for occupation, religion, note, etc.
// Initial matching only. Need PHP to apply filter.
$query->where('spouse_families.f_gedcom', $this->like_operator, "%\n1 " . $parts[1] . ' %' . $field_value . '%');
$query->where('spouse_families.f_gedcom', $this->iLike(), "%\n1 " . $parts[1] . ' %' . $field_value . '%');
} elseif (str_starts_with($field_name, 'INDI:') && str_ends_with($field_name, ':TYPE')) {
// Initial matching only. Need PHP to apply filter.
$query->where('individuals.i_gedcom', $this->like_operator, "%\n1 " . $parts[1] . "%\n2 TYPE %" . $field_value . '%');
$query->where('individuals.i_gedcom', $this->iLike(), "%\n1 " . $parts[1] . "%\n2 TYPE %" . $field_value . '%');
} elseif (str_starts_with($field_name, 'INDI:')) {
// e.g. searches for occupation, religion, note, etc.
// Initial matching only. Need PHP to apply filter.
$query->where('individuals.i_gedcom', $this->like_operator, "%\n1 " . $parts[1] . '%' . $parts[2] . '%' . $field_value . '%');
$query->where('individuals.i_gedcom', $this->iLike(), "%\n1 " . $parts[1] . '%' . $parts[2] . '%' . $field_value . '%');
}
}

Expand Down Expand Up @@ -1079,7 +1073,7 @@ private function paginateQuery(Builder $query, Closure $row_mapper, Closure $row
private function whereSearch(Builder $query, Expression|string $column, array $search_terms): void
{
foreach ($search_terms as $search_term) {
$query->where($column, $this->like_operator, '%' . addcslashes($search_term, '\\%_') . '%');
$query->where($column, $this->iLike(), '%' . addcslashes($search_term, '\\%_') . '%');
}
}

Expand All @@ -1093,9 +1087,9 @@ private function whereSearch(Builder $query, Expression|string $column, array $s
private function wherePhonetic(Builder $query, $field, string $soundex): void
{
if ($soundex !== '') {
$query->where(static function (Builder $query) use ($soundex, $field): void {
$query->where(function (Builder $query) use ($soundex, $field): void {
foreach (explode(':', $soundex) as $sdx) {
$query->orWhere($field, $this->like_operator, '%' . $sdx . '%');
$query->orWhere($field, $this->iLike(), '%' . $sdx . '%');
}
});
}
Expand Down Expand Up @@ -1325,4 +1319,16 @@ private function submitterRowMapper(): Closure
return Registry::submitterFactory()->mapper($tree)($row);
};
}

/**
* @internal - a better solution would support other RDBMS, probably by using collations.
*/
private function iLike(): string
{
if (DB::connection()->getDriverName() === 'pgsql') {
return 'ILIKE';
}

return 'LIKE';
}
}

0 comments on commit ebb4f7d

Please sign in to comment.