SharePoint 中的查询优化

了解如何在处理搜索查询和结果时,以编程方式使用 SharePoint 查询精简功能。

查询精简功能可用于为最终用户提供与查询相关的精简选项。 借助这些功能,最终用户可以使用针对结果计算出的精简数据,向下钻取搜索结果。 精简数据是由索引组件计算得出,依据是针对搜索查询的所有结果聚合的托管属性统计信息。

通常,查询细化用于与索引项(如项目中出现的创建日期、作者或文件类型)关联的元数据。 通过使用细化选项,你可以优化查询以仅显示特定时段内创建的项,或仅显示特定文件类型的项。

在查询对象模型中使用精简条件

查询细化所涉及的两类查询:

  1. 可以通过向最终用户的查询添加精简条件规范,请求在搜索结果中返回 一组精简条件 。 细化规格是 Refiners 属性的输入。 此查询针对搜索索引运行。 搜索结果由相关结果和细化数据组成。
  2. 你可以通过创建精简的查询使用细化数据深入了解搜索结果。 将 RefinementFilters 属性添加到查询中,以便最终搜索结果既满足最终用户的原始查询文本的要求,也满足精简数据中所选优化选项的要求。

以下各部分详细说明这些步骤,并提供代码示例。

使用精简条件规格将精简条件添加到最终用户的查询中

可以使用 KeywordQuery 类的 Refiners 属性指定请求的查询精简条件。 请使用以下语法指定请求的查询精简条件:

<refiner>[,<refiner>]*

每个 refiner 都具有以下格式:

<refiner-name>[(parameter=value[,parameter=value]*)]?

其中:

  • <refiner-name> 是与精简条件关联的托管属性的名称。 在搜索架构中必须将此托管属性设置为 RefinableSortable
  • parameter=value 对的可选列表指定已命名精简条件的非默认配置值。 如果精简条件的参数未在括号中列出,搜索架构配置会提供默认设置。 表 1 列出了 parameter=value 对可能的值。

注意

指定精简条件时,至少必须指定托管属性 refiner-name

示例

Refiners = "FileType"

或者你还可以使用高级语法调整精简条件设置:

Refiners = "FileType,Write(discretize=manual/2013-01-01/2013-08-22/2013-09-15/2013-09-21/2013-09-22),companies"

表 1:精简条件参数列表

参数 说明
deephits
替换作为精简条件计算基数的默认点击量。 如果生成精简条件,则将计算查询的所有结果。 正常情况下,使用此参数将提升搜索性能。
语法
deephits=<integer value>
示例
price(deephits=1000)
注意:此限制适用于每个索引分区。 由于跨搜索分区进行聚合,因此计算出的实际命中数会大于此值。
discretize
指定数字精简条件的自定义间隔(精简量化)。
语法
discretize=manual/<threshold>/<threshold>[/<threshold>]*
示例
write(discretize=manual/2013-01-01/2013-08-22/2013-09-15/2013-09-21/2013-09-22)
<threshold> 属性指定每个精简量化的阈值。
在指定的第一个阈值下面有一个针对所有内容的时间间隔,每个连续阈值间有一个时间间隔,最后一个阈值上面也有一个针对所有内容的时间间隔。
对于类型 DateTime 的精简条件,按照以下 ISO 8601 兼容的格式之一指定阈值:
  • YYYY-MM-DD
  • YYYY-MM-DDThh:mm:ss
  • YYYY-MM-DDThh:mm:ss:Z
格式的值指定以下内容:
  • YYYY - 四位数年份。 仅支持四位数年份。
  • MM - 两位数月份。 01 = 一月。
  • DD - 月份中两位数的天数( 从 01 到 31)。
  • T - 字母 "T"。
  • hh - 两位数的小时数(从 00 到 23)。 不允许用 A.M. 或 P.M. 表示。
  • mm - 两位数的分钟数(从 00 到 59)。
  • ss - 两位数的秒数(00 到 59)。
重要: 所有日期/时间值都必须根据协调世界时 (UTC) 指定,也称为格林威治标准时间 (GMT) 区域。 UTC 时区标识符(结尾的“Z”字符)是可选的。
sort
定义量化在字符串精简条件内的排序方式。
语法
sort=<property>/<direction>
这些属性执行以下操作:
  • <property> - 指定排序算法。 下面列出了有效的小写值:
    • frequency - 按在量化内出现的次数进行排序。
    • name - 按标签名称排序。
    • number - 将字符串视为数字,并使用数字分类。 此值可用于指定离散值,例如,执行包含在类型 String 的托管属性中的值的数字分类。
  • <direction> - 指定排序方向。 下面列出了有效的小写值:
    • descending
    • ascending
示例
sort=name/ascending
默认值:frequency/descending
filter
定义 String 类型精简条件中的量化在返回到客户端之前是如何进行筛选的。
语法
filter=<bins>/<freq>/<prefix>[<levels>]
这些属性执行以下操作:
  • <bins> - 指定返回的量化数量上限。
    对精简条件中的箱进行排序后,使用此属性截取所有排在尾部的箱。例如,如果箱=10,则根据指定的排序算法仅返回前 10 个箱。
  • <freq> - 限制返回的量化数量。
    使用此属性删除具有低频率计数的箱。 例如, freq=2 表示仅返回包含 2 个或多个数量的箱。
  • <prefix> - 指定仅返回名称以此前缀开头的量化。
    通配符"*"匹配所有名称。
    示例
    filter=30/2/*
  • <levels> - 指定分类级别。
    此属性可用于根据分类级别筛选分层精简条件输出。 此属性应为正整数 n默认值为 n=0。 如果 n>0,则仅返回包含小于 n 个 分类路径分隔符符号 (“/”) 的精简条件条目。 如果 n=0,不执行任何级别筛选。 级别分隔符是正斜线字符(“/”)。
    请注意使用大型分类导航器的性能成本。 将分类作为结果处理的一部分来执行。
注意:此参数应用特定于应用程序的结果端筛选。 这与 cutoff 参数不同。cutoff 是出于性能方面的原因,在每个索引分区中应用限制。
cutoff
限制必须针对深入字符串精简条件转移和处理的数据。 你可以仅将精简条件配置为返回相关度最高的值(箱)。
注意:此截止筛选在每个索引分区内执行。 这不同于仅执行结果端筛选的 filter 参数。 可以结合使用这两个参数。
语法
cutoff=<frequency>/<minbins>/<maxbins>
这些属性执行以下操作:
  • <maxbins> - 限制量化数量。
    此参数限制精简条件将返回的唯一值(箱)的数量。 这是在返回字符串精简条件和大量箱时,提高搜索性能的首选方式。 “0”表示已使用搜索架构中指定的默认值。
  • <frequency> - 按频率限制量化数量。
    如果结果集中精简条件值出现的次数小于或等于频率值,则不返回精简条件值。 “0”表示已使用搜索架构的默认值。
  • <minbins> - 指定频率截止的最小值。
    如果查询的唯一精简条件值的数量小于此值,则不执行任何频率截止,并且从该搜索分区中返回所有精简条件值。 如果已使用按频率截止,则此参数可用于指定将要返回的唯一精简条件值的最小数量,而不考虑出现的次数。 此属性的默认值为“0”,表示无论唯一导航器值是多少,都会执行频率截止。 “0”表示已使用索引架构中指定的默认值。
注意:请谨慎使用 <频率><minbins> 。 建议仅使用 <maxbins>

示例:添加精简条件

以下 CSOM 示例说明如何以编程方式请求三个精简条件: FileTypeWriteCompaniesWrite 代表项目最后修改日期,并使用高级语法返回固定大小日期/时间箱。

using (var context = new ClientContext("http://<serverName>/<siteCollectionPath>"))
{
    var query = new KeywordQuery(context)
    {
        QueryText = "home",
        Refiners = "FileType,Write(discretize=manual/2013-01-01/2013-08-22/2013-09-
            15/2013-09-21/2013-09-22),companies"
    };

    var executor = new SearchExecutor(context);
    var results = executor.ExecuteQuery(query);

    context.ExecuteQuery();

    ResultTable relevantResultsTable = results.Value[0];
    ResultTable refinerResultsTable = results.Value[1];
    Console.WriteLine(refinerResultsTable.RowCount + " refinement options:");
    foreach (var refinementOption in refinerResultsTable.ResultRows)
    {
        Console.WriteLine("RefinerName: '{0}' RefinementName: '{1}'
            RefinementValue: '{2}' RefinementToken: '{3}' RefinementCount: '{4}'",
            refinementOption["RefinerName"],
            refinementOption["RefinementName"],
            refinementOption["RefinementValue"],
            refinementOption["RefinementToken"],
            refinementOption["RefinementCount"]
        );
    }
}

了解搜索结果中的精简数据

如果已在查询中为托管属性启用查询优化,则查询结果包含细化数据(拆分为精简箱)。此数据位于 ResultTableCollection 内的 RefinementResults 表 ( RefinementResults ) 中。 一个优化箱表示托管属性的特定值或值范围。 RefinementResults 表每个精简箱包含一行,并包含表 2 中指定的列。

表 2:为细化箱返回的数据

参数 说明
RefinerName 查询精简条件的名称。
RefinementName 表示细化箱的字符串。 如果要在搜索结果页面上向用户呈现精简选项,则通常使用此字符串通。
RefinementValue 表示细化的特定于实现的格式化字符串。 为调试返回此数据,且客户端通常不需要此数据。
RefinementToken 表示在执行精简的查询时使用 RefinerName 的细化箱。
RefinementCount 此细化箱的结果计数。 此数据表示搜索结果中的项目(包括副本)数量,该值为针对此细化箱给定托管属性的值。

示例:精简数据

下面的表 3 包含两行精简数据。 第一行是索引项的精简数据,其中文件类型为 HTML。 第二行是索引项的精简数据,其中最后修改时间为 2013/09/21 到 2013/09/22。

表 3:精简数据的格式和内容

RefinerName RefinementName RefinementValue RefinementToken RefinementCount
FileType Html Html "????68746d6c" 50553
写入 从 2013-09-21T00:00:00Z 到 2013-09-22T00:00:00Z 从 2013-09-21T00:00:00Z 到 2013-09-22T00:00:00Z range(2013-09-21T00:00:00Z, 2013-09-22T00:00:00Z) 37

创建精简的查询

搜索结果表示字符串值或值范围形式的精简选项。 每个字符串值或数值范围称为细化箱,每个细化箱具有一个关联的 RefinementToken 值。 精简选项与 RefinerName 值提供的托管属性关联。

连接 RefinementTokenRefinerName 值以创建 refinement filter 字符串。 此字符串表示一个筛选器,该筛选器可用于将搜索结果项限制为仅包含这样的项目:项目中的托管属性具有细化箱中的值。 简言之:

refinement filter = <RefinerName>:<RefinementToken>

可以通过将精简筛选器添加到 KeywordQuery 类的 RefinementFilters 属性,为优化查询提供一个或多个精简筛选器。 通过多个精简筛选器,你可以提供对搜索结果的深入了解,并在多值属性中应用细化。 例如,你可以将查询优化到具有两个作者(每个作者由细化箱表示)的项目,排除仅包含其中一个作者的项目。

示例 1:创建 HTML 文件类型的精简查询

下面的 CSOM 示例展示了如何以编程方式执行精简,将搜索结果限制为 HTML 文件类型。 如示例:精简数据中所述,与此精简选项相关的精简数据将 RefinerName 设置为 Filetype将 RefinementToken 设置为“????68746d6c”。

using (var context = new ClientContext("http://<serverName>/<siteCollectionPath>"))
{
    var query = new KeywordQuery(context)
    {
        QueryText = "home"
    };

    query.RefinementFilters.Add("FileType:\\"????68746d6c\\"");
    var executor = new SearchExecutor(context);
    var results = executor.ExecuteQuery(query);

    context.ExecuteQuery();

    ResultTable relevantResultsTable = results.Value[0];
    var resultCount = 1;
    foreach (var relevantResult in relevantResultsTable.ResultRows)
    {
        Console.WriteLine("Relevant result number {0} has file type {1}.",
            resultCount, relevantResult["FileType"]);
            resultCount++;
    }
}

示例 2:使用以前获取的精简数据创建精简查询

以下 CSOM 示例显示如何使用精简条件规格运行查询,以创建后续用于执行细化的精简数据。 此示例模拟最终用户选择第一个精简选项的过程。

using (var context = new ClientContext("http://<serverName>/<siteCollectionPath>"))
{
    // Step 1: Run the query with refiner spec to provide refinement data in search result
    var query = new KeywordQuery(context)
    {
        QueryText = "home",
        Refiners = "FileType,Write(discretize=manual/2013-01-01/2013-08-22/2013-09-15/2013-09-21/2013-09-22),companies"
    };

    Console.WriteLine("Run query '{0}' with refiner spec '{1}'.", query.QueryText, query.Refiners);
    var executor = new SearchExecutor(context);
    var results = executor.ExecuteQuery(query);
    context.ExecuteQuery();

    // The query has been run and we can now look at the refinement data, to view the
    // refinement options
    ResultTable relevantResultsTable = results.Value[0];
    ResultTable refinerResultsTable = results.Value[1];
    Console.WriteLine("Got back {0} refinement options in the result:",
        refinerResultsTable.RowCount);

    foreach (var refinementOption in refinerResultsTable.ResultRows)
    {
        Console.WriteLine("RefinerName: '{0}' RefinementName: '{1}'
            RefinementValue: '{2}' RefinementToken: '{3}' RefinementCount: '{4}'",
            refinementOption["RefinerName"],
            refinementOption["RefinementName"],
            refinementOption["RefinementValue"],
            refinementOption["RefinementToken"],
            refinementOption["RefinementCount"]
        );
    }

    // Step 2: Run the refined query with refinement filter to drill down into
    // the search results. This example uses the first refinement option in the refinement
    // data, if available. This simulates an end user selecting this refinement option.
    var refinementOptionArray = refinerResultsTable.ResultRows.ToArray();

    if (refinementOptionArray.Length > 0)
    {
        var firstRefinementOption = refinementOptionArray[6];
        // Construct the refinement filter by concatenation
        var refinementFilter = firstRefinementOption["RefinerName"] + ":" +
            firstRefinementOption["RefinementToken"];
        var refinedQuery = new KeywordQuery(context)
        {
            QueryText = query.QueryText
        };
        refinedQuery.RefinementFilters.Add(refinementFilter);
        refinedQuery.SelectProperties.Add("FileType");
        refinedQuery.SelectProperties.Add("Write");
        refinedQuery.SelectProperties.Add("Companies");
        Console.WriteLine("Run query '{0}' with refinement filter '{1}'",
            refinedQuery.QueryText, refinementFilter);
        var refinedResults = executor.ExecuteQuery(refinedQuery);
        context.ExecuteQuery();
        ResultTable refinedRelevantResultsTable = refinedResults.Value[0];
        var resultCount = 1;
        foreach (var relevantResult in refinedRelevantResultsTable.ResultRows)
        {
            Console.WriteLine("Relevant result number {0} has FileType='{1}',
                Write='{2}', Companies='{3}'",
                resultCount,
                relevantResult["FileType"],
                relevantResult["Write"],
                relevantResult["Companies"]
            );
            resultCount++;
        }
    }
}

另请参阅

其他资源