当前位置: 首页 > article >正文

Elasticsearch7.X建模各属性文档

1.分析器(analyzer)

只有数据类型为 text 的文档才可以配置这个参数,如果未配置 search_analyzer 参数,索引和搜索分析时都会使用 analyzer 配置的分词器,不能使用更新映射API在已有字段上更新分析器配置。

创建索引并配置分析器和映射

PUT my-index-000001
{
   "settings": {
      "analysis": {
         "analyzer": {
            "my_analyzer": { 
               "type": "custom",
               "tokenizer": "standard",
               "filter": ["lowercase"]
            },
            "my_stop_analyzer": { 
               "type": "custom",
               "tokenizer": "standard",
               "filter": ["lowercase", "english_stop"]
            }
         },
         "filter": {
            "english_stop": {
               "type": "stop",
               "stopwords": "_english_"
            }
         }
      }
   },
   "mappings": {
       "properties": {
          "title": {
             "type": "text",
             "analyzer": "my_analyzer", 
             "search_analyzer": "my_stop_analyzer", 
             "search_quote_analyzer": "my_analyzer" 
         }
      }
   }
}

插入文档

PUT my-index-000001/_doc/1
{
   "title": "The Quick Brown Fox"
}

PUT my-index-000001/_doc/2
{
   "title": "A Quick Brown Fox"
}

在Elasticsearch中,analyzersearch_analyzersearch_quote_analyzer都是用来指定如何处理文本数据的分析器,但它们有着不同的用途:

  1. analyzer

    • Elasticsearch添加新文档时,这个分析器会决定文本字段中的内容如何被转换成倒排索引中的词条。
    • 在这个例子中,title字段在索引时会使用my_analyzer分析器,将文本转换为小写,并通过标准分词器将文本转换成倒排索引中的词条。
  2. search_analyzer

    • 执行搜索查询时,Elasticsearch会使用search_analyzer来处理查询字符串,ES允许在索引和搜索时使用不同的分析器。
    • 在这个例子中,title字段在搜索时会使用my_stop_analyzer,这意味着查询字符串会被转换为小写,并且英语停用词会被移除。
  3. search_quote_analyzer

    • 这个分析器特别用于处理带引号的查询,确保短语内的词汇不会被停用词过滤器等改变。如果未指定,则默认使用search_analyzer

    • 主要用于 query_stringsimple_query_string 查询中的短语查询。

      GET my-index-000001/_search
      {
         "query": {
            "query_string": {
               "query": "\"the quick brown fox\"" 
            }
         }
      }
      
      // search_quote_analyzer生效,结果是:
      
        "hits" : {
          "total" : {
            "value" : 1,
            "relation" : "eq"
          },
          "max_score" : 1.2401117,
          "hits" : [
            {
              "_index" : "my-index-000001",
              "_type" : "_doc",
              "_id" : "1",
              "_score" : 1.2401117,
              "_source" : {
                "title" : "The Quick Brown Fox"
              }
            }
          ]
        }
      
      
      GET /my-index-000001/_search
      {
        "query": {
          "match": {
            "title": "the quick brown fox"
          }
        }
      }
      
      // search_quote_analyzer未生效,search_analyzer生效,结果是:
      
      
        "hits" : {
          "total" : {
            "value" : 2,
            "relation" : "eq"
          },
          "max_score" : 0.5469647,
          "hits" : [
            {
              "_index" : "my-index-000001",
              "_type" : "_doc",
              "_id" : "1",
              "_score" : 0.5469647,
              "_source" : {
                "title" : "The Quick Brown Fox"
              }
            },
            {
              "_index" : "my-index-000001",
              "_type" : "_doc",
              "_id" : "2",
              "_score" : 0.5469647,
              "_source" : {
                "title" : "A Quick Brown Fox"
              }
            }
          ]
        }
      
      

2.提升权重(boost)

单个字段可以在查询时自动提升-对相关性评分进行更多计数,使用boost参数如下(不推荐):

PUT my-index-000002
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "boost": 2 
      },
      "content": {
        "type": "text"
      }
    }
  }
}

title字段的匹配权重将是content字段的两倍,content字段的权重默认提升为1.0。

可以通过在查询中直接使用boost参数来实现相同的效果,例如以下查询(带字段时间boost):

GET /my-index-000002/_search
{
  "query": {
    "match": {
      "title": {
        "query": "quick brown fox"
      }
    }
  }
}

等同于(推荐):

GET /my-index-000002/_search
{
  "query": {
    "match": {
      "title": {
        "query": "quick brown fox",
        "boost": 2
      }
    }
  }
}

从 Elasticsearch 5.0.0 开始,boost 参数不再支持在索引时应用,支持在查询时应用。

执行查询

GET /my-index-000002/_search
{
  "query": {
    "match": {
      "title": {
        "query": "quick brown fox",
        "boost": 1
      }
    }
  }
}

结果是:

 "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.5469647,
    "hits" : [
      {
        "_index" : "my-index-000001",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.5469647,
        "_source" : {
          "title" : "The Quick Brown Fox"
        }
      },
      {
        "_index" : "my-index-000001",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.5469647,
        "_source" : {
          "title" : "A Quick Brown Fox"
        }
      }
    ]
  }

最大评分是0.5469647

GET /my-index-000002/_search
{
  "query": {
    "match": {
      "title": {
        "query": "quick brown fox",
        "boost": 1
      }
    }
  }
}

这个查询的结果是:

"hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 1.0939294,
    "hits" : [
      {
        "_index" : "my-index-000001",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0939294,
        "_source" : {
          "title" : "The Quick Brown Fox"
        }
      },
      {
        "_index" : "my-index-000001",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 1.0939294,
        "_source" : {
          "title" : "A Quick Brown Fox"
        }
      }
    ]
  }

权重提升的意思就是得分提升了。

3.强制格式转换(coerce)

根据生成方式的不同,数字可能会在JSON主体中呈现为真正的JSON数字,但也可能呈现为字符串,甚至可能会被呈现为浮点数,例如,数字5的呈现方式:“5”、 5.0、“5.0”。

强制转换试图清理脏值以适应字段的数据类型,例如,字符串将被强制转换为数字;整数值将截断浮点数。

PUT my-index-000002
{
  "mappings": {
    "properties": {
      "number_one": {
        "type": "integer"
      },
      "number_two": {
        "type": "integer",
        "coerce": false
      }
    }
  }
}

PUT my-index-000002/_doc/1
{
  "number_one": "10" 
}

PUT my-index-000002/_doc/2
{
  "number_two": "10" 
}

number_one将包含整数10,number_two将被拒绝,因为这个字段禁用了coerce

可以使用更新映射API在现有字段上更新强制设置值。

可以在索引级别设备强制的默认级别,字段级别优先级高于索引级别:

PUT my-index-000001
{
  "settings": {
    "index.mapping.coerce": false
  },
  "mappings": {
    "properties": {
      "number_one": {
        "type": "integer",
        "coerce": true
      },
      "number_two": {
        "type": "integer"
      }
    }
  }
}

4.动态映射(dynamic)

在索引包含新字段的文档时,Elasticsearch会动态地将该字段添加到文档或文档中的内部对象中。下面的文档在name对象下添加字符串字段username、对象字段name和两个字符串字段:

PUT my-index-000001/_doc/1
{
  "username": "johnsmith",
  "name": { 
    "first": "John",
    "last": "Smith"
  }
}

GET my-index-000001/_mapping 

结果是:

{
  "my-index-000006" : {
    "mappings" : {
      "properties" : {
        "name" : {
          "properties" : {
            "first" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "last" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            }
          }
        },
        "username" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}

下面的文档添加了两个字符串字段:emailname.middle

PUT my-index-000006/_doc/2
{
  "username": "marywhite",
  "email": "mary@white.com",
  "name": {
    "first": "Mary",
    "middle": "Alice",
    "last": "White"
  }
}

GET my-index-000006/_mapping

结果是:

{
  "my-index-000006" : {
    "mappings" : {
      "properties" : {
        "email" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "name" : {
          "properties" : {
            "first" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "last" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "middle" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            }
          }
        },
        "username" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}

内部对象从其父对象或映射类型继承动态设置。在下面的示例中,在类型级别禁用了动态映射,因此不会动态添加新的顶级字段。

然而,user.social_networks对象支持动态映射,因此可以向这个内部对象添加字段。

PUT my-index-000007
{
  "mappings": {
    "dynamic": false, 
    "properties": {
      "user": { 
        "properties": {
          "name": {
            "type": "text"
          },
          "social_networks": {
            "dynamic": true, 
            "properties": {}
          }
        }
      }
    }
  }
}
  • 在类型级别就禁用了动态映射
  • 这个user对象继承了类型级的设置
  • social_networks对象启用了动态映射

dynamic参数控制是否动态添加新字段,并接受以下参数:

  • true

    这是默认的行为。当Elasticsearch遇到新的未知字段时,它会自动将该字段添加到映射中,并根据数据推断字段的类型。这意味着你可以直接向索引中添加包含新字段的文档,而不需要事先更新映射。

  • runtime

    使用此选项时,任何新字段都会被作为运行时字段添加。运行时字段不是在索引时构建的,而是查询时从_source字段动态创建的。它们不会占用额外的磁盘空间,因为它们不建立倒排索引,但这也意味着它们不能用于排序或聚合操作。运行时字段非常适合用于那些仅需要偶尔查询且不需要高性能的字段。

  • false

    dynamic设置为false时,Elasticsearch将忽略任何不在现有映射中的新字段。虽然这些字段的数据不会被索引或搜索,但是它们仍然会被存储在_source字段中,因此可以通过访问_source来查看原始文档内容。如果你希望保持映射的严格控制,或者你不想因为意外的字段而增加索引大小,这是一个有用的设置。

  • strict

    这是最严格的设置。如果设置了strict并且遇到了新的未知字段,Elasticsearch将拒绝整个文档的索引操作,并抛出异常。这种设置适用于对映射有非常严格要求的情况,例如生产环境中,你可能希望确保所有的字段都是预定义的,以避免潜在的数据结构混乱或性能问题。在这种情况下,所有新字段必须显式地添加到映射中,才能被接受和使用。

5.合并字段(copy_to)

该参数允许您复制多个 fields 放入一个 group 字段中,然后可以作为单个字段使用。

如果经常搜索多个字段,则可以通过使用copy_to来搜索较少的字段以提高搜索速度。

例如,将索引中的first_namelast_name的值copyfull_name

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "first_name": {
        "type": "text",
        "copy_to": "full_name" 
      },
      "last_name": {
        "type": "text",
        "copy_to": "full_name" 
      },
      "full_name": {
        "type": "text"
      }
    }
  }
}

PUT my-index-000001/_doc/1
{
  "first_name": "John",
  "last_name": "Smith"
}

GET my-index-000001/_search
{
  "query": {
    "match": {
      "full_name": { 
        "query": "John Smith",
        "operator": "and"
      }
    }
  }
}

合并之后仍然可以分别查询first_namelast_name字段的名和姓,但是也可以同时查询full_name

一些要点:

  1. copy_to复制的是字段值,而不是术语(这是分析过程的结果)。
  2. 原始_source字段不会被修改以显示复制的值。
  3. 相同的值可以复制到多个字段,copy_to: ["field_1", "field_2"]
  4. 不能通过中间字段递归地复制,如A和B copy_to 到 C,再把C copy_to 到 D。
  5. copy_to不支持对象类型字段,如 objectnesteddate_range 等。

在动态映射中使用copy_to时,需要考虑以下几点:

  • 如果目标字段在索引映射中不存在,且dynamic设置为true,则不存在的目标字段将被动态添加到索引映射中。
  • 如果dynamic设置为false,则不会将目标字段添加到索引映射中,并且不会复制该值。
  • 如果dynamic设置为strict,复制到不存在的字段将导致错误。
  • 如果目标字段是嵌套的,那么copy_to字段必须指定嵌套字段的完整路径。省略完整路径将导致一个strict_dynamic_mapping_exception。使用“copy_to”: ["parent_field。Child_field "]来正确定位一个嵌套字段。

示例:

PUT /test_index
{
  "mappings": {
    "dynamic": "strict",
    "properties": {
      "description": {
        "properties": {
          "notes": {
            "type": "text",
            "copy_to": [ "description.notes_raw"], 
            "analyzer": "standard",
            "search_analyzer": "standard"
          },
          "notes_raw": {
            "type": "keyword"
          }
        }
      }
    }
  }
}
  • notes字段被复制到notes_raw字段。如果只针对notes_raw而不是description.notes_raw会导致strict_dynamic_mapping_exception

    在本例中,notes_raw没有定义在映射的根,而是定义在description字段下。如果没有完全限定的路径,Elasticsearch将把copy_to目标解释为根级字段,而不是描述下的嵌套字段。

6.列式存储(doc_values)

默认情况下,大多数字段都是被索引的,这使得它们可以被搜索。倒排索引允许查询在唯一排序的词列表中查找搜索词,并从中立即访问包含该词的文档列表。

排序、聚合和访问脚本中的字段值需要不同的数据访问模式。我们需要能够查找文档并查找字段中包含的术语,而不是查找术语并查找文档。

文档值是磁盘上的数据结构,在索引文档时构建,这使得这种数据访问模式成为可能。它们采用面向列的方式存储与_source相同的值,这对于排序和聚合来说更有效。除了textannotated_text字段外,几乎所有字段类型都支持文档值

所有支持doc_values的字段默认都是启用的。如果你确定你不需要对字段进行排序或聚合,或者从脚本中访问字段值,你可以禁用文档值以节省磁盘空间。

对于wildcard(通配符)字段类型来说,doc_values是必须的,因为这种字段类型通常用于模式匹配查询,例如查找文件路径或URL等结构化字符串。为了支持高效的聚合和排序功能,Elasticsearch强制启用了这些字段的doc values,不允许用户关闭此设置。

如果出于某些原因你需要减少索引的磁盘占用或者提高索引速度,但又不打算对wildcard字段执行排序或聚合操作,那么你可能需要考虑使用其他字段类型来代替wildcard字段,比如keyword类型,这取决于你的具体需求和应用场景。

7.全局序号(eager_global_ordinals)

为了支持聚合和其他需要在每个文档的基础上查找字段值的操作,Elasticsearch使用了一种称为文档值(doc_values)的数据结构。基于术语的字段类型,例如使用序数映射存储其文档值,以获得更紧凑的表示。这种映射的工作原理是根据每个术语的字典顺序为其分配增量整数或序数。字段的doc values仅存储每个文档的序数,而不是原始术语,并使用单独的查找结构在序数和terms.keyword之间进行转换。

在聚合期间使用序号可以极大地提高性能。例如,聚合仅依赖于序数将文档收集到分片级的桶中,然后在跨分片组合结果时将序数转换回其原始术语值。

每个索引段定义自己的序数映射,但是聚合在整个分片上收集数据。因此,为了能够在分片级操作(如聚合)中使用序数,Elasticsearch创建了一个称为全局序数的统一映射。全局序数映射建立在段序数之上,并通过维护每个段从全局序数到局部序数的映射来工作。

如果搜索包含以下任何组件,则使用全局序数:

  • 特定类型的桶聚合:
    • keyword
    • ip
    • flattened:扁平化字段,用于存储扁平化的JSON对象。
    • terms:术语聚合,根据字段值对文档进行分组。
    • composite:复合聚合,允许多级分组。
    • diversified_sampler:多样化采样器,确保结果集中不同类别的文档分布均匀。
    • significant_terms:显著项聚合,识别在特定上下文中显著异常的项
  • 启用fielddata的字段上的桶聚合,如果要执行文本字段的某些聚合操作(如术语聚合),需要启用fielddata
  • 父文档和子文档之间的操作
    • join:用于关联父文档和子文档。
    • has_child:查询子文档是否存在。
    • parent:查询父文档。

全局序数映射使用堆内存作为字段数据缓存的一部分。高基数字段上的聚合可能会使用大量内存并触发字段数据断路器。

高基数字段指的是那些具有大量唯一值的字段。例如,一个user_id字段可能包含数百万个不同的用户标识符,或者一个product_id字段可能对应着成千上万的不同产品。与之相对的是低基数字段,如布尔值(true/false)、状态码(成功/失败)、国家/地区代码等,这些字段的唯一值数量相对较少。

在Elasticsearch中,当对高基数字段执行聚合操作时,比如使用terms聚合来统计每个唯一值的数量,系统需要处理大量的不同值,这可能导致性能问题和内存消耗过大。

字段数据断路器是Elasticsearch的一种保护机制,旨在防止由于过多的内存使用而导致节点崩溃或变得不稳定。它通过监控用于存储字段数据(例如全局序数、fielddata等)的堆内存使用情况,在内存使用接近或超过预设阈值时自动中断操作,从而避免节点耗尽所有可用内存。

断路器会实时跟踪用于字段数据结构的堆内存使用量,如果某个查询或操作尝试分配的内存超出了断路器设定的限制,该操作将被拒绝,并抛出异常,以防止节点因内存不足而陷入不稳定状态。

Elasticsearch中的主要断路器有字段数据断路器 (fielddata)、请求断路器 (request)和总断路器 (total):

  • 字段数据断路器主要用于限制聚合、排序等操作中字段数据缓存(如全局序数)所占用的内存,默认限制是JVM堆大小的60%
  • 请求断路器用于限制单个查询或聚合请求所能使用的内存总量,默认限制是JVM堆大小的40%
  • 总断路器作为一个额外的安全层,它监控所有断路器的综合内存使用情况,所有类型的断路器总共可以使用的最大内存,默认限制是JVM堆大小的70%

对于高基数字段进行聚合操作的有效策略是:

  • 可以使用其他类型的聚合,例如使用sampler聚合来减少参与聚合的文档数量,或者使用composite聚合分页获取结果。
  • 通过数据建模调整来减少唯一值的数量,比如增加哈希函数或日期范围。
  • 非必要不对文本字段启用fielddata来进行聚合,可以使用keyword子字段来进行精确匹配和聚合。
  • 确保只对必要的字段执行聚合,并尽量减少同时聚合的字段数量。

sampler聚合允许你从匹配的文档集中随机抽取一个子集进行聚合,从而减少参与聚合的文档数量,进而降低基数,这对于需要高基数聚合但不需要精确结果的场景非常有用。

假设我们有一个日志索引,并且想要获取最常见的用户代理(User-Agent)字符串:

GET /logs/_search
{
  "size": 0,
  "aggs": {
    "sample": {
      "sampler": {
        "shard_size": 5000 # 每个分片上抽取的文档数量
      },
      "aggs": {
        "top_user_agents": {
          "terms": {
            "field": "user_agent.keyword",
            "size": 10
          }
        }
      }
    }
  }
}

在这个例子中,sampler聚合会确保每个分片最多只会处理5000个文档,然后从中找出最常出现的10个用户代理字符串。

composite聚合允许你以分页的方式逐步获取聚合结果,特别适合于高基数字段或大数据集,还是上面的例子,我们可以设置一个composite聚合来逐步获取用户代理的信息。

首先,我们需要定义一个初始查询

GET /logs/_search
{
  "size": 0,
  "aggs": {
    "user_agents": {
      "composite": {
        "size": 100,  # 每次返回的最大桶数
        "sources": [
          {
            "user_agent": {
              "terms": {
                "field": "user_agent.keyword"
              }
            }
          }
        ]
      }
    }
  }
}

然后在每次请求中带上上次响应中的after_key值来进行下一页的结果获取。

GET /logs/_search
{
  "size": 0,
  "aggs": {
    "user_agents": {
      "composite": {
        "size": 100,
        "sources": [
          {
            "user_agent": {
              "terms": {
                "field": "user_agent.keyword"
              }
            }
          }
        ],
        "after": {
          "user_agent": "上次响应中的 after_key 值"
        }
      }
    }
  }
}

虽然composite聚合允许逐步获取大量数据,但频繁的小批量请求也会对集群性能产生影响,根据实际情况调整size参数以找到合适的平衡点。

在搜索期间使用序数之前,必须先构建全局序数映射。默认情况下,Elasticsearch会在第一次执行涉及该字段的聚合或排序查询时构建全局序数映射。这种策略优先考虑索引速度,因为全局序数的构建不会立即发生,而是在第一次查询时触发。

eager_global_ordinals设置为true时,意味着每当有新的数据刷新到分片(例如,当段被合并或新数据被添加)或者创建分片的新副本(如增加副本数量或将分片重新定位到新节点)时,Elasticsearch会立即构建全局序数映射。这增加了索引的时间开销,因为它必须等待全局序数构建完成才能继续。但由于全局序数已经预先构建好,后续的搜索请求都不需要再等待全局序数的生成,从而提高了搜索的响应速度。

PUT my-index-000001/_mapping
{
  "properties": {
    "tags": {
      "type": "keyword",
      "eager_global_ordinals": true
    }
  }
}

通过更新设置:eager_global_ordinals,可以随时禁用主动加载。

PUT my-index-000001/_mapping
{
  "properties": {
    "tags": {
      "type": "keyword",
      "eager_global_ordinals": false
    }
  }
}

在冻结(_freeze)的索引上,全局序数在每次搜索后就会被丢弃,并在请求时重新构建。这意味着不应该在冻结索引上使用eager_global_ordinals,因为这将导致每次搜索时重新加载全局序数。相反,应该在冻结索引之前将索引强制合并到单个段,可以减少全局序数映射的复杂度。

强制合并索引到单个段:POST /my-index-000001/_forcemerge?max_num_segments=1

通常,全局序数在加载时间和内存使用方面不会产生很大的开销。但是,对于具有大型分片的索引,或者字段包含大量唯一术语值的索引,加载全局序数可能会非常昂贵。因为全局序数为分片上的所有段提供了统一的映射,所以当一个新的段变得可见时,它们也需要完全重建。

terms(词条)、sampler(采样器)、significant_terms(显著词条) 等聚合支持execution_hint的参数,它用于控制如何收集桶(buckets)。默认情况下,该参数的值是 global_ordinals(全局序号),但可以将其设置为 map,这样可以直接使用词条的值,而不必加载全局序号。

8.索引禁用(enabled)

Elasticsearch试图索引你给它的所有字段,但有时你只想存储字段而不索引它。例如,使用Elasticsearch作为web会话存储,此时只希望索引会话ID和上次更新时间,不需要对会话数据本身进行查询或运行聚合。

enabled的设置只能应用于顶级映射定义(不能直接用于基本数据类型)和对象字段,这会导致Elasticsearch完全跳过对字段内容的解析。JSON仍然可以从_source字段中检索,但不能搜索或以任何其他方式存储:

PUT my-index-000009
{
  "mappings": {
    "properties": {
      "user_id": {
        "type":  "keyword"
      },
      "last_updated": {
        "type": "date"
      },
      "session_data": { 
        "type": "object",
        "enabled": false
      }
    }
  }
}

PUT my-index-000009/_doc/session_1
{
  "user_id": "kimchy",
  "session_data": { 
    "arbitrary_object": {
      "some_array": [ "foo", "bar", { "baz": 2 } ]
    }
  },
  "last_updated": "2015-12-06T18:20:22"
}

PUT my-index-000009/_doc/session_2
{
  "user_id": "jpountz",
  "session_data": "none", 
  "last_updated": "2015-12-06T18:22:13"
}

session_data字段被禁用,任何任意数据都可以传递给session_data字段,因为它将被完全忽略。session_data也会忽略非JSON对象的值。

整个映射也可能被禁用,在这种情况下,文档存储在_source字段中,这意味着它可以被检索,但它的任何内容都不会以任何方式被索引:

PUT my-index-000010
{
  "mappings": {
    "enabled": false 
  }
}

PUT my-index-000010/_doc/session_1
{
  "user_id": "kimchy",
  "session_data": {
    "arbitrary_object": {
      "some_array": [ "foo", "bar", { "baz": 2 } ]
    }
  },
  "last_updated": "2015-12-06T18:20:22"
}

GET my-index-000010/_doc/session_1 

GET my-index-000010/_mapping 

整个映射都被禁用,可以检索文档,检查映射会发现没有添加任何字段。

无法更新现有字段和顶级映射定义的enabled设置。

需要注意的是,因为Elasticsearch完全跳过解析字段内容,所以可以将非对象数据添加到禁用字段:

PUT my-index-000011
{
  "mappings": {
    "properties": {
      "session_data": {
        "type": "object",
        "enabled": false
      }
    }
  }
}

PUT my-index-000011/_doc/session_1
{
  "session_data": "foo bar" 
}

GET my-index-000011/_search
{
  "query": {
    "match_all": {}
  }
}

文档被成功添加,即使session_data包含非对象数据。

9.日期格式(format)

在JSON文档中,日期用字符串表示。Elasticsearch使用一组预先配置的格式来识别和解析这些字符串,并将其解析为一个长值,表示UTC中从epoch开始的毫秒数。

除了内置格式之外,还可以自定义格式:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "date": {
        "type":   "date",
        "format": "yyyy-MM-dd"
      }
    }
  }
}

许多支持日期值的API也支持日期数学表达式,例如now-1m/d 表示当前时间减去一个月,舍入到最近的一天(即今天的零点)。

Elasticsearch 提供了丰富的日期数学表达式功能,允许用户在查询中使用动态的时间计算。这些表达式可以用于定义时间范围、设置索引模式等场景,特别适合于日志分析和监控系统中,以处理滚动索引或根据当前时间进行过滤。

日期数学表达式的基本格式为:<base><+|-><time-unit>/<rounding>。其中:

  • <base> 是基础时间点,如 now 或者具体的日期字符串。
  • <+|-> 表示增加或减少时间。
  • <time-unit> 是要增加或减少的时间单位。
  • <rounding>(可选)是将结果舍入到最近的时间单位边界。

以下是一些常用的时间单位符号:

  • y
  • M
  • w
  • d
  • h 小时
  • m 分钟
  • s

示例表达式:

  1. 当前时间:
    • now 表示当前时间。
  2. 相对时间:
    • now-1h 表示当前时间减去一小时。
    • now+1d 表示当前时间加上一天。
  3. 舍入时间:
    • now/d 表示当前时间舍入到当天的开始(即今天的零点)。
    • now/M 表示当前时间舍入到当月的第一天。
    • now/y 表示当前时间舍入到当年的第一天。
  4. 组合操作:
    • now-1M/d 表示当前时间减去一个月,并舍入到最近的一天。
    • now-1w/h 表示当前时间减去一周,并舍入到最近的一小时。
  5. 指定时间点的数学运算:
    • 2023-07-01||+1M 表示从2023年7月1日起增加一个月。
    • 2023-07-01T12:00:00||-1d/h 表示从2023年7月1日中午12点起减去一天并舍入到最近的一小时。
  6. 更复杂的时间间隔:
    • /h 舍入到最接近的小时。
    • /w 舍入到最接近的周。
    • /M 舍入到最接近的月。
    • /y 舍入到最接近的年。
  7. 混合使用:
    • now-1M+1d 表示从当前时间减去一个月后再加上一天。
    • 2023-07-01||+1M-1d/h 表示从2023年7月1日起增加一个月再减去一天,并舍入到最近的一小时。

注意事项:

  • 如果没有提供明确的基础时间点,默认使用 now
  • 当使用具体日期作为基础时间点时,必须通过双竖线 || 分隔基础时间和加减时间部分。
  • 舍入操作总是朝向过去的方向,即它会找到之前最近的一个时间单位边界。
  • 对于非整数时间单位(例如秒),舍入行为可能不会按照预期工作,因此通常只对较大的时间单位(如日、周、月、年)使用舍入。

10.限制字符数(ignore_above)

ignore_above 是 Elasticsearch 中用于字段映射的一个参数,主要用于 keyword 类型的字段。它指定了一个字符数限制,当字符串或数组中的元素长度超过这个限制时,Elasticsearch 将不会索引这些值也不会将它们存储在倒排索引中用于搜索和聚合。

大于ignore_above设置的字符串将不会被索引或存储。对于字符串数组,ignore_above将分别应用于每个数组元素,并且长度大于ignore_above的字符串元素将不会被索引或存储。

如果后者被启用,所有的字符串/数组元素仍然会出现在_source字段中,这是Elasticsearch的默认值。

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "message": {
        "type": "keyword",
        "ignore_above": 20 
      }
    }
  }
}

PUT my-index-000001/_doc/1
{
  "message": "Syntax error"
}

PUT my-index-000001/_doc/2 
{
  "message": "Syntax error with some long stacktrace"
}

GET my-index-000001/_search 
{
  "aggs": {
    "messages": {
      "terms": {
        "field": "message"
      }
    }
  }
}
  • message字段将忽略任何超过20个字符的字符串。
  • id为1的文档已成功建立索引,id为2的文档将被索引,但不会索引message字段。
  • 搜索返回两个文档,但只有第一个文档出现在术语聚合中。

可以使用更新映射API在现有字段上更新ignore_above设置。

这个选项对于防止Lucene的32766字节长度限制也很有用。

ignore_above的值是字符数,但Lucene计算字节数。如果您使用带有许多非ascii字符的UTF-8文本,您可能希望将限制设置为32766 / 4 = 8191,因为UTF-8字符最多可能占用4个字节。

11.忽略格式异常(ignore_malformed)

默认情况下,尝试将错误的数据类型编入字段将引发异常,并拒绝整个文档。ignore_malformed参数如果设置为true,则允许忽略异常。格式错误的字段没有被索引,但是文档中的其他字段被正常处理。

例如:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "number_one": {
        "type": "integer",
        "ignore_malformed": true
      },
      "number_two": {
        "type": "integer"
      }
    }
  }
}

PUT my-index-000001/_doc/1
{
  "text":       "Some text value",
  "number_one": "foo" 
}

PUT my-index-000001/_doc/2
{
  "text":       "Some text value",
  "number_two": "foo" 
}
  • id为1的文档只会索引text字段,不会索引number_one字段,但是可以通过_source获取到number_one字段。
  • id为2的文档将被拒绝,因为number_two不允许格式错误的值。

支持ignore_malformed设置的数据类型:

  • 数值类型:long, integer, short, byte, double, float, half_float, scaled_float
  • 日期类型:datedate_nanos
  • 空间地理:geo_pointgeo_shape
  • IP类型:ip

可以使用更新映射API在现有字段上更新ignore_malformed设置值。

可以在索引级别上设置ignore_malformed设置,以在所有允许的映射类型中忽略全局的异常内容。如果在索引级别上设置了ignore_malformed,不支持该设置的映射类型将忽略它。

PUT my-index-000001
{
  "settings": {
    "index.mapping.ignore_malformed": true 
  },
  "mappings": {
    "properties": {
      "number_one": { 
        "type": "byte"
      },
      "number_two": {
        "type": "integer",
        "ignore_malformed": false 
      }
    }
  }
}
  • number_one字段继承索引级别设置,number_two字段覆盖索引级别设置以关闭ignore_malformed

只有在无法避免格式错误的情况下,或者在某些字段的格式错误不影响业务逻辑时,才考虑使用 ignore_malformed。如果可以的话,尽量确保数据格式的正确性,以避免潜在的问题和不必要的复杂性。

为了检查哪些文档包含格式错误的字段,可以利用一些特殊的 _ignore 字段进行查询。通过使用 existstermterms 查询,你可以检查有多少文档包含这些格式错误的字段。这样做可以帮助你了解数据的质量,及时发现格式错误的字段并进行修正。

示例:

PUT my-index
{
  "mappings": {
    "properties": {
      "date_field": {
        "type": "date",
        "format": "yyyy-MM-dd||epoch_millis",
        "ignore_malformed": true
      }
    }
  }
}

POST my-index/_doc
{
  "date_field": "invalid-date"
}

# 找到所有有格式错误的文档
GET my-index/_search
{
  "query": {
    "exists": {
      "field": "_ignored"
    }
  }
}

# 找出具体哪个字段有格式错误
GET my-index/_search
{
  "query": {
    "term": {
      "_ignored": "date_field"
    }
  }
}

不能对nestedobjectrange数据类型使用ignore_malformed。也不能使用ignore_malformed来忽略提交给错误数据类型字段的JSON对象。JSON对象是用大括号“{}”包围的任何数据,包括映射到嵌套、对象和范围数据类型的数据。将JSON对象提交到不支持的字段,Elasticsearch将返回一个错误并拒绝整个文档,而不管ignore_malformed设置如何。

12.索引字段(index)

index选项控制是否对字段值进行索引。它接受truefalse,默认为true。没有索引的字段是不可查询的。

enabled选项的对比:

特性index: falseenabled: false
适用字段所有字段类型仅对象字段和顶级映射
索引行为不创建倒排索引,但仍解析并存储完全跳过解析,不创建索引
_source 行为字段值存储在 _source原始 JSON 存储在 _source,但未解析
查询能力不可查询、排序或聚合不可查询、排序或聚合

13.反向索引(index_options)

index_options 参数控制了 Elasticsearch 中如何将信息添加到反向索引(inverted index)中,影响搜索和高亮的效果。简单来说,它决定了反向索引中存储哪些信息,并且这个设置通常只用于 text 类型字段。选择合适的index_options可以优化索引大小和查询性能。

反向索引:

​ 在 Elasticsearch 中,反向索引是一种数据结构,用来快速查找某个术语(term)在文档中出现的位置。反向索引包含了术语(单词)到文档的映射,允许 Elasticsearch 快速地查找包含特定词语的文档。

index_options 的不同值和它们的意义:

  1. docs
    • 含义:仅将文档编号索引。
    • 用途:只能回答问题——“这个术语是否出现在该字段中?”
    • 存储信息:只记录哪些文档包含该术语,而不关心术语在文档中的具体频率、位置或其他详细信息。
    • 适用场景:当你只关心是否存在某个术语时(例如,布尔查询),而不关心该术语在文档中的具体位置或频率。
  2. freqs
    • 含义:索引文档编号和术语频率。
    • 用途:除了知道术语是否存在外,还知道它在每个文档中出现的次数。
    • 存储信息:不仅记录哪些文档包含该术语,还记录每个文档中该术语出现的频率。频率信息可以用于给出现频繁的术语更高的分数。
    • 适用场景:当你需要根据术语在文档中出现的频率来打分时(例如,文本评分或相关性排名)。
  3. positions(默认值)
    • 含义:索引文档编号、术语频率和术语位置。
    • 用途:除了术语是否存在和术语频率外,还知道术语在文档中的位置。
    • 存储信息:记录术语的出现位置(即它在文本中的具体位置),使得可以执行短语查询(phrase queries)和相邻查询(proximity queries),这些查询依赖于术语在文本中的顺序。
    • 适用场景:当你需要进行短语查询或确定两个词是否在文本中的某个范围内时(例如,查询“quick brown”时,检查它们是否紧跟在一起)。
  4. offsets
    • 含义:索引文档编号、术语频率、术语位置和术语的字符偏移量(start 和 end)。
    • 用途:除了上述信息,还知道术语在原始文本中的开始和结束位置。
    • 存储信息:记录术语的字符偏移量(即它在原始文本中的起始和结束位置),这对于高亮显示特别重要。高亮显示会用到这些偏移量来快速确定术语在原始文本中的位置,从而显示高亮效果。
    • 适用场景:当你需要高效地对搜索结果进行高亮显示时,尤其是当查询结果返回大量文本内容时。

各种设置的比较:

选项存储信息用途
docs只记录文档编号知道术语是否出现在文档中。
freqs文档编号 + 术语频率知道术语是否存在,并记录它在文档中的出现频率。
positions文档编号 + 术语频率 + 术语位置知道术语是否存在,频率和术语在文档中的位置。
offsets文档编号 + 术语频率 + 术语位置 + 术语的字符偏移量(开始和结束)除了所有前面提到的信息,还能高效进行高亮显示。

使用场景:

  • 如果你仅仅关心一个术语是否出现在文档中,你可以选择 docs
  • 如果你还关心术语在文档中出现的频率,可以选择 freqs
  • 如果你需要使用短语查询、邻近查询等,推荐使用 positions
  • 如果你需要高亮显示功能,推荐使用 offsets,因为它提供了更精确的位置信息。
PUT my-index-000001
{
  "mappings": {
    "properties": {
      "text": {
        "type": "text",
        "index_options": "offsets"
      }
    }
  }
}

PUT my-index-000001/_doc/1
{
  "text": "Quick brown fox"
}

GET my-index-000001/_search
{
  "query": {
    "match": {
      "text": "brown fox"
    }
  },
  "highlight": {
    "fields": {
      "text": {} 
    }
  }
}

14.索引二元词组(index_phrases)

如果启用,词组(shingles)将被索引到单独的字段中。这使得精确短语查询(无slop)可以更有效地运行,但代价是更大的索引。

请注意,当停止词未被删除时,此操作效果最好,因为包含停止词的短语将不使用子字段,而将退回到标准短语查询。

默认值是false

15.索引词条的前缀(index_prefixes)

index_prefixes 参数用于优化前缀查询(prefix queries)的性能。通过索引词条的前缀,Elasticsearch 可以更高效地处理以特定字符串开头的查询请求。这对于需要频繁执行前缀搜索的应用场景特别有用,例如自动补全、搜索建议等。

index_prefixes 参数允许你指定哪些长度的前缀应该被索引,并接受以下可选设置:

  • min_chars:指定了要索引的最短前缀长度,默认是2,必须大于 0,且是包含性的。
  • max_chars:指定了要索引的最长前缀长度,默认是5,必须小于 20,且是包含性的。
  • 默认情况下,长度在 2 到 5 个字符之间的前缀都会被索引。

可以根据具体需求自定义 min_charsmax_chars,以调整索引的前缀长度范围:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "full_name": {
        "type": "text",
        "index_prefixes": {
          "min_chars": 1,
          "max_chars": 10
        }
      }
    }
  }
}

在这个例子中,所有长度在 1 到 10 个字符之间的前缀都会被索引。

索引更多的前缀会增加索引文件的大小,因此需要权衡查询性能和存储成本。

min_charsmax_chars 的值应根据实际应用场景来设定。过短的前缀可能会导致过多的匹配项,而过长的前缀则可能无法覆盖足够的搜索词。

16.字段元数据(meta)

字段元数据是附加到字段上的信息,这些信息对 Elasticsearch 本身是不透明的,主要用于多个应用程序在同一个索引上共享关于字段的元信息。可以帮助不同应用程序之间共享字段的额外信息,例如单位、度量类型等。

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "latency": {
        "type": "long",
        "meta": {
          "unit": "ms"
        }
      }
    }
  }
}

在这个例子中,latency 字段被赋予了一个 unit 元数据项,其值为 "ms",表示该字段的单位是毫秒。

元数据限制

  • 条目数量:每个字段最多可以有 5 个元数据条目。
  • 键长度:元数据键的最大长度为 20 个字符。
  • 值长度:元数据值必须是字符串,且最大长度为 50 个字符。

可以通过提交映射更新来修改字段的元数据,新的元数据将完全覆盖现有字段的元数据。

Elastic 产品使用了一些标准的元数据条目,建议遵循这些约定以获得更好的开箱即用体验:

  • unit:与数值字段关联的单位,如时间单位或百分比。对于百分比,使用 percent;对于字节,使用 byte
  • metric_type:数值字段的度量类型,可以是 gaugecounter
    • gauge:单值测量,可以随时间上下波动,例如温度。
    • counter:累积计数器,只增不减,例如 Web 服务器处理的请求数。

17.多字段(fields)

多字段的目的是以不同的方式索引同一字段,例如,字符串字段可以映射为全文搜索的text字段,也可以映射为排序或聚合的keyword字段:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "city": {
        "type": "text",
        "fields": {
          "raw": { 
            "type":  "keyword"
          }
        }
      }
    }
  }
}

PUT my-index-000001/_doc/1
{
  "city": "New York"
}

PUT my-index-000001/_doc/2
{
  "city": "York"
}

GET my-index-000001/_search
{
  "query": {
    "match": {
      "city": "york" 
    }
  },
  "sort": {
    "city.raw": "asc" 
  },
  "aggs": {
    "Cities": {
      "terms": {
        "field": "city.raw" 
      }
    }
  }
}

city.raw字段是city字段的keyword版本;city字段可以用于全文搜索;city.raw字段可以用于排序和聚合。

可以使用更新映射API为已经存在的字段添加多字段。

多字段映射与父字段的映射完全分离,多字段不会从父字段继承任何映射选项。多字段不会改变原来的_source字段。

多字段别的用法是以不同的方式分析相同的字段,以获得更好的相关性。例如,我们可以用标准分析器索引一个字段,它将文本分解成单词,再用英语分析器将单词分解成词根形式:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "text": { 
        "type": "text",
        "fields": {
          "english": { 
            "type":     "text",
            "analyzer": "english"
          }
        }
      }
    }
  }
}

PUT my-index-000001/_doc/1
{ "text": "quick brown fox" } 

PUT my-index-000001/_doc/2
{ "text": "quick brown foxes" } 

GET my-index-000001/_search
{
  "query": {
    "multi_match": {
      "query": "quick brown foxes",
      "fields": [ 
        "text",
        "text.english"
      ],
      "type": "most_fields" 
    }
  }
}

text字段使用默认的分词器进行索引,text.english字段使用english分析器进行索引,该分析器会对英文文本进行特定的预处理,如去除停用词、词干提取等。

text 字段使用默认的分词器,可能会将 quick brown foxes 分词为 ["quick", "brown", "foxes"]

text.english 字段使用 english 分析器,可能会将 quick brown foxes 分词为 ["quick", "brown", "fox"](注意 foxes 被词干提取为 fox)。

文档 1 (quick brown fox) 在 text.english 字段上会有很好的匹配,因为 foxes 被转换成了 fox

文档 2 (quick brown foxes) 在 text 字段上直接匹配,在 text.english 字段上也会有很好的匹配,所以它的评分较高。

18.标准化处理(Normalizer)

keyword字段的normalizer属性与analyzer类似,但它保证分析链只产生一个token。这意味着它不会将文本分割成多个部分,而是作为一个整体处理。

normalizer在索引keyword之前应用,并且在查询时通过像match查询或term级别的查询解析器搜索keyword字段时也会被应用。

Elasticsearch自带了一些简单的normalizer,如lowercaseasciifolding,前者将所有字符转为小写,后者移除变音符号和其他非ASCII字符。也可以在分析设置中定义自定义的normalizer,如下所示:

PUT index
{
  "settings": {
    "analysis": {
      "normalizer": {
        "my_normalizer": {
          "type": "custom",
          "char_filter": [],
          "filter": ["lowercase", "asciifolding"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "foo": {
        "type": "keyword",
        "normalizer": "my_normalizer"
      }
    }
  }
}

接下来是向索引中添加三个文档,其中两个文档含有不同形式的"bar"(一个是带有重音的大写BAR,另一个是全小写的bar),还有一个文档含有单词baz

PUT index/_doc/1
{
  "foo": "BÀR"
}

PUT index/_doc/2
{
  "foo": "bar"
}

PUT index/_doc/3
{
  "foo": "baz"
}

执行term查询和match查询,二者都用大写的BAR作为查询条件:

GET index/_search
{
  "query": {
    "term": {
      "foo": "BAR"
    }
  }
}

GET index/_search
{
  "query": {
    "match": {
      "foo": "BAR"
    }
  }
}

上述查询匹配到了文档1和2,因为BAR在索引和查询时都被转换成了bar

此外,关键词在索引前进行转换也意味着聚合操作返回的是标准化后的值:

GET index/_search
{
  "size": 0,
  "aggs": {
    "foo_terms": {
      "terms": {
        "field": "foo"
      }
    }
  }
}

19.归一化(norms)

norms 存储了各种归一化因子,这些因子在查询时被用来计算文档相对于查询的得分。

虽然对于评分非常有用,但norms也需要相当多的磁盘空间(通常每个字段每篇文档占用大约一个字节,即使该文档没有这个特定字段)。因此,如果你不需要对某个字段进行评分,你应该禁用该字段上的norms。特别是对于仅用于过滤或聚合的字段,这是非常必要的。

可以使用更新映射API来禁用现有字段上的norms

norms一旦禁用后无法重新启用,可以通过更新映射API如下操作:

PUT my-index-000001/_mapping
{
  "properties": {
    "title": {
      "type": "text",
      "norms": false
    }
  }
}

禁用norms不会立即生效,而是在继续索引新文档时,随着旧段合并到新段中逐步移除。对于已经移除了norms的字段上的任何得分计算可能会返回不一致的结果,因为有些文档不再有norms,而其他文档可能仍然保留着norms

20.替换空值(null_value)

空值(null value)不能被索引或搜索。当一个字段被设置为空值(null),或者是一个空数组,或者是包含空值的数组时,它会被视为该字段没有任何值。

null_value参数允许你用指定的值替换显式的空值,以便它可以被索引和搜索。例如:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "status_code": {
        "type":       "keyword",
        "null_value": "NULL" 
      }
    }
  }
}
PUT my-index-000001/_doc/1
{
  "status_code": null
}
PUT my-index-000001/_doc/2
{
  "status_code": [] 
}
GET my-index-000001/_search
{
  "query": {
    "term": {
      "status_code": "NULL" 
    }
  }
}

使用null_value参数可以将显式的空值替换为术语“NULL”。

需要注意的是,空数组不包含显式的空值,因此不会被替换为null_value

查询“NULL”会返回文档1,但不会返回文档2。

null_value必须与字段的数据类型相同。例如,一个long类型的字段不能有一个字符串类型的null_value

null_value只影响数据如何被索引,并不会修改_source文档。

21.位置增量间隙(position_increment_gap)

位置增量间隙(position_increment_gap)是指在索引具有多个值的文本字段时,在这些值之间添加的一个“虚拟”间隔。这个间隔的目的在于防止大多数短语查询跨这些值进行匹配,从而影响搜索结果的准确性。此间隔的大小可以通过设置position_increment_gap参数来配置,默认情况下为100。

例如:

当我们索引一个文档,该文档的names字段包含两个值:“John Abraham” 和 “Lincoln Smith”。

PUT my-index-000001/_doc/1
{
  "names": [ "John Abraham", "Lincoln Smith"]
}

如果我们执行一个短语匹配查询,查找names字段中包含"Abraham Lincoln"的文档:

GET my-index-000001/_search
{
  "query": {
    "match_phrase": {
      "names": {
        "query": "Abraham Lincoln"
      }
    }
  }
}

因为默认的position_increment_gap是100,所以这个查询不会匹配到我们的文档,这是预期的行为,因为"Abraham"和"Lincoln"并不在同一个字符串中。

但是,如果我们使用一个大于position_increment_gapslop值(表示词之间的最大允许距离),比如101:

GET my-index-000001/_search
{
  "query": {
    "match_phrase": {
      "names": {
        "query": "Abraham Lincoln",
        "slop": 101
      }
    }
  }
}

这时查询会匹配到文档,即使"Abraham"和"Lincoln"是在不同的字符串中,因为slop值超过了位置增量间隙。

我们可以在映射中指定position_increment_gap。例如,将其设置为0:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "names": {
        "type": "text",
        "position_increment_gap": 0
      }
    }
  }
}

然后再次索引相同的文档,并执行相同的短语匹配查询:

GET my-index-000001/_search
{
  "query": {
    "match_phrase": {
      "names": "Abraham Lincoln"
    }
  }
}

在这种情况下,下一个数组元素的第一个词条将与前一个数组元素的最后一个词条相距0个词条。因此,短语查询会匹配到文档,这可能看起来有些奇怪,但这是因为我们在映射中明确指定了这样的行为。

总结来说,position_increment_gap参数控制了多值文本字段中各值之间的假设距离,它有助于决定哪些短语查询会匹配,以及哪些不会。通过调整这个参数,可以微调搜索引擎的行为以适应特定的需求。

22.属性(properties)

属性(Properties)是指类型映射、对象字段和嵌套字段中包含的子字段。这些属性可以是任何数据类型,包括对象(object)和嵌套(nested)。添加属性的方式有:

  • 在创建索引时显式定义它们。
  • 通过使用更新映射API来添加或更新映射类型时显式定义它们。
  • 只需索引包含新字段的文档即可动态添加。

以下是一个向映射类型、对象字段以及嵌套字段添加属性的例子:

PUT my-index-000001
{
  "mappings": {
    "properties": { 
      "manager": {
        "properties": { 
          "age":  { "type": "integer" },
          "name": { "type": "text"  }
        }
      },
      "employees": {
        "type": "nested",
        "properties": { 
          "age":  { "type": "integer" },
          "name": { "type": "text"  }
        }
      }
    }
  }
}

接着,我们可以插入一个符合上述映射的文档:

PUT my-index-000001/_doc/1 
{
  "region": "US",
  "manager": {
    "name": "Alice White",
    "age": 30
  },
  "employees": [
    {
      "name": "John Smith",
      "age": 34
    },
    {
      "name": "Peter Brown",
      "age": 26
    }
  ]
}

这里的properties设置允许在同一索引中同名字段有不同的设置。新的属性可以通过更新映射API添加到已存在的字段中。

在查询、聚合等操作中,可以使用 . 引用内部字段:

GET my-index-000001/_search
{
  "query": {
    "match": {
      "manager.name": "Alice White"
    }
  },
  "aggs": {
    "Employees": {
      "nested": {
        "path": "employees"
      },
      "aggs": {
        "Employee Ages": {
          "histogram": {
            "field": "employees.age",
            "interval": 5
          }
        }
      }
    }
  }
}

必须指定内部字段的完整路径。在这个例子中,我们首先进行了一个匹配查询,寻找manager.name等于"Alice White"的文档。然后,我们对employees进行了嵌套聚合,并进一步对员工年龄进行了直方图聚合,以每5岁为一个区间统计员工的数量。

这样,通过使用.符号,我们可以精确地访问和操作复杂数据结构中的各个部分。

23.搜索分析器(search_analyzer)

search_analyzer设置允许你在搜索时使用与索引时不同的分析器(analyzer)。通常,为了确保查询中的词条格式与倒排索引中的词条格式一致,应该在索引和搜索时应用相同的分析器。然而,在某些情况下,比如实现自动补全或在搜索时使用同义词,使用不同的分析器是有意义的。

默认情况下,查询会使用字段映射中定义的分析器,但你可以通过search_analyzer设置覆盖这个行为。例如:

PUT my-index-000001
{
  "settings": {
    "analysis": {
      "filter": {
        "autocomplete_filter": {
          "type": "edge_ngram",
          "min_gram": 1,
          "max_gram": 20
        }
      },
      "analyzer": {
        "autocomplete": { 
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "autocomplete_filter"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "text": {
        "type": "text",
        "analyzer": "autocomplete", 
        "search_analyzer": "standard" 
      }
    }
  }
}

在此配置中,我们定义了一个自定义的autocomplete分析器,它使用了edge_ngram过滤器来生成从单词开头开始的不同长度的n-gram。这个分析器会在索引时应用于text字段,而搜索时则使用标准分析器(standard)。

接着,我们向索引中添加一个文档:

PUT my-index-000001/_doc/1
{
  "text": "Quick Brown Fox" 
}

当这个文档被索引时,text字段会被分解成以下词条:[ q, qu, qui, quic, quick, b, br, bro, brow, brown, f, fo, fox ]。

然后我们可以执行一个匹配查询:

GET my-index-000001/_search
{
  "query": {
    "match": {
      "text": {
        "query": "Quick Br", 
        "operator": "and"
      }
    }
  }
}

在这个查询中,我们查找包含词条 [ quick, br ] 的文档。因为我们在搜索时使用的是标准分析器,所以这些词条是以完整的单词形式进行匹配的,而不是n-gram。

search_analyzer 设置可以通过更新映射API在已存在的字段上进行更新。需要注意的是,为了这样做,任何现有的analyzer设置和type都需要在更新的字段定义中重复指定。

总结来说,search_analyzer提供了一种机制,可以在索引和搜索时对同一个字段使用不同的文本处理逻辑,从而优化特定应用场景下的搜索体验。

24.相关性(similarity)

Elasticsearch允许你为每个字段配置评分算法或相似性(similarity)。通过similarity设置,你可以选择不同于默认的BM25算法的相似性算法,比如传统的TF/IDF算法。

相似性设置主要用于文本字段,但也可以应用于其他类型的字段。用户可以通过调整内置相似性算法的参数来自定义相似性。有关这些高级选项的更多细节,可以参考相似性模块文档。

无需额外配置即可直接使用的相似性算法有:

  • BM25:Okapi BM25算法是Elasticsearch和Lucene中的默认算法。它是一种概率检索模型,考虑了词频(Term Frequency)和逆文档频率(Inverse Document Frequency),并且引入了文档长度归一化因子。
  • classic:自7.0.0版本起已被弃用。这是以前Elasticsearch和Lucene中的默认算法,基于传统的TF/IDF方法。
  • boolean:当不需要全文检索排名,并且评分仅基于查询词条是否匹配时使用的一种简单的布尔相似性。布尔相似性会给词条一个等于其查询提升权重(query boost)的评分。

相似性可以在创建字段时在字段级别设置,如下所示:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "default_field": { 
        "type": "text"
      },
      "boolean_sim_field": {
        "type": "text",
        "similarity": "boolean" 
      }
    }
  }
}

在这个例子中:

  • default_field 使用默认的BM25相似性。
  • boolean_sim_field 使用布尔相似性。

选择合适的相似性算法可以根据你的具体需求来优化搜索结果的相关性。如果你的应用场景要求更加精确地控制评分机制,那么调整相似性设置可能是一个重要的步骤。然而,对于大多数应用来说,默认的BM25算法已经提供了良好的性能和相关性。

25.单独存储(store)

在Elasticsearch中,默认情况下字段值会被索引以使其可被搜索,但它们不会被存储。这意味着你可以查询这些字段,但无法直接获取原始字段值。通常这并不会造成困扰,因为字段值已经是默认存储的_source字段的一部分。如果你只想检索单个字段或少数几个字段的值,而不是整个_source,那么可以通过源过滤(source filtering)来实现。

然而,在某些特定情况下,存储字段是有意义的。例如,如果你有一个包含标题、日期和一个非常大的内容字段的文档,你可能希望只检索标题和日期,而不必从一个大的_source字段中提取这些信息:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "store": true 
      },
      "date": {
        "type": "date",
        "store": true 
      },
      "content": {
        "type": "text"
      }
    }
  }
}

在这个映射配置中,titledate字段被设置为存储("store": true),而content字段则没有。

然后,我们可以添加一个文档到这个索引中:

PUT my-index-000001/_doc/1
{
  "title":   "Some short title",
  "date":    "2015-01-01",
  "content": "A very long content field..."
}

最后,我们可以通过指定stored_fields参数来检索存储的字段值:

GET my-index-000001/_search
{
  "stored_fields": [ "title", "date" ] 
}

此请求将仅返回titledate字段的值。

需要注意的是,为了保持一致性,存储的字段总是作为一个数组返回,因为系统无法知道原始字段值是一个单一值、多个值还是一个空数组。如果你需要原始值,应该从_source字段中获取它。

通过这种方式,可以优化查询性能,特别是当文档很大时,只加载必要的字段可以减少网络传输的数据量并加快响应时间。

26.术语向量(term_vector)

术语向量(term vectors)包含了由分析过程生成的词条的相关信息,包括:

  • 词条列表。
  • 每个词条的位置(或顺序)。
  • 映射词条到原始字符串中的起始和结束字符偏移量。
  • 如果有的话,负载(payloads)——与每个词条位置关联的用户定义的二进制数据。

这些术语向量可以被存储下来,以便日后为特定文档检索它们。term_vector设置接受以下值:

  • no:不存储任何术语向量。(默认)
  • yes:仅存储字段中的词条。
  • with_positions:存储词条及其位置。
  • with_offsets:存储词条及字符偏移量。
  • with_positions_offsets:存储词条、位置及字符偏移量。
  • with_positions_payloads:存储词条、位置及负载。
  • with_positions_offsets_payloads:存储词条、位置、偏移量及负载。

快速向量高亮显示(fast vector highlighter)需要with_positions_offsets选项。术语向量API可以根据存储的内容检索相应信息。

例如,如果你将term_vector设置为with_positions_offsets,那么该字段的索引大小可能会翻倍。下面是一个配置示例:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "text": {
        "type":        "text",
        "term_vector": "with_positions_offsets"
      }
    }
  }
}

接着,我们插入一个文档:

PUT my-index-000001/_doc/1
{
  "text": "Quick brown fox"
}

当你执行搜索并启用高亮显示时:

GET my-index-000001/_search
{
  "query": {
    "match": {
      "text": "brown fox"
    }
  },
  "highlight": {
    "fields": {
      "text": {} 
    }
  }
}

由于启用了术语向量,快速向量高亮显示将会默认用于text字段。这意味着Elasticsearch可以利用存储的术语位置和偏移量信息来更准确地高亮匹配的文本片段。

术语向量对于某些高级功能非常重要,比如近似查询、短语查询以及上述提到的高亮显示。但是,开启术语向量会增加索引的大小,并可能影响索引和搜索性能,因此应该根据实际需求谨慎使用。


http://www.kler.cn/a/610788.html

相关文章:

  • Ubuntu 24.04 LTS磁盘空间不足解决
  • Python爬虫:Feapder 的详细使用和案例
  • WRC世界机器人大会-2024年展商汇总
  • 解决PHP内存溢出问题的讨论和分析
  • LINUX基础IO [六] - 文件理解与操作
  • 第三天 开始Unity Shader的学习之旅之第二天的补充
  • 顺序表(C语言源码详解,附加测试代码)
  • el-table + el-pagination 前端实现分页操作
  • 16-CSS3新增选择器
  • SpringCloud 面试备战指南
  • Linux dma的使用与理解
  • [学成在线]07-视频转码
  • 极光优化PLO-Transformer-LSTM多变量时序
  • 不连续平面提取
  • deepseek(2)——deepseek 关键技术
  • 23中设计模式-迭代器(Iterator)设计模式
  • 【Git多分支使用教程】
  • Android 中 Activity 和 Fragment 的区别
  • C# 多标签浏览器 谷歌内核Csharp
  • 从底层原理到实际应用:BFS 算法借助队列征服迷宫