分析组件

分析组件允许用户计算结果集上的复杂统计聚合。

分析组件已弃用。我们建议研究 JSON 分面 API 中提供的类似功能。如果存在 JSON 分面当前未涵盖的您需要的功能,请通知项目。

该组件支持以多种方式与数据交互,既可以通过各种分析函数,也可以通过强大的分面功能。分析组件中支持标准分面,并添加了利用其分析功能的功能。

模块

这是通过 analytics Solr 模块提供的,该模块需要在使用前启用。

分析配置

由于分析框架是一个*搜索组件*,因此必须将其声明为搜索组件并添加到搜索处理程序中。

对于云集合上的分布式分析请求,该组件严格使用 AnalyticsHandler 进行分片间通信。用户不应使用 Analytics Handler 提交分析请求。

接下来,您需要注册请求处理程序和搜索组件。将以下行添加到 solrconfig.xml 中,靠近其他请求处理程序的定义。

solrconfig.xml
<!-- To handle user requests -->
<searchComponent name="analytics" class="org.apache.solr.handler.component.AnalyticsComponent" />

<requestHandler name="/select" class="solr.SearchHandler">
    <arr name="last-components">
        <str>analytics</str>
    </arr>
</requestHandler>

<!-- For inter-shard communication during distributed requests -->
<requestHandler name="/analytics" class="org.apache.solr.handler.AnalyticsHandler" />

要使这些更改生效,请重新启动 Solr 或重新加载核心或集合。

请求语法

分析请求通过参数 analytics 传递给 Solr,该参数是在发送到 搜索处理程序 的请求中。由于分析请求是在搜索处理程序请求内部发送的,因此它将根据搜索处理程序确定的结果集计算结果。

例如,此 curl 命令编码并将简单的分析请求 POST 到搜索处理程序

$ curl --data-binary 'analytics={
   "expressions" : {
      "revenue" : "sum(mult(price,quantity))"
      }
   }'
   https://127.0.0.1:8983/solr/sales/select?q=*:*&wt=json&rows=0

任何分析请求都有 3 个主要部分

表达式

在整个结果集上执行的计算列表。表达式将搜索结果聚合为单个返回值。此列表完全独立于每个分组中定义的表达式。在表达式部分中了解有关它们的更多信息。

函数

一个或多个 变量函数,用于在请求的其余部分中使用。这些本质上是 lambda 函数,并且可以以多种方式组合。这些函数用于 expressions 以及 groupings 中定义的表达式。

分组

要计算的 分组列表,除了表达式之外。分组包含一组分面以及一个在这些分面上计算的表达式列表。分组中定义的表达式仅在在该分组中定义的分面上计算。

可选参数
expressionsgroupings 参数必须出现在请求中,否则将不会有分析要计算。functions 参数始终是可选的。
示例分析请求
{
    "functions": {
        "sale()": "mult(price,quantity)"
    },
    "expressions" : {
        "max_sale" : "max(sale())",
        "med_sale" : "median(sale())"
    },
    "groupings" : {
        "sales" : {
            "expressions" : {
                "stddev_sale" : "stddev(sale())",
                "min_price" : "min(price)",
                "max_quantity" : "max(quantity)"
            },
            "facets" : {
                "category" : {
                    "type" : "value",
                    "expression" : "fill_missing(category, 'No Category')",
                    "sort" : {
                        "criteria" : [
                            {
                                "type" : "expression",
                                "expression" : "min_price",
                                "direction" : "ascending"
                            },
                            {
                                "type" : "facetvalue",
                                "direction" : "descending"
                            }
                        ],
                        "limit" : 10
                    }
                },
                "temps" : {
                    "type" : "query",
                    "queries" : {
                        "hot" : "temp:[90 TO *]",
                        "cold" : "temp:[* TO 50]"
                    }
                }
            }
        }
    }
}

表达式

表达式是从分析组件请求信息的方式。这些是您希望计算并在响应中返回的统计表达式。

构造表达式

表达式组件

表达式使用字段、常量、映射函数和缩减函数构建。下面介绍了定义这些方法的方式。

  • 常量:在表达式中定义的值。支持的常量类型在常量部分中介绍。

  • 字段:从索引读取的 Solr 字段。支持的字段在支持的字段类型部分中列出。

映射函数

映射函数为每个 Solr 文档或归约映射值。提供的映射函数在分析映射函数中详细说明。

  • 未归约映射:将一个字段映射到另一个字段或常量会为每个 Solr 文档返回一个值。未归约映射函数可以接受字段、常量以及其他未归约映射函数作为输入。

  • 已归约映射:将一个归约函数映射到另一个归约函数或常量会返回单个值。

归约函数

将每个 Solr 文档的源和/或未归约映射函数的值归约为单个值的函数。提供的归约函数在分析归约函数中详细说明。

组件排序

必须按照以下顺序使用表达式组件才能创建有效的表达式。

  1. 已归约映射函数

    1. 常量

    2. 归约函数

      1. 未归约映射函数

        1. 未归约映射函数

    3. 已归约映射函数

  2. 归约函数

此排序基于以下规则

  • 没有归约函数可以作为另一个归约函数的参数。由于所有归约都在一个步骤中完成,因此一个归约函数不能依赖于另一个归约函数的结果。

  • 不能留下未归约的字段,因为分析组件无法为表达式返回值的列表(每个文档一个值)。每个表达式都必须归约为单个值。

  • 创建函数时,映射函数不是必需的,但是可以根据需要使用任意多个嵌套映射。

  • 嵌套映射函数必须是相同的类型,因此要么都必须是未归约的,要么都必须是已归约的。已归约映射函数不能将未归约映射函数作为参数,反之亦然。

示例构造

根据以上定义和排序,可以将示例表达式分解为其组成部分

div(sum(a,fill_missing(b,0)),add(10.5,count(mult(a,c)))))

总的来说,这是一个已归约的映射函数。div 函数是一个已归约的映射函数,因为它是一个提供的映射函数,并且具有已归约的参数。

如果我们进一步分解表达式

  • sum(a,fill_missing(b,0)):归约函数
    sum 是一个提供的归约函数

    • a:字段

    • fill_missing(b,0):未归约映射函数
      fill_missing 是一个未归约的映射函数,因为它是一个提供的映射函数,并且具有字段参数。

      • b:字段

      • 0:常量

  • add(10.5,count(mult(a,c))):已归约映射函数
    add 是一个已归约的映射函数,因为它是一个提供的映射函数,并且具有归约函数参数。

    • 10.5:常量

    • count(mult(a,c)):归约函数
      count 是一个提供的归约函数

      • mult(a,c):未归约映射函数
        mult 是一个未归约的映射函数,因为它是一个提供的映射函数,并且具有两个字段参数。

        • a:字段

        • c:字段

表达式基数(多值和单值)

所有多值表达式的根是多值字段。单值表达式可以从常量或单值字段开始。所有单值表达式都可以被视为包含一个值的多值表达式。

单值表达式和多值表达式可以一起在许多映射函数中使用,多值表达式也可以单独使用,并且许多单值表达式也可以一起使用。例如

add(<单值双精度数>, <单值双精度数>, …​)

返回一个单值双精度数表达式,其中每个表达式的值被相加。

add(<单值双精度数>, <多值双精度数>)

返回一个多值双精度数表达式,其中第二个表达式的每个值都被添加到第一个表达式的单个值。

add(<多值双精度数>, <单值双精度数>)

与上述函数的作用相同。

add(<多值双精度数>)

返回一个单值双精度数表达式,它是参数表达式的多个值的总和。

类型和隐式转换

新的分析组件当前支持下表中列出的类型。这些类型为以下关系启用单向隐式转换

类型 隐式转换为

布尔值

字符串

日期

长整型,字符串

整型

长整型,浮点型,双精度型,字符串

长整型

双精度型,字符串

浮点型

双精度型,字符串

双精度型

字符串

字符串

隐式转换意味着如果一个函数需要特定类型的值作为参数,那么如果可能的话,参数将自动转换为该类型。

例如,concat() 只接受字符串参数,由于所有类型都可以隐式转换为字符串,因此任何类型都可以作为参数接受。

动态类型函数也是如此。fill_missing() 需要两个相同类型的参数。但是,也可以使用隐式转换为相同类型的两种类型。

例如,fill_missing(<long>,<float>) 将转换为 fill_missing(<double>,<double>),因为长整型不能隐式转换为浮点型,而浮点型也不能隐式转换为长整型。

隐式转换是有顺序的,其中更专门的类型排在更通用的类型之前。因此,即使长整型和浮点型都可以隐式转换为双精度型和字符串,它们也会转换为双精度型。这是因为双精度型是一种比字符串更专门的类型,所有类型都可以转换为字符串。

排序与它们在上表中的顺序相同。

基数也可以隐式转换。单值表达式始终可以隐式转换为多值表达式,因为所有单值表达式都是包含一个值的多值表达式。

只有在不进行隐式转换表达式将无法“编译”时才会发生隐式转换。如果一个表达式最初遵循所有类型规则,则不会发生隐式转换。某些函数(如 string()date()round()floor()ceil())充当显式转换,声明所需的类型。但是,round()floor()cell() 可以根据参数类型返回 int 或 long。

变量函数

变量函数是一种缩短表达式并简化编写分析查询的方式。它们本质上是在请求中定义的 lambda 函数。

示例基本函数
{
    "functions" : {
        "sale()" : "mult(price,quantity)"
    },
    "expressions" : {
        "max_sale" : "max(sale())",
        "med_sale" : "median(sale())"
    }
}

在上面的请求中,我们定义了一个 sale() 函数来抽象这个概念,而不是两次编写 mult(price,quantity)。然后该函数被用于多个表达式中。

假设我们要查看特定类别的销售额

{
    "functions" : {
        "clothing_sale()" : "filter(mult(price,quantity),equal(category,'Clothing'))",
        "kitchen_sale()" : "filter(mult(price,quantity),equal(category,\"Kitchen\"))"
    },
    "expressions" : {
        "max_clothing_sale" : "max(clothing_sale())"
      , "med_clothing_sale" : "median(clothing_sale())"
      , "max_kitchen_sale" : "max(kitchen_sale())"
      , "med_kitchen_sale" : "median(kitchen_sale())"
    }
}

参数

与其为每个类别创建一个函数,不如将 category 作为 sale() 函数的输入要容易得多。此功能的一个示例如下所示

带参数的示例函数
{
    "functions" : {
        "sale(cat)" : "filter(mult(price,quantity),equal(category,cat))"
    },
    "expressions" : {
        "max_clothing_sale" : "max(sale(\"Clothing\"))"
      , "med_clothing_sale" : "median(sale('Clothing'))"
      , "max_kitchen_sale" : "max(sale(\"Kitchen\"))"
      , "med_kitchen_sale" : "median(sale('Kitchen'))"
    }
}

变量函数可以接受任意数量的参数,并在函数表达式中使用它们,就像它们是字段或常量一样。

可变长度参数

有些分析函数接受可变数量的参数。因此,在某些情况下,变量函数需要接受可变数量的参数。

例如,产品的价格可能存在多个(但未确定的)组成部分。如果最后一个参数后跟 ..,则函数可以接受可变长度的参数

带有可变长度参数的示例函数
{
    "functions" : {
        "sale(cat, costs..)" : "filter(mult(add(costs),quantity),equal(category,cat))"
    },
    "expressions" : {
        "max_clothing_sale" : "max(sale('Clothing', material, tariff, tax))"
      , "med_clothing_sale" : "median(sale('Clothing', material, tariff, tax))"
      , "max_kitchen_sale" : "max(sale('Kitchen', material, construction))"
      , "med_kitchen_sale" : "median(sale('Kitchen', material, construction))"
    }
}

在上面的示例中,使用了可变长度参数来封装产品的所有成本。没有为可变长度参数请求明确数量的参数,因此服装表达式可以使用 3 个,而厨房表达式可以使用 2 个。当调用 sale() 函数时,costs 将展开为给定的参数。

因此,在上面的请求中,在 sale 函数内部

  • add(costs)

展开为以下两者

  • add(material, tariff, tax)

  • add(material, construction)

For-Each 函数

高级功能

以下函数详细信息适用于高级请求。

尽管上述功能允许将未定义的参数数量传递给函数,但它不允许与这些参数进行交互。

很多时候,我们可能希望将每个参数包装在其他函数中。例如,我们可能希望能够同时查看多个类别。所以我们想看看 category EQUALS x **OR** category EQUALS y,等等。

为了做到这一点,我们需要使用 for-each lambda 函数,它转换可变长度参数的每个值。for-each 以可变长度参数之后的 : 字符开始。

带有 For-Each 的示例函数
{
    "functions" : {
        "sale(cats..)" : "filter(mult(price,quantity),or(cats:equal(category,_)))"
    },
    "expressions" : {
        "max_sale_1" : "max(sale('Clothing', 'Kitchen'))"
      , "med_sale_1" : "median(sale('Clothing', 'Kitchen'))"
      , "max_sale_2" : "max(sale('Electronics', 'Entertainment', 'Travel'))"
      , "med_sale_2" : "median(sale('Electronics', 'Entertainment', 'Travel'))"
    }
}

在此示例中,cats: 是一个语法,它在每个参数 cats 上启动一个 for-each lambda 函数,并且在 for-each 的每次迭代中,使用 _ 字符来引用 cats 的值。当调用 sale("Clothing", "Kitchen") 时,lambda 函数 equal(category,_) 将应用于 or() 函数内的 Clothing 和 Kitchen。

使用所有这些规则,表达式

`sale("Clothing","Kitchen")`

由表达式解析器展开为

`filter(mult(price,quantity),or(equal(category,"Kitchen"),equal(category,"Clothing")))`

分组和分面

与 Solr 的其他部分非常相似,分面允许通过正在计算表达式的数据属性来分解和分组分析结果。

分析组件中当前可用的分面是值分面、透视分面、范围分面和查询分面。每个分面都必须在其定义的组中具有唯一的名称,并且没有分面可以在组之外定义。

分组允许用户在一组分面上计算相同的表达式分组。分组必须同时给出 expressionsfacets

示例基本分面请求
{
    "functions" : {
        "sale()" : "mult(price,quantity)"
    },
    "groupings" : {
        "sales_numbers" : {
            "expressions" : {
                "max_sale" : "max(sale())",
                "med_sale" : "median(sale())"
            },
            "facets" : {
                "<name>" : "< facet request >"
            }
        }
    }
}
示例基本分面响应
{
    "analytics_response" : {
        "groupings" : {
            "sales_numbers" : {
                "<name>" : "< facet response >"
            }
        }
    }
}

分面排序

某些分析分面允许对其结果进行复杂的排序。当前可排序的两个分面是分析值分面分析透视分面

参数

标准

必需

默认值:无

用于排序分面的标准列表。

它采用以下参数

类型

必需

默认值:无

排序的类型。有两种可能的值:* expression:按在同一组中定义的表达式的值排序。* facetvalue:按分面值的字符串表示形式排序。

方向

可选

默认值:ascending

排序的方向。选项为 ascendingdescending

表达式

可选

默认值:无

typeexpression 时,是在同一组中定义的表达式的名称。

限制

可选

默认值:-1

将返回的分面值数量限制为前 N 个。默认值表示没有限制。

偏移量

可选

默认值:0

设置限制时,跳过前 N 个分面值。

示例排序请求
{
    "criteria" : [
        {
            "type" : "expression",
            "expression" : "max_sale",
            "direction" : "ascending"
        },
        {
            "type" : "facetvalue",
            "direction" : "descending"
        }
    ],
    "limit" : 10,
    "offset" : 5
}

值分面

值分面用于根据应用于每个文档的映射表达式的值对文档进行分组。映射表达式是不包含归约函数的表达式。

有关更多信息,请参阅表达式部分。例如:

  • mult(quantity, sum(price, tax)):按产生的收入细分文档。

  • fillmissing(state, "N/A"):按州细分文档,如果文档不包含州信息,则使用 N/A。

值分面可以排序。

参数

表达式

必需

默认值:无

为每个文档选择分面桶的表达式。

排序

可选

默认值:无

透视结果的排序

值分面请求示例
{
    "type" : "value",
    "expression" : "fillmissing(category,'No Category')",
    "sort" : {}
}
值分面响应示例
[
    { "..." : "..." },
    {
        "value" : "Electronics",
        "results" : {
            "max_sale" : 103.75,
            "med_sale" : 15.5
        }
    },
    {
        "value" : "Kitchen",
        "results" : {
            "max_sale" : 88.25,
            "med_sale" : 11.37
        }
    },
    { "..." : "..." }
]
这是对原始分析组件中存在的字段分面的替代。通过使用字段名称作为表达式,可以在值分面中保持字段分面功能。

分析透视分面

透视分面用于根据应用于每个文档的多个映射表达式的值对文档进行分组。

透视分面的工作方式与值分面的层级非常相似。需要一个透视列表,并且列表的顺序直接影响返回的结果。给定的第一个透视将像普通的值分面一样处理。给定的第二个透视将像第一个透视的每个值的一个值分面一样处理。这些第二级值分面中的每一个都将限制为其第一级分面桶中的文档。对于提供的任意多个透视,此过程都将继续。

排序在每个透视的基础上启用。这意味着如果您的顶部透视具有 limit:1 的排序,则只会深入到该分面的第一个值。每个透视中的排序都独立于其他透视。

参数

pivots

必需

默认值:无

用于计算向下钻取分面的透视列表。该列表按从最顶部到最底部的级别排序。

名称

必需

默认值:无

透视的名称。

表达式

必需

默认值:无

为每个文档选择分面桶的表达式。

排序

可选

默认值:无

透视结果的排序

透视分面请求示例
{
    "type" : "pivot",
    "pivots" : [
        {
            "name" : "country",
            "expression" : "country",
            "sort" : {}
        },
        {
            "name" : "state",
            "expression" : "fillmissing(state, fillmissing(providence, territory))"
        },
        {
            "name" : "city",
            "expression" : "fillmissing(city, 'N/A')",
            "sort" : {}
        }
    ]
}
透视分面响应示例
[
    { "..." : "..." },
    {
        "pivot" : "Country",
        "value" : "USA",
        "results" : {
            "max_sale" : 103.75,
            "med_sale" : 15.5
        },
        "children" : [
            { "..." : "..." },
            {
                "pivot" : "State",
                "value" : "Texas",
                "results" : {
                    "max_sale" : 99.2,
                    "med_sale" : 20.35
                },
                "children" : [
                    { "..." : "..." },
                    {
                        "pivot" : "City",
                        "value" : "Austin",
                        "results" : {
                            "max_sale" : 94.34,
                            "med_sale" : 17.60
                        }
                    },
                    { "..." : "..." }
                ]
            },
            { "..." : "..." }
        ]
    },
    { "..." : "..." }
]

分析范围分面

范围分面用于根据字段值将文档分组到给定的范围集中。分析范围分面的输入与 Solr 范围分面使用的输入相同。有关使用的更多信息,请参阅范围分面部分。

参数

字段

必需

默认值:无

要进行分面的字段。

开始

必需

默认值:无

范围的下限。

结束

必需

默认值:无

范围的上限。

间隔

必需

默认值:无

用于生成分面桶的范围间隔列表。如果桶的总和不能完全覆盖从 startend 的范围,则最后一个 gap 值将重复多次,以填充任何未使用的范围。

hardend

可选

默认值:false

是否在溢出时将最后一个分面桶范围截断为 end 值。

包括

可选

默认值:lower

要在分面桶中包含的边界。* lower - 所有基于间隔的范围都包含其下限。* upper - 所有基于间隔的范围都包含其上限。* edge - 第一个和最后一个间隔范围包含其边缘边界(第一个的下限,最后一个的上限),即使未指定相应的上/下选项。* outer - beforeafter 范围将包含其边界,即使第一个或最后一个范围已经包含这些边界。* all - 包括所有选项:lowerupperedgeouter

其他

可选

默认值:none

要包含在分面中的其他范围。* before - 所有字段值低于第一个范围下限的记录。* after - 所有字段值大于最后一个范围上限的记录。* between - 所有字段值在第一个范围下限和最后一个范围上限之间的记录。* none - 不包含上述任何一个的分面桶。* all - 包括 beforeafterbetween 的分面桶。

范围分面请求示例
{
    "type" : "range",
    "field" : "price",
    "start" : "0",
    "end" : "100",
    "gap" : [
        "5",
        "10",
        "10",
        "25"
    ],
    "hardend" : true,
    "include" : [
        "lower",
        "upper"
    ],
    "others" : [
        "after",
        "between"
    ]
}
范围分面响应示例
[
    {
        "value" : "[0 TO 5]",
        "results" : {
            "max_sale" : 4.75,
            "med_sale" : 3.45
        }
    },
    {
        "value" : "[5 TO 15]",
        "results" : {
            "max_sale" : 13.25,
            "med_sale" : 10.20
        }
    },
    {
        "value" : "[15 TO 25]",
        "results" : {
            "max_sale" : 22.75,
            "med_sale" : 18.50
        }
    },
    {
        "value" : "[25 TO 50]",
        "results" : {
            "max_sale" : 47.55,
            "med_sale" : 30.33
        }
    },
    {
        "value" : "[50 TO 75]",
        "results" : {
            "max_sale" : 70.25,
            "med_sale" : 64.54
        }
    },
    { "..." : "..." }
]

查询分面

查询分面用于根据给定的一组查询对文档进行分组。

参数

查询

必需

默认值:无

要按其分面的查询列表。

查询分面请求示例
{
    "type" : "query",
    "queries" : {
        "high_quantity" : "quantity:[ 5 TO 14 ] AND price:[ 100 TO * ]",
        "low_quantity" : "quantity:[ 1 TO 4 ] AND price:[ 100 TO * ]"
    }
}
查询分面响应示例
[
    {
        "value" : "high_quantity",
        "results" : {
            "max_sale" : 4.75,
            "med_sale" : 3.45
        }
    },
    {
        "value" : "low_quantity",
        "results" : {
            "max_sale" : 13.25,
            "med_sale" : 10.20
        }
    }
]