按与 Laravel 集合 whereHas() 的相关性排序
Sorting by relevance with Laravel Collections whereHas()
接受这个工作代码。它首先在 users
table 中搜索关键字,然后使用 whereHas()
方法搜索枢轴 table category_user
以查找分配给给定的任何用户基于搜索的类别。这很好用,检索了所有条目。
所以要绝对清楚 3 table 正在使用,users
、categories
和 category_user
.
我正在使用 Laravel 集合 sortByDesc()
根据相关性对条目进行排序。因此,如果 类别 1、类别 2 和 我的示例搜索词 的关键字都存储在 用户 1 并且只有 类别 1 存储在 用户 2、用户 1 将首先显示在结果中。
下面的代码正是这样做的,但是 ONLY users
table。由于我需要订购的方式的独特性,我已经进行了很好的扫描并且似乎无法将此解决方案用于此类查询。任何人都可以建议任何方法吗?
//props for sortByDesc
$props = ['name', 'slug', 'location', 'company_name', 'biography', 'usually_active'];
//Search Query
//1. Search users table for keywords
//2. use whereHas() to search pivot table of categories for keyword and filters selected
//3. use sortByDesc collections to order results based number of matches, most matches ordered at the top
$search_results = User::where('approved', '=', 1)
->where('user_type_id', '=', 1)
->orWhere(function ($q) use ($search_items) {
//search users tables
foreach ($search_items as $value) {
$q->orWhere('name', 'like', "%{$value}%");
$q->orWhere('location', 'like', "%{$value}%");
$q->orWhere('company_name', 'like', "%{$value}%");
$q->orWhere('biography', 'like', "%{$value}%");
$q->orWhere('usually_active', 'like', "%{$value}%");
}
})->whereHas('categories', function ($q) use($search_items) {
//search categories table
if(!empty($search_items)):
$q->whereIn('name', $search_items);
$q->orWhereIn('slug', $search_items);
endif;
//Now we sort based on best matches
})->get()->sortByDesc(function($i, $k) use ($search_items, $props) {
// The bigger the weight, the higher the record
$weight = 0;
// Iterate through search terms
foreach($search_items as $item) {
foreach($props as $prop) {
if(strpos($i->{$prop}, $item) !== false)
$weight += 1; // Increase weight if the search term is found
}
}
return $weight;
});
//get the results and paginate
$search_results = $search_results->values()->all();
$search_results = $this->paginate($search_results);
简答:
为要过滤的类别列添加 select。请注意,生成的数据集将包含 categories.name
之类的列,因此您可能需要相应地调整 search_items
中的键或为添加的 select.
添加别名
为此,您需要在查询中加入类别 table,我猜到了您的外键,但您可以随意在连接子句中更正它们以匹配您在数据库中的内容。
这样您就可以将 where 子句简化为一个
<?
User::join('categories', 'user.categories_id', '=', 'categories.id')
where('approved', '=', 1)
->where('user_type_id', '=', 1)
->orWhere(function ($q) use ($search_items) {
//search users tables
foreach ($search_items as $value) {
$q->orWhere('name', 'like', "%{$value}%");
$q->orWhere('location', 'like', "%{$value}%");
$q->orWhere('company_name', 'like', "%{$value}%");
$q->orWhere('biography', 'like', "%{$value}%");
$q->orWhere('usually_active', 'like', "%{$value}%");
$q->orWhereIn('categories.name', $search_items);
$q->orWhereIn('categories.slug', $search_items);
}
})
// Add the columns you want to sort on
->addSelect(['categories.name', 'categories.slug'])
->get()->sortByDesc(function($i, $k) use ($search_items, $props) {
// The bigger the weight, the higher the record
$weight = 0;
// Iterate through search terms
foreach($search_items as $item) {
foreach($props as $prop) {
if(strpos($i->{$prop}, $item) !== false)
$weight += 1; // Increase weight if the search term is found
}
}
return $weight;
});
更长的答案:
当您从模型开始查询时,在您的情况下,User
、eloquent 会自动将此模型的列添加到 select,而不管定义的关系如何模型。
如果要访问加入的 table 上的列,您需要使用 ->addSelect([...])
将它们手动添加到 select。
您可以在官方文档上阅读更多相关信息:https://laravel.com/docs/8.x/queries#specifying-a-select-clause
解决方案是使用 withCount()
方法来确定存储在数据透视表 table 中的用户的类别数。然后,我可以使用每个项目返回的 categories_count
来根据每个用户标记的类别数量对条目进行排序。
$search_results = User::where('approved', '=', 1)
->where('user_type_id', '=', 1)
->where(function ($q) use ($search_items) {
//search users tables
foreach ($search_items as $value) {
$q->orWhere('users.name', 'like', "%{$value}%");
$q->orWhere('users.location', 'like', "%{$value}%");
$q->orWhere('users.company_name', 'like', "%{$value}%");
$q->orWhere('users.biography', 'like', "%{$value}%");
}
})->orWhereHas('categories', function ($q) use($search_items) {
if(!empty($search_items)):
$q->whereIn('name', $search_items);
$q->orWhereIn('slug', $search_items);
endif;
//Now we sort based on best matches
})->withCount([
'categories' => function ($q) use($search_items) {
//search categories table
if(!empty($search_items)):
$q->whereIn('name', $search_items);
$q->orWhereIn('slug', $search_items);
endif;
}
])->orderByDesc('categories_count')->paginate(25);
接受这个工作代码。它首先在 users
table 中搜索关键字,然后使用 whereHas()
方法搜索枢轴 table category_user
以查找分配给给定的任何用户基于搜索的类别。这很好用,检索了所有条目。
所以要绝对清楚 3 table 正在使用,users
、categories
和 category_user
.
我正在使用 Laravel 集合 sortByDesc()
根据相关性对条目进行排序。因此,如果 类别 1、类别 2 和 我的示例搜索词 的关键字都存储在 用户 1 并且只有 类别 1 存储在 用户 2、用户 1 将首先显示在结果中。
下面的代码正是这样做的,但是 ONLY users
table。由于我需要订购的方式的独特性,我已经进行了很好的扫描并且似乎无法将此解决方案用于此类查询。任何人都可以建议任何方法吗?
//props for sortByDesc
$props = ['name', 'slug', 'location', 'company_name', 'biography', 'usually_active'];
//Search Query
//1. Search users table for keywords
//2. use whereHas() to search pivot table of categories for keyword and filters selected
//3. use sortByDesc collections to order results based number of matches, most matches ordered at the top
$search_results = User::where('approved', '=', 1)
->where('user_type_id', '=', 1)
->orWhere(function ($q) use ($search_items) {
//search users tables
foreach ($search_items as $value) {
$q->orWhere('name', 'like', "%{$value}%");
$q->orWhere('location', 'like', "%{$value}%");
$q->orWhere('company_name', 'like', "%{$value}%");
$q->orWhere('biography', 'like', "%{$value}%");
$q->orWhere('usually_active', 'like', "%{$value}%");
}
})->whereHas('categories', function ($q) use($search_items) {
//search categories table
if(!empty($search_items)):
$q->whereIn('name', $search_items);
$q->orWhereIn('slug', $search_items);
endif;
//Now we sort based on best matches
})->get()->sortByDesc(function($i, $k) use ($search_items, $props) {
// The bigger the weight, the higher the record
$weight = 0;
// Iterate through search terms
foreach($search_items as $item) {
foreach($props as $prop) {
if(strpos($i->{$prop}, $item) !== false)
$weight += 1; // Increase weight if the search term is found
}
}
return $weight;
});
//get the results and paginate
$search_results = $search_results->values()->all();
$search_results = $this->paginate($search_results);
简答:
为要过滤的类别列添加 select。请注意,生成的数据集将包含 categories.name
之类的列,因此您可能需要相应地调整 search_items
中的键或为添加的 select.
为此,您需要在查询中加入类别 table,我猜到了您的外键,但您可以随意在连接子句中更正它们以匹配您在数据库中的内容。
这样您就可以将 where 子句简化为一个
<?
User::join('categories', 'user.categories_id', '=', 'categories.id')
where('approved', '=', 1)
->where('user_type_id', '=', 1)
->orWhere(function ($q) use ($search_items) {
//search users tables
foreach ($search_items as $value) {
$q->orWhere('name', 'like', "%{$value}%");
$q->orWhere('location', 'like', "%{$value}%");
$q->orWhere('company_name', 'like', "%{$value}%");
$q->orWhere('biography', 'like', "%{$value}%");
$q->orWhere('usually_active', 'like', "%{$value}%");
$q->orWhereIn('categories.name', $search_items);
$q->orWhereIn('categories.slug', $search_items);
}
})
// Add the columns you want to sort on
->addSelect(['categories.name', 'categories.slug'])
->get()->sortByDesc(function($i, $k) use ($search_items, $props) {
// The bigger the weight, the higher the record
$weight = 0;
// Iterate through search terms
foreach($search_items as $item) {
foreach($props as $prop) {
if(strpos($i->{$prop}, $item) !== false)
$weight += 1; // Increase weight if the search term is found
}
}
return $weight;
});
更长的答案:
当您从模型开始查询时,在您的情况下,User
、eloquent 会自动将此模型的列添加到 select,而不管定义的关系如何模型。
如果要访问加入的 table 上的列,您需要使用 ->addSelect([...])
将它们手动添加到 select。
您可以在官方文档上阅读更多相关信息:https://laravel.com/docs/8.x/queries#specifying-a-select-clause
解决方案是使用 withCount()
方法来确定存储在数据透视表 table 中的用户的类别数。然后,我可以使用每个项目返回的 categories_count
来根据每个用户标记的类别数量对条目进行排序。
$search_results = User::where('approved', '=', 1)
->where('user_type_id', '=', 1)
->where(function ($q) use ($search_items) {
//search users tables
foreach ($search_items as $value) {
$q->orWhere('users.name', 'like', "%{$value}%");
$q->orWhere('users.location', 'like', "%{$value}%");
$q->orWhere('users.company_name', 'like', "%{$value}%");
$q->orWhere('users.biography', 'like', "%{$value}%");
}
})->orWhereHas('categories', function ($q) use($search_items) {
if(!empty($search_items)):
$q->whereIn('name', $search_items);
$q->orWhereIn('slug', $search_items);
endif;
//Now we sort based on best matches
})->withCount([
'categories' => function ($q) use($search_items) {
//search categories table
if(!empty($search_items)):
$q->whereIn('name', $search_items);
$q->orWhereIn('slug', $search_items);
endif;
}
])->orderByDesc('categories_count')->paginate(25);