mongerinc/search-request

表示复杂的搜索查询,并转换为 JSON 格式

v6.0.0 2018-06-28 21:22 UTC

README

此库提供了一组类,帮助表示复杂数据的请求,并提供了一种将请求转换为和从标准 JSON 格式的方法。如果你有具有大量参数($filters、$groupings、$page、$rowsPerPage 等)的接口,或者如果你只是寻找一种标准方式将复杂数据请求传达给其他应用而无需在 JSON 中表示这些数据时绞尽脑汁,你会喜欢这个库。

  • 版本 5.6.2

Build Status

目录

安装

通过在 composer.json 文件的 require 块中添加以下内容来安装 SearchRequest:

"mongerinc/search-request": "5.*"

使用方法

从头创建 SearchRequest 时,首先需要实例化一个请求

$request = new SearchRequest;

//or

$request = SearchRequest::create();

//or call any method statically

$request = SearchRequest::where('something', true);

作为起点,每个搜索请求都没有排序、筛选和分组。分页从第 1 页开始,默认每页 10 行。

JSON

使用 toJson() 方法,每个 SearchRequest 实例都可以编译成一个 JSON 字符串,你可以使用它来跨应用边界进行通信。

$request->toJson();

同样,你可以使用由 SearchRequest 实例编译的 JSON 字符串来构建一个新的 SearchRequest 实例。

$request = new SearchRequest($json);

选择

设置请求上的选择的最常见方法是使用 select() 方法,它将覆盖任何现有的选择

$request->select(['field1', 'field2']);

如果你想添加到现有的选择中,你可以调用 addSelect() 方法。你可以链接此方法

$request->addSelect('field1')->addSelect('field2');

通过 getSelects() 方法检索选择集。

$request->getSelects();

排序

排序请求的最常见方法是使用 sort() 方法,它将覆盖任何现有的排序

$request->sort('field', 'asc');

任何排序调用中的第一个参数是字符串字段,第二个参数是排序方向,它限于 ascdesc

如果你想创建具有多个排序的请求,你可以调用 addSort() 方法。你可以链接此方法

$request->addSort('field', 'asc')->addSort('otherField', 'desc');

如果你想检索排序,你可以调用 getSort() 方法以获取主要排序,或者你可以调用 getSorts() 以获取所有排序的数组。每个排序都表示为一个 Sort 实例,你可以要求字段和方向

$sort = $request->getSort();

$databaseQuery->orderBy($sort->getField(), $sort->getDirection());

分组

可以使用 groupBy() 方法对 SearchRequest 进行分组。该方法接受字符串或字符串数组作为输入。该方法也可以链接。

$request->groupBy('field')->groupBy('anotherField');

$request->groupBy(['field', 'anotherField']);

通过 getGroups() 方法检索组集。

$request->getGroups();

分页

创建新的 SearchRequest 默认为第 1 页和每页 10 行的限制。如果你想修改这个,你可以调用 page()limit() 方法

$request->page(5)->limit(40);

如果你想简单地转到下一页,你可以调用 nextPage() 方法,它会将当前页码增加 1。

通过方法 getPage()getLimit() 检索页码和限制。

$limit = $request->getLimit();
$page = $request->getPage();

$databaseQuery->take($limit)->skip(($page - 1) * $limit);

或者,你可以调用 getSkip() 来避免上面的计算。

如果你想忽略请求的分页,你可以调用 unlimited()all() 方法。

$request = $request->unlimited();

if (!$request->isUnlimited())
   $databaseQuery->take($request->getLimit())->skip($request->getSkip());

无限功能只是一个标志。您的数据库查询应用必须使用 isUnlimited() 方法来检查此标志以支持该功能。

筛选

可以使用 where() 方法对 SearchRequest 进行过滤。作为第二个参数可以提供一个操作符,可能的类型包括 =>>=<<=!=innot inlikenot likeregexnot regexexistsnot existsbetweennot between。如果没有提供操作符,则假定它是 =

$request->where('someField', '>=', 5.45)
        ->where('isFun', true);            //assumed to be an equality

每个类似单词的操作符(inlikeregexexistsbetween)都有一组四个伴随的辅助方法。这些方法遵循以下通用格式

$request->where{Word}($field, $value)
        ->orWhere{Word}($field, $value)
        ->whereNot{Word}($field, $value)
        ->orWhereNot{Word}($field, $value);

//example:
$request->whereLike($field, $value)
        ->orWhereLike($field, $value)
        ->whereNotLike($field, $value)
        ->orWhereNotLike($field, $value);

可以使用 getFilters() 方法从搜索请求中读取过滤器

foreach ($request->getFilters() as $filter)
{
	$databaseQuery->where($filter->getField(), $filter->getOperator(), $filter->getValue());
}

您还可以在 SearchRequestFilterSet 上调用 getFilter($field)getFilterValue($field) 来获取与该字段名称匹配的第一个 Filter/值。这对于您的过滤器相对简单且您只期望每个字段名称有一个值的情况非常有用。

$request->where('foo', true);
$request->where('foo', '>', 5);

$request->getFilterValue('foo'); //true
$request->getFilters()->getFilterValue('foo'); //true

可以通过使用嵌套条件来实现更复杂的过滤。假设您想要创建一个表示以下伪-SQL条件语句的请求

...WHERE goodTimes = true AND (profit > 1000 OR revenue > 1000000)

...您将执行以下操作...

$request->where('goodTimes', true)
        ->where(function($filterSet)
        {
            $filterSet->where('profit', '>', 1000)
                      ->orWhere('revenue', '>', 1000000);
        });

在从 SearchRequest 读取一组复杂的条件时,有几个重要概念需要理解

  1. 每个嵌套层(包括顶层)都由一个 FilterSet 表示。一个 FilterSet 有一个布尔(和/或)操作符,可以由任何组合的 FilterFilterSet 对象组成。这些对象按它们被输入的顺序提供。
  2. 一个 Filter 对象表示一个字段、值、条件操作符以及一个布尔(和/或)操作符。

由于条件嵌套在理论上是无界的,您可能需要实现一个递归函数来将请求应用于您选择的库(如数据库查询构建器)。一个例子可以在 /examples 目录中看到。

也可以通过名称删除过滤器

$request->where('foo', 1)->where('moo', 2)->where('goo', 3);

$request->removeFilters('foo')->removeFilters(['moo', 'goo']);

分解

可以通过这种方式对 SearchRequest 进行分面(即获取属性值及其计数)

$facet = $request->facet('someField');

这将创建一个针对 someField 的分面,并且与其它方法不同,它返回一个 Facet 实例而不是 SearchRequest。您也可以一次创建多个分面

$request->addFacets(['someField', 'someOtherField']);

您也可以按字段名称一次获取一个分面(只会返回第一个匹配项)

$request->getFacet('someField');

或者一次性获取所有分面

$request->getFacets();

可以通过计数或值(默认)以及方向来对分面的结果进行排序

$facet->isCountSorting(); //bool
$facet->isValueSorting(); //bool
$facet->getSortDirection(); //'asc' or 'desc'

$facet->sortByCount();
$facet->sortByValue();
$facet->setSortDirection('asc');

分面字段必须至少有 1 个值才能在结果集中返回。

$facet->getMinimumCount(); //1

$facet->setMinimumCount(5);

默认情况下,对于分面字段的现有过滤器会从构建分面结果时排除。

$facet->shouldExcludeOwnFilters(); //true

$facet->excludeOwnFilters();
$facet->includeOwnFilters();

分面也可以像搜索请求一样进行分页,并使用相同的默认值

$facet->getPage();
$facet->getLimit();
$facet->getSkip();

$facet->page(5)->limit(100);
$facet->nextPage();

字段替换

在创建搜索请求时,您通常希望将数据存储方案的具体细节隐藏在您的代码库之外。例如,您可能在 products 表上有一个非规范化 SQL 字段 category_name,它在输出过程中被格式化为

[
	...
	'category' => [
		'id' => $product->category_id,
		'name' => $product->category_name,
	],
	...
]

因此,系统的其余部分将 category.name 视为字段,但您的 SQL 存储库只理解 category_name。这就是字段替换发挥作用的地方

//abstract layer code
$request->where('category.name', 'Foo');

//then later in the repository
$request->substituteField('category.name', 'category_name');

您也可以通过传递一个包含 original => substitution 值的数组一次替换多个字段

$request->substituteFields(
	['category.name' => 'category_name'],
	['category.id' => 'category_id'],
);

示例

本项目的根目录下有一个/examples目录,其中包含了一些如何将搜索请求应用于数据库查询的示例。由于本项目受到了Laravel查询构建器的启发,其中一个示例展示了如何通过SearchRequest以可重用的方式将复杂的参数集应用于查询。采用相同的方法,SearchRequest可以用于将复杂的参数集应用于任何需要它们的库(例如MongoDB、Solr等)。