Laravel поиск по нескольким ключевым словам на нескольких таблицах с оператором AND / OR - PullRequest
0 голосов
/ 02 ноября 2019

У меня есть форма расширенного поиска, которая фильтрует результаты из базы данных mySql. Есть одна часть, которая представляет собой поиск по ключевым словам, который должен соответствовать любым текстовым полям во всех связанных таблицах.

Это код указанной формы поиска.

<form action="/search" method="POST">
   Keyword: <input type="text" name="keyword">
   Matching: <input type="radio" name="keywordControl" value="AND"> Match every words<br>
             <input type="radio" name="keywordControl" value="OR"> Match any words<br>
 <input type="submit">
</form>

Таблицы:

CREATE TABLE `User` (
  `id` INT,
  `firstName` VARCHAR(100),
  `lastName` VARCHAR(100),
  `email` VARCHAR(100),
  `address` VARCHAR(400)
);

CREATE TABLE `savedLinks` (
  `id` INT,
  `userId` INT,
  `linkName` VARCHAR(100),
  `linkURL` VARCHAR(400),
  `linkNote` VARCHAR(100),
);

CREATE TABLE `BlogEntry` (
  `id` INT,
  `userId` INT,
  `entryTitle` VARCHAR(100),
  `entryExcerpt` VARCHAR(100),
  `entryBody` TEXT,
);

Что мне нужно:

, если я ищу по этим параметрам:

ключевое слово: naruto area51

keywordControl: AND

Я ожидаю увидеть результаты, которые соответствуют как наруто, так и области 51 во всех таблицах.

Однако я могу отфильтровать результаты в одной таблице. Мне удается отображать результаты, такие как

  • "Мистер Наруто ", который имеет адрес " Area51 lane ..."

  • Любой, кто сохранил ссылки, содержащие как " Наруто ", так и " Area51 "

  • Любой, кто ведет блого " Наруто Бег" и " Зона51 "

Но я не могу привести такие результаты, как

  • "Мистер Наруто ", который ведет блог о " Area51 "

  • всех, кто сохранил ссылки о " Наруто", и написал в блоге о" Area51"

Потому что последние должны искать по всем таблицам. Это мой красноречивый код laravel.

$keyword = urldecode($request->input('keyword'));
$keywords = preg_split('/\s+/', $keyword, -1, PREG_SPLIT_NO_EMPTY);

$users = User::when(sizeof($keywords) > 0, function ($query) use ($keywords, $keywordControl) {
    $query->where(function ($query) use ($keywords, $keywordControl) {
        foreach ($keywords as $keyword) {
            if ($keywordControl == 'AND') {
                $query->whereRaw("CONCAT(firstName,lastName,email,address) LIKE ?", "%{$keyword}%");
            } else {
                $query->orWhereRaw("CONCAT(firstName,lastName,email,address) LIKE ?", "%{$keyword}%");
            }
        }
    });
})
->when(sizeof($keywords) > 0, function ($query) use ($keywords, $keywordControl) {
    $query->whereHas('savedLinks', function ($query) use ($keywords, $keywordControl) {
        $query->where(function ($query) use ($keywords, $keywordControl) {
            foreach ($keywords as $keyword) {
                if($keywordControl == 'AND'){
                    $query->whereRaw("CONCAT(linkName,linkURL,linkNote) LIKE ?", "%{$keyword}%");
                } else {
                    $query->orWhereRaw("CONCAT(linkName,linkURL,linkNote) LIKE ?", "%{$keyword}%");
                }
            }
        });
    });
})
->when(sizeof($keywords) > 0, function ($query) use ($keywords, $keywordControl) {
    $query->whereHas('blogEntry', function ($query) use ($keywords, $keywordControl) {
        $query->where(function ($query) use ($keywords, $keywordControl) {
            foreach ($keywords as $keyword) {
                if($keywordControl=='AND') {
                    $query->whereRaw("CONCAT(entryTitle,entryExcerpt,entryBody) LIKE ?", "%{$keyword}%");
                } else {
                    $query->orWhereRaw("CONCAT(entryTitle,entryExcerpt,entryBody) LIKE ?", "%{$keyword}%");
                }
            }
        });
    });
})
->get();

Я имею в виду, что мне нужно создать другую таблицу и объединить весь этот текст в одном поле только для целей поиска. Я не знаю, хорошая это идея или нет. Пожалуйста, ведите меня, спасибо!

1 Ответ

0 голосов
/ 02 ноября 2019

Определили ли вы отношения для пользователя, SavedLinks и блогов? Для этого запроса необходимо, чтобы пользователь имел правильные отношения с соответствующими моделями:

# User model
public function savedLinks()
{
    return $this->hasMany(SavedLink::class, 'userId', 'id');
}

public function blogEntries()
{
    return $this->hasMany(BlogEntry::class, 'userId', 'id');
}

Все ваши условия when(...) одинаковы. Этот запрос может быть переписан с учетом этого.

Кроме того, whereRaw() принимает 3 параметров. Если вы проверите это определение , вы увидите следующее:

/**
 * Add a raw where clause to the query.
 *
 * @param  string  $sql
 * @param  mixed   $bindings
 * @param  string  $boolean
 * @return $this
 */
public function whereRaw($sql, $bindings = [], $boolean = 'and')
{
    $this->wheres[] = ['type' => 'raw', 'sql' => $sql, 'boolean' => $boolean];
    $this->addBinding((array) $bindings, 'where');
    return $this;
}

/**
 * Add a raw or where clause to the query.
 *
 * @param  string  $sql
 * @param  mixed   $bindings
 * @return \Illuminate\Database\Query\Builder|static
 */
public function orWhereRaw($sql, $bindings = [])
{
    return $this->whereRaw($sql, $bindings, 'or');
}

Мне также кажется, что вы должны использовать orWhereHas. Вы можете использовать empty() вместо sizeof() и unless() ( это просто противоположная функция ) вместо when(), чтобы сделать вещи более читабельными. Имея это в виду, ваш запрос уже может быть сведен к этому, если предположить, что $keywordControl является либо И, либо ИЛИ:

$users = User::unless(empty($keywords), function ($query) use ($keywords, $keywordControl) {
    $query->where(function ($query) use ($keywords, $keywordControl) {
        foreach ($keywords as $keyword) {
            $query->whereRaw("CONCAT(firstName,lastName,email,address) LIKE ?", "%{$keyword}%", $keywordControl);
        }
    });
})
->orWhereHas('savedLinks', function ($query) use ($keywords, $keywordControl) {
    $query->where(function ($query) use ($keywords, $keywordControl) {
        foreach ($keywords as $keyword) {
            $query->whereRaw("CONCAT(linkName,linkURL,linkNote) LIKE ?", "%{$keyword}%", $keywordControl);
        }
    });
})
->orWhereHas('blogEntry', function ($query) use ($keywords, $keywordControl) {
    $query->where(function ($query) use ($keywords, $keywordControl) {
        foreach ($keywords as $keyword) {
            $query->whereRaw("CONCAT(entryTitle,entryExcerpt,entryBody) LIKE ?", "%{$keyword}%", $keywordControl);
        }
    });
})
->get();

Я не знаю, происходит ли объединение вещей с whereRaw, а затем сравнение выполняется быстрее, чемиспользуя условия where для каждого атрибута.

# Example 
->whereRaw("CONCAT(entryTitle,entryExcerpt,entryBody) LIKE ?", "%{$keyword}%", $keywordControl);
# vs
->where([
    ['entryTitle', 'like', "%{keyword}%", $keywordControl],
    ['entryExcerpt', 'like', "%{keyword}%", $keywordControl],
    ['entryBody', 'like', "%{keyword}%", $keywordControl],
])
# If someone could comment on this I'd be grateful.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...