无模式模式

无模式模式是一组 Solr 功能,当一起使用时,允许用户通过简单地索引示例数据来快速构建有效的模式,而无需手动编辑模式。

这些 Solr 功能都通过 solrconfig.xml 控制,它们是:

  1. 托管模式:模式修改在运行时通过 Solr API 进行,这需要使用支持这些更改的 schemaFactory。有关详细信息,请参阅模式工厂配置部分。

  2. 字段值类猜测:先前未见过的字段会通过级联的一组基于值的解析器运行,这些解析器会猜测字段值的 Java 类 - 目前提供布尔值、整数、长整数、浮点数、双精度数和日期的解析器。

  3. 基于字段值类的自动模式字段添加:先前未见过的字段会根据字段值 Java 类添加到模式中,这些类会映射到模式字段类型 - 请参阅字段类型定义和属性

使用无模式示例

在 Solr 发行版的 _default configset 中预配置了无模式模式的三个功能。要使用这些配置启动 Solr 的示例实例,请运行以下命令

bin/solr start -e schemaless

这将启动一个 Solr 服务器,并自动创建一个集合(名为 “gettingstarted”),该集合在初始模式中仅包含三个字段:id_version__text_

您可以使用 /schema/fields Schema API 来确认这一点:curl https://127.0.0.1:8983/solr/gettingstarted/schema/fields 将输出

{
  "responseHeader":{
    "status":0,
    "QTime":1},
  "fields":[{
      "name":"_text_",
      "type":"text_general",
      "multiValued":true,
      "indexed":true,
      "stored":false},
    {
      "name":"_version_",
      "type":"long",
      "indexed":true,
      "stored":true},
    {
      "name":"id",
      "type":"string",
      "multiValued":false,
      "indexed":true,
      "required":true,
      "stored":true,
      "uniqueKey":true}]}

配置无模式模式

如上所述,要以无模式模式使用 Solr,需要配置三个配置元素。在 Solr 附带的 _default configset 中,这些已配置完成。但是,如果您想在自己的配置上实现无模式模式,则应进行以下更改。

启用托管模式

正如模式工厂配置部分所述,除非您的配置指定应使用 ClassicIndexSchemaFactory,否则默认情况下会启用托管模式支持。

您可以通过添加一个如下所示的显式 <schemaFactory/> 来配置 ManagedIndexSchemaFactory(并控制使用的资源文件,或禁用未来的修改)。有关可用选项的更多详细信息,请参阅模式工厂配置

<schemaFactory class="ManagedIndexSchemaFactory">
  <bool name="mutable">true</bool>
  <str name="managedSchemaResourceName">managed-schema.xml</str>
</schemaFactory>

启用字段类猜测

在 Solr 中,UpdateRequestProcessorChain 定义了一个插件链,这些插件会在文档被索引之前或索引期间应用于文档。

Solr 无模式模式的字段猜测方面使用一个特别定义的 UpdateRequestProcessorChain,允许 Solr 猜测字段类型。您还可以定义要使用的默认字段类型类。

首先,您应该按如下方式定义它(有关更新处理器工厂的文档,请参见下面的 javadoc 链接)

  <updateProcessor class="solr.UUIDUpdateProcessorFactory" name="uuid"/>
  <updateProcessor class="solr.RemoveBlankFieldUpdateProcessorFactory" name="remove-blank"/>
  <updateProcessor class="solr.FieldNameMutatingUpdateProcessorFactory" name="field-name-mutating"> (1)
    <str name="pattern">[^\w-\.]</str>
    <str name="replacement">_</str>
  </updateProcessor>
  <updateProcessor class="solr.ParseBooleanFieldUpdateProcessorFactory" name="parse-boolean"/> (2)
  <updateProcessor class="solr.ParseLongFieldUpdateProcessorFactory" name="parse-long"/>
  <updateProcessor class="solr.ParseDoubleFieldUpdateProcessorFactory" name="parse-double"/>
  <updateProcessor class="solr.ParseDateFieldUpdateProcessorFactory" name="parse-date">
    <arr name="format">
      <str>yyyy-MM-dd['T'[HH:mm[:ss[.SSS]]]z</str>
      <str>yyyy-MM-dd['T'[HH:mm[:ss[,SSS]]]z</str>
      <str>yyyy-MM-dd HH:mm[:ss[.SSS]]]z</str>
      <str>yyyy-MM-dd HH:mm[:ss[,SSS]]]z</str>
      <str>[EEE, ]dd MMM yyyy HH:mm[:ss] z</str>
      <str>EEEE, dd-MMM-yy HH:mm:ss z</str>
      <str>EEE MMM ppd HH:mm:ss [z ]yyyy</str>
    </arr>
  </updateProcessor>
  <updateProcessor class="solr.AddSchemaFieldsUpdateProcessorFactory" name="add-schema-fields"> (3)
    <lst name="typeMapping">
      <str name="valueClass">java.lang.String</str> (4)
      <str name="fieldType">text_general</str>
      <lst name="copyField"> (5)
        <str name="dest">*_str</str>
        <int name="maxChars">256</int>
      </lst>
      <!-- Use as default mapping instead of defaultFieldType -->
      <bool name="default">true</bool>
    </lst>
    <lst name="typeMapping">
      <str name="valueClass">java.lang.Boolean</str>
      <str name="fieldType">booleans</str>
    </lst>
    <lst name="typeMapping">
      <str name="valueClass">java.util.Date</str>
      <str name="fieldType">pdates</str>
    </lst>
    <lst name="typeMapping">
      <str name="valueClass">java.lang.Long</str> (6)
      <str name="valueClass">java.lang.Integer</str>
      <str name="fieldType">plongs</str>
    </lst>
    <lst name="typeMapping">
      <str name="valueClass">java.lang.Number</str>
      <str name="fieldType">pdoubles</str>
    </lst>
  </updateProcessor>

  <!-- The update.autoCreateFields property can be turned to false to disable schemaless mode -->
  <updateRequestProcessorChain name="add-unknown-fields-to-the-schema" default="${update.autoCreateFields:true}"
           processor="uuid,remove-blank,field-name-mutating,parse-boolean,parse-long,parse-double,parse-date,add-schema-fields"> (7)
    <processor class="solr.LogUpdateProcessorFactory"/>
    <processor class="solr.DistributedUpdateProcessorFactory"/>
    <processor class="solr.RunUpdateProcessorFactory"/>
  </updateRequestProcessorChain>

此链中定义了很多内容。让我们逐步了解其中的一些内容。

1 首先,我们使用 FieldNameMutatingUpdateProcessorFactory 将所有字段名称转换为小写。请注意,此元素和以下每个 <processor> 元素都包含一个 name。这些名称将在此示例末尾的最终链定义中使用。
2 接下来,我们添加几个更新请求处理器来解析不同的字段类型。请注意,ParseDateFieldUpdateProcessorFactory 包括一个长长的日期格式列表,这些格式将被解析为有效的 Solr 日期。如果您有自定义日期,您可以将其添加到此列表(请参阅下面的 Javadocs 链接,以获取有关如何操作的信息)。
3 解析字段后,我们定义将分配给这些字段的字段类型。您可以修改您想要更改的任何类型。
4 在此定义中,如果解析步骤确定字段中的传入数据是字符串,我们将将其放入 Solr 中,字段类型为 text_general。默认情况下,此字段类型允许 Solr 查询此字段。
5 在我们添加 text_general 字段后,我们还定义了一个复制字段规则,该规则会将所有数据从新的 text_general 字段复制到名称相同但后缀为 _str 的字段。这是通过 Solr 的动态字段功能完成的。通过以这种方式将复制字段规则的目标定义为动态字段,您可以控制模式中使用的字段类型。默认选择允许 Solr 对这些字段进行分面、高亮和排序。
6 这是映射规则的另一个示例。在这种情况下,我们定义当 LongInteger 字段解析器之一识别字段时,它们都应将其字段映射到 plongs 字段类型。
7 最后,我们添加一个链定义,该定义调用插件列表。这些插件都通过我们在定义它们时给它们的名称来调用。我们还可以向链中添加其他处理器,如此处所示。请注意,我们还为整个链赋予了一个 name(“add-unknown-fields-to-the-schema”)。我们将在下一节中使用此名称来指定我们的更新请求处理程序应使用此链定义。
此链定义将为字符串字段创建许多从相应的文本字段创建的复制字段规则。如果您的数据导致最终产生大量复制字段规则,则索引速度可能会明显减慢,并且索引大小会更大。为了控制这些问题,建议您查看创建的复制字段规则,并删除您不需要用于分面、排序、高亮等操作的任何规则。

如果您对本链中使用的类有更多兴趣,这里提供了上面提到的更新处理器工厂的 Javadocs 链接

设置默认的 UpdateRequestProcessorChain

一旦定义了 UpdateRequestProcessorChain,您必须指示您的 UpdateRequestHandler 在处理索引更新(即添加、删除、替换文档)时使用它。

有两种方法可以做到这一点。上面显示的更新链具有一个 default=true 属性,该属性将使其用于任何更新处理程序。

另一种更明确的方法是使用 InitParams 在所有 /update 请求处理程序上设置默认值

<initParams path="/update/**">
  <lst name="defaults">
    <str name="update.chain">add-unknown-fields-to-the-schema</str>
  </lst>
</initParams>
完成所有这些更改后,应重新启动 Solr 或重新加载核心。

禁用自动字段猜测

可以使用 update.autoCreateFields 属性禁用自动字段创建。为此,您可以使用 bin/solr config 以及如下命令

bin/solr config -c mycollection -p 8983 --action set-user-property --property update.autoCreateFields --value false

索引文档的示例

一旦启用了无模式模式(无论您是手动配置的还是使用 _default configset),包含模式中未定义的字段的文档将被索引,并使用自动添加到模式的猜测字段类型。

例如,添加 CSV 文档将导致添加未知字段,其 fieldType 基于值

curl "https://127.0.0.1:8983/solr/gettingstarted/update?commit=true&wt=xml" -H "Content-type:application/csv" -d '
id,Artist,Album,Released,Rating,FromDistributor,Sold
44C,Old Shews,Mead for Walking,1988-08-13,0.01,14,0'

输出指示成功

<response>
  <lst name="responseHeader"><int name="status">0</int><int name="QTime">106</int></lst>
</response>

模式中现在的字段(来自 curl https://127.0.0.1:8983/solr/gettingstarted/schema/fields 的输出)

{
  "responseHeader":{
    "status":0,
    "QTime":2},
  "fields":[{
      "name":"Album",
      "type":"text_general"},
    {
      "name":"Artist",
      "type":"text_general"},
    {
      "name":"FromDistributor",
      "type":"plongs"},
    {
      "name":"Rating",
      "type":"pdoubles"},
    {
      "name":"Released",
      "type":"pdates"},
    {
      "name":"Sold",
      "type":"plongs"},
    {
      "name":"_root_", ...},
    {
      "name":"_text_", ...},
    {
      "name":"_version_", ...},
    {
      "name":"id", ...}
]}

此外,文本字段的字符串版本也通过 copyFields 索引到 *_str 动态字段:(来自 curl https://127.0.0.1:8983/solr/gettingstarted/schema/copyfields 的输出)

{
  "responseHeader":{
    "status":0,
    "QTime":0},
  "copyFields":[{
      "source":"Artist",
      "dest":"Artist_str",
      "maxChars":256},
    {
      "source":"Album",
      "dest":"Album_str",
      "maxChars":256}]}
您仍然可以明确

即使您希望对大多数字段使用无模式模式,您仍然可以使用模式 API 在索引使用它们的文档之前,预先创建一些具有显式类型的字段。

在内部,模式 API 和无模式更新处理器都使用相同的托管模式功能。

此外,如果您不需要文本字段的 *_str 版本,您可以简单地从自动生成的模式中删除 copyField 定义,由于原始字段现在已定义,它将不会被重新添加。

一旦将字段添加到模式中,其字段类型就会被固定。因此,添加具有与先前猜测的字段类型冲突的字段值的文档将失败。例如,在添加上述文档后,“Sold”字段的 fieldType 为 plongs,但下面的文档在此字段中包含一个非整数的小数值

curl "https://127.0.0.1:8983/solr/gettingstarted/update?commit=true&wt=xml" -H "Content-type:application/csv" -d '
id,Description,Sold
19F,Cassettes by the pound,4.93'

此文档将失败,如此输出所示

<response>
  <lst name="responseHeader">
    <int name="status">400</int>
    <int name="QTime">7</int>
  </lst>
  <lst name="error">
    <str name="msg">ERROR: [doc=19F] Error adding field 'Sold'='4.93' msg=For input string: "4.93"</str>
    <int name="code">400</int>
  </lst>
</response>