【问题标题】:Laravel multiple keywords search on multi table with operator AND/ORLaravel 使用运算符 AND/OR 在多表上搜索多个关键字
【发布时间】:2019-11-03 01:48:48
【问题描述】:

我有一个高级搜索表单,可以过滤来自 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,
);

我需要什么:

如果我按这些参数搜索:

关键词:火影忍者51区

keywordControl:AND

我希望在所有表格中看到与 naruto 和 area51 匹配的结果。

但是,我可以做的是在同一张表上过滤结果。我设法显示像

这样的结果
  • “先生火影忍者”,地址为“Area51车道...”

  • 保存包含“Naruto”和“Area51”的链接的任何人

  • 任何写过“火影忍者跑步”和“Area51”的人

但我不能带来类似的结果

  • “火影忍者先生”,曾在博客上写过“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();

我的想法是,或者我是否应该创建另一个表并将所有这些文本连接到同一字段中,仅用于搜索目的。我不知道这是不是个好主意。请指导我,谢谢!

【问题讨论】:

  • 即使它确实有效,这也会非常缓慢
  • 更新:所以我确实创建了另一个表并将所有文本连接到单个 TEXT 字段中。它工作得很快。易于编写查询。唯一的缺点是它占用了一些存储空间,我还有很多。

标签: mysql laravel laravel-5 laravel-5.7


【解决方案1】:

您是否定义了用户、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 参数。如果您检查it's definition,您将看到以下内容:

/**
 * 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() (it's just the opposite function) 代替when(),以使内容更具可读性。考虑到这一点,您的查询已经可以简化为,假设 $keywordControl 是 AND 或 OR:

$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.

【讨论】:

    猜你喜欢
    • 2020-07-20
    • 2017-11-25
    • 2012-09-09
    • 2021-01-03
    • 2014-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多