Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Elasticsearch 学习笔记 #1

Open
copie opened this issue Sep 18, 2018 · 0 comments
Open

Elasticsearch 学习笔记 #1

copie opened this issue Sep 18, 2018 · 0 comments
Assignees
Labels
ElasticSearch ElasticSearch

Comments

@copie
Copy link
Owner

copie commented Sep 18, 2018

Elasticsearch 学习笔记

路由一个文档到一个分片中

shard = hash(routing) % number_of_primary_shards

routing 通常是id
number_of_primary_shards 主分片的数目

主分片一般不可以扩展,不通过手段的话

一个自定义的路由参数可以用来确保所有相关的文档——例如所有属于同一个用户的文档——都被存储到同一个分片中

主分片和副本分片如何交互

每个节点都知道任意一个文档的位置,他可以将请求进行转发,
当发送请求的时候, 为了扩展负载,更好的做法是轮询集群中所有的节点。
? 我思考这里使用 nginx 进行负载均衡岂不是很好

新建、索引和删除文档

新建、索引和删除 是写操作必须在主分片上操作,然后复制到副本分片
具体过程是:

请求 -> 转发 —> 索引 -> 所有的副分片与主分片同步 -> 返回结果给转发的节点 -> 返回给客户端

取回单个文档

请求 -> 轮询转发 -> 查询 -> 返回结果给转发的节点 -> 返回给客户端

如果建立索引返回了 那么 主分片 和 副分片的数据是一致的。

局部更新文档

请求 -> 转发 —> 修改(如果有别的进程修改会在有限次数内重试)(重新建立索引) -> 所有的副分片与主分片同步 -> 返回结果给转发的节点 -> 返回给客户端

同步的时候用的是一个文档的全部,因为这个同步是异步的如果只同步修改可能会导致问题

多文档模式

mget

请求 —> 拆分 -> 转发 -> 处理 -> 搜集 -> 返回结果

bulk

请求 -> 拆分/组和 —> 转发 -> 修改 -> 同步 -> 搜集结果 -> 返回给客户端

bulk 对于格式要求严格 是性能上面的考虑

多索引, 多类型

/_search
在所有的索引中搜索所有的类型

/gb/_search
在 gb 索引中搜索所有的类型

/gb,us/_search
在 gb 和 us 索引中搜索所有的文档

/g*,u*/_search
在任何以 g 或者 u 开头的索引中搜索所有的类型

/gb/user/_search
在 gb 索引中搜索 user 类型

/gb,us/user,tweet/_search
在 gb 和 us 索引中搜索 user 和 tweet 类型

/_all/user,tweet/_search
在所有的索引中搜索 user 和 tweet 类型

映射和分析

_all 字段是 string
如果 Elasticsearch 猜测到 一个字段是 data 类型你用 str 的方式搜索会导致搜索不到结果。

精确值 VS 全文

精确值就像 sql 查询那样 是就是是,不是就是不是。
全文的话就是说获取到文档和查询的匹配度。
全文搜索我们更加希望获得更多相关的信息

倒排索引

所谓的倒排索引就是将所有的文档进行分词然后对每个词和所对应的文档建立索引。
类似与下面这个样子

Term      Doc_1  Doc_2
-------------------------
Quick   |       |  X
The     |   X   |
brown   |   X   |  X
dog     |   X   |
dogs    |       |  X
fox     |   X   |
foxes   |       |  X
in      |       |  X
jumped  |   X   |
lazy    |   X   |  X
leap    |       |  X
over    |   X   |  X
quick   |   X   |
summer  |       |  X
the     |   X   |
------------------------

如果对同义词和相同词根的词进行合并的话

Term      Doc_1  Doc_2
-------------------------
brown   |   X   |  X
dog     |   X   |  X
fox     |   X   |  X
in      |       |  X
jump    |   X   |  X
lazy    |   X   |  X
over    |   X   |  X
quick   |   X   |  X
summer  |       |  X
the     |   X   |  X
------------------------

如果我们对用户的搜索词也采用这种方法进行统一就可以快速的获得结果。

分词和标准化叫做分析

复杂核心域类型

多值域

{ "tag": [ "search", "nosql" ]}

类型必须相同, 自动确定类型为第一个, 保存没有顺序

空域

Lucene 中不能存储 null 值

"null_value":               null,
"empty_array":              [],
"array_with_null_value":    [ null ]

都是就空域不会被索引

多层级对象

一个对象内可以有其他对象

{
    "tweet":            "Elasticsearch is very flexible",
    "user": {
        "id":           "@johnsmith",
        "gender":       "male",
        "age":          26,
        "name": {
            "full":     "John Smith",
            "first":    "John",
            "last":     "Smith"
        }
    }
}

内部对象映射

一个内部对象的 type 是 object。
其他都是一样

内部对象索引

{
    "tweet":            [elasticsearch, flexible, very],
    "user.id":          [@johnsmith],
    "user.gender":      [male],
    "user.age":         [26],
    "user.name.full":   [john, smith],
    "user.name.first":  [john],
    "user.name.last":   [smith]
}

可以通过 . 来引用比如:

tweet.user.name.first

索引建立不会有复杂的结构

内部对象数组

会丢失对象内部数据的关联性
使用 嵌套对象解决

{
    "followers": [
        { "age": 35, "name": "Mary White"},
        { "age": 26, "name": "Alex Jones"},
        { "age": 19, "name": "Lisa Smith"}
    ]
}

会被解释为:

{
    "followers.age":    [19, 26, 35],
    "followers.name":   [alex, jones, lisa, smith, mary, white]
}

查询

  • 查看所有的 index

    curl -X GET "http://localhost:9200/_cat/indices?v"
    
  • 查看一个 index 的信息

    curl -X GET "http://localhost:9200/_cat/indices/tests"
    
  • 删除一个索引

    curl -X DELETE  "http://127.0.0.1:9200/tests"
    
  • 查看 tests 索引下的 test 类型下的所有数据

    curl -X GET "http://127.0.0.1:9200/tests/test/_search"
    
  • 查看 id 是 1 的数据

    curl -X GET "http://127.0.0.1:9200/tests/test/1"
    
  • 只查看部分数据

    curl -X GET "http://127.0.0.1:9200/tests/test/1?_source=a,b"
    
  • 不显示 元数据

    curl -X GET "http://127.0.0.1:9200/tets/test/1/_source"
    
  • 检查文档是否存在

    curl -X HEAD "http://localhost:9200/tests/test/1"
    
  • 更新文档 文档不可改变 所以所谓的更新就行进行重新创建

    curl -X PUT "http://localhost:9200/megacorp/employee/1" -H 'Content-Type: application/json' -d'
    {
        "first_name": "John",
        "last_name": "Smith",
        "age": 25,
        "about": "I love to go rock climbing 萝卜青菜,各有所爱",
        "interests": [
            "sports",
            "music"
        ]
    }
    '
    
  • 创建文档

    // 创建一个文档如果 _id 已经存在创建失败 201/409 op_type=create
    // curl -X PUT "http://localhost:9200/megacorp/employee/4?op_type=create"
    // 也可以完成相同的工作
    
    curl -X PUT "http://localhost:9200/megacorp/employee/5/_create" -H 'Content-Type: application/json' -d'
    {
        "first_name": "John jj",
        "last_name": "Smith kk",
        "age": 90,
        "about": "I love to go rock climbing 萝卜青菜,各有所爱",
        "interests": [
            "sports",
            "music",
            "sf"
        ]
    }
    '
    
  • 删除文档

    curl -X DELETE  "http://localhost:9200/megacorp/employee/5" -H 'Content-Type: application/json'
    
  • 更新文档

    // 更新文档 使用 version 进行乐观的并发控制
    // 409 存的版本不同于提供的版本
    // 200 成功
    
    curl -X PUT "http://localhost:9200/megacorp/employee/1?version=9" -H 'Content-Type: application/json' -d'
    {
        "first_name": "John",
        "last_name": "Smith",
        "age": 25,
        "about": "I love to go rock climbing 萝卜青菜,各有所爱",
        "interests": [
            "sports",
            "music"
        ]
    }
    '
    
    // 外部版本号控制并发
    // 可以提供比现在版本号高的版本号不可以低
    // 409/200
    curl -X PUT "http://localhost:9200/megacorp/employee/1?version=14&version_type=external" -H 'Content-Type: application/json' -d'
    {
        "first_name": "John",
        "last_name": "Smith",
        "age": 25,
        "about": "I love to go rock climbing 萝卜青菜,各有所爱",
        "interests": [
            "sports",
            "music"
        ]
    }
    
    curl -X POST "http://localhost:9200/megacorp/employee/1/_update" -H 'Content-Type: application/json' -d'
    {
        "doc":{
            "age":26
        }
    }
    
  • 使用脚本更新文档

    curl -X POST "http://localhost:9200/megacorp/employee/1/_update" -H 'Content-Type: application/json' -d'
    {
        "script":"ctx._source.age+=1"
    }
    
  • 通过脚本给数组添加数据

    curl -X POST "http://localhost:9200/megacorp/employee/1/_update" -H 'Content-Type: application/json' -d'
    {
        "script" : {
            "inline" : "ctx._source.interests.add(params.new_interest)",
            "params" : {
                        "new_interest" : "search"
                }
        }
    
    }
    '
    
  • 批量获得多个文档, 相应数据和请求数据顺序相同

    curl -X GET "http://localhost:9200/_mget" -H 'Content-Type: application/json' -d'
    {
        "docs" : [
            {
                "_index" : "megacorp",
                "_type" :  "employee",
                "_id" :    2
    
            },
            {
                "_index" : "megacorp",
                "_type" :  "employee",
                "_id" :    1,
                "_source": ["age","about"]
            }
        ]
    }
    '
    
  • 在url中给出 _index 或者 _type, url中给定的可以被覆盖

    curl -X POST "http://localhost:9200/megacorp/employee/_mget" -H 'Content-Type: application/json' -d'
    {
        "docs":[
            {"_id":1},
            {"_id":2},
            {"_id":3},
            {
                "_id":4,
                "_index" : "megacorp",
                "_type" :  "employee"
            }
        ]
    }
    '
    
  • _index 和 _type 相同 而且只请求 id 的话直接使用 ids, 如果指定的 _id 不存在的话 found=false

    curl -X POST "http://localhost:9200/megacorp/employee/_mget" -H 'Content-Type: application/json' -d'
    {
        "ids":["1",2,3,"88"]
    }
    
  • 代价小的批量操作

    // 代价小的批量操作
    // { action: { metadata }}\n
    // { request body        }\n
    // { action: { metadata }}\n
    // { request body        }\n
    // delete 不需要 request body
    // create 只有不存在才会成功
    // index 创建新的或者是修改
    // update 脚本部分更新
    // http 状态码是 200
    // 具体的每个操作的状态码是各自的。
    // 该操作不具有有原子性
    curl -X POST "http://localhost:9200/_bulk" -H 'Content-Type: application/json' -d'
    { "delete": { "_index": "megacorp", "_type": "employee", "_id": "1" }}
    { "create": { "_index": "megacorp", "_type": "employee", "_id": "1" }}
    { "first_name": "你好啊","last_name": "我不好","age": 73,"about": "他是一个73岁的老人", "interests": ["玩游戏","LOL"]}
    '
    // 不用重复的指定 _index, _type
    // 也可以覆盖
    // 具体多大就要看数据的大小,和机器性能了
    // 一个好的办法是开始时将 1,000 到 5,000 个文档作为一个批次
    curl -X POST "http://localhost:9200/megacorp/employee/_bulk" -H 'Content-Type: application/json' -d'
    { "delete": { "_id": "1" }}
    { "create": { "_id": "1" }}
    { "first_name": "你好啊","last_name": "我不好","age": 73,"about": "他是一个73岁的老人", "interests": ["玩游戏","LOL"]}
    '
    
  • 空搜索

    // 空搜索
    // hits.total 匹配到的文档总数
    // hits._score 文档与查询的匹配程度 默认降序
    // max_score 所有 hits._score 的最大值
    // took 搜索请求耗费了多少毫秒
    // shards 各个分片的状态, 灾难级别的故障,可能丢失部分分片,返回部分能够查到的数据并且报告失败  
    // 可以指定超时,超时前会返回给客户端部分或全部结果,其他分片继续查询,虽然结果已经返回了。
    curl -X GET "http://localhost:9200/_search?timeout=1ms"
    
  • 分页

    // size
    //      显示应该返回的结果数量,默认是 10
    // from
    //      显示应该跳过的初始结果数量,默认是 0
    // 分页深度问题,获得分页深度越大,需要消耗的性能越多,而且是指数型的。
    // 因为查询需要获取多个分片的结果。
    // 理解为什么深度分页是有问题的,我们可以假设在一个有 5 个主分片的索引中搜索。 当我们请求结果的第一页(结果从 1 到 10 ),每一个分片产生前 10 的结果,并且返回给 协调节点 ,协调节点对 50 个结果排序得到全部结果的前 10 个。
    // 现在假设我们请求第 1000 页--结果从 10001 到 10010 。所有都以相同的方式工作除了每个分片不得不产生前10010个结果以外。 然后协调节点对全部 50050 个结果排序最后丢弃掉这些结果中的 50040 个结果。
    // 可以看到,在分布式系统中,对结果排序的成本随分页的深度成指数上升。这就是 web 搜索引擎对任何查询都不要返回超过 1000 个结果的原因。
    
    curl -X GET "http://localhost:9200/_search?size=5&from=10"
    
    • 前缀表示必须与查询条件匹配。类似地, - 前缀表示一定不与查询条件匹配
      curl -X GET "http://localhost:9200/_search?q=+age:25 -last_name:Smith"
      
  • 建立一个 mapping

    curl -X POST http://localhost:9200/index/fulltext/_mapping -H 'Content-Type:application/json' -d'
    {
            "properties": {
                "content": {
                    "type": "text",
                    "analyzer": "ik_max_word",
                    "search_analyzer": "ik_max_word"
                }
            }
    
    }'
    
  • 查看 mapping

    curl -X GET http://localhost:9200/index/fulltext/_mapping
    
  • 打开一个索引

    curl -XPOST http://localhost:9200/index/_open
    
  • 查看分词器的效果

    curl -XGET "http://localhost:9200/_analyze" -H 'Content-Type: application/json' -d'
    {
        "text":"百度一下你就知道","tokenizer": "ik_max_word"
    }'
    
  • 新建一个索引,并且指定一个type中的一个域的分析器

    // 不要将 type 设置为 string 因为它被废弃了,可以使用 text or keyword 
    curl -X PUT "http://localhost:9200/index" -H 'Content-Type: application/json' -d'
    {
        "mappings": {
        "fulltext" : {
            "properties" : {
            "content" : {
                "type" :    "text",
                "analyzer": "ik_max_word",
                "search_analyzer": "ik_max_word"
            },
            "date" : {
                "type" :   "date"
            },
            "name" : {
                "type" :   "keyword",
                "index": false
            },
            "user_id" : {
                "type" :   "long"
            }
            }
        }
        }
    }
    '
    
  • 获取一个 type 的 mapping

    curl -X GET "http://localhost:9200/products/_mapping/product"
    
  • 不可以直接修改一个type的 mapping

    // 不可以直接修改一个type的 mapping
    // 因为如果修改了类型,以前的索引全部都失败了
    // 但是可以添加一个字段,并设置类型
    // 如果想要修改的话需要用到  **Reindex API**
    curl -X PUT "http://localhost:9200/index/_mapping/fulltext" -H 'Content-Type: application/json' -d'
    {
        "properties" : {
            "tag" : {
            "type" :    "long",
            "index":    false
            }
        }
        }
    '
    
  • 匹配所有文档

    curl -X GET "http://localhost:9200/_search?pretty" -H 'Content-Type: application/json' -d'
    {
        "query": {
            "match_all": {}
        }
    }
    '
    
  • 简单的组合查询

    // 简单的组合查询
    // must, must_not, should, filter
    curl -X GET "http://localhost:9200/_search?pretty" -H 'Content-Type: application/json' -d'
    {
        "query":{
        "bool":{
            "must":[{"match":{"tn":"百度一下"}},{"match":{"cn":"你就知道"}}]
        }
        }
    
    }
    '
    
  • 验证查询

    curl -X GET "http://localhost:9200/_validate/query?explain" -H 'Content-Type: application/json' -d'
    {
        "query": {
            "bool": {
                "must": [
                    {
                        "multi_match": {
                            "query": "百度一下,你就知道",
                            "fields": ["a", "b", "c"],
                            "type": "cross_fields",
                            "minimum_should_match": "75%"
                        }
    
                    }
                ],
                "should": [
                    {
                        "multi_match": {
                            "query": "百度一下,你就知道",
                            "fields": ["a", "b", "c"],
                            "boost": 0.4,
                            "analyzer": "ik_smart",
                            "minimum_should_match": "100%"
                        }
                    }
                ]
            }
        }
    }
    '
    
  • 聚合 提取其他数据

    curl -X GET "http://localhost:9200/c2h4/tests/_search?pretty" -H 'Content-Type: application/json' -d'
    {
        "query":{
            "bool":{
                "must":{ 
                        "multi_match": { 
                            "query": "百度一下, 你就知道", 
                            "fields":   [ "a", "b","c" ],
                            "minimum_should_match": "85%"
                            
                        } 
                }
            }
        },
        "aggregations":{
            "name" : {
                "terms" : { 
                    "field" : "x"
                },
                "aggregations":{
                    "types":{
                        "top_hits":{
                            "size":1,
                            "_source" : ["a","c"]
                        }
                    }
                }
            }
        },
        "size":0
    }
    '
    
  • 自定义评分

    curl -X GET "http://localhost:9200/_search?pretty" -H 'Content-Type: application/json' -d'
    {
        "query": {
            "function_score": {
                "query": {
                    "match": { "a": "百度一下" }
                },
                "script_score" : {
                    "script" : {
                        "source": "0.8*Math.log(1+doc[\u0027ppp\u0027].value) + Math.log(1+doc[\u0027rrr\u0027].value)+doc[\u0027aaa\u0027].value"
                    }
                }
            }
        }
    }
    '
    
  • 自定义评分指定计算

    // 这个例子会在 原来的评分基础上面加 100
    // 具体如何运算结合上面的那个例子一起用
    curl -X GET "http://localhost:9200/_search?pretty&&explain" -H 'Content-Type: application/json' -d'
    {
        "query":{
            "function_score": {
                    "query": {
                        "bool": {
                            "must": [
                                {
                                    "multi_match": {
                                        "query": "百度一下, 你就知道",
                                        "fields": ["a", "n", "ne"],
                                        "type": "cross_fields",
                                        "minimum_should_match": "85%",
                                        "analyzer": "ik_max_word"
                                    }
    
                                }
                            ],
                            "should": [
                                {
                                    "multi_match": {
                                        "query": "百度一下,你就知道",
                                        "fields": ["bn", "tn", "bne"],
                                        "boost": 1.4,
                                        "analyzer": "ik_smart",
                                        "type": "most_fields"
                                    }
                                }
                            ]
                        }
                    },
                    "script_score":{
                        "script":{
                            "source":"100"
                        }
                    },
                    "boost_mode":"sum"
            }
        }    
    }
    '
    
  • 过滤非空排序

    curl -X GET "http://localhost:9200/_search?pretty" -H 'Content-Type: application/json' -d'
    {
        "query": {
            "bool": {
                "must": [
                    {
                        "multi_match": {
                            "query": "百度一下,你就知道",
                            "fields": ["a", "n", "ne"],
                            "type": "cross_fields",
                            "minimum_should_match": "75%",
                            "analyzer": "ik_max_word"
                        }
    
                    },
                    {"exists" : { "field" : "p" }}
                ]
            }
        },
        "sort":[{"p":"asc"}, "_score"],
        "min_score": 10
    }
    '
    
  • 自定义字段的权重

    curl -X GET "http://localhost:9200/tests/test/_search?pretty" -H 'Content-Type: application/json' -d'
    {
        "query": {
            "bool": {
                "must": [{
                    "multi_match": {
                        "query": "百度一下,你就知道",
                        "fields": ["t^20", "s", "c^1","a^10"],
                        "minimum_should_match": "75%"
                    }
                }]
            }
        },
        "from": 0,
        "size": 15
    }
    '
    
  • 使用 painless 脚本进行排序

curl -X GET "http://localhost:9200/tests/_search?pretty" -H 'Content-Type: application/json' -d'
{
    "query": {
        "bool": {
            "must": [
                {
                    "multi_match": {
                        "query": "百度一下, 你就知道",
                        "fields": ["a", "n", "ne"],
                        "type": "cross_fields",
                        "minimum_should_match": "2<85%"
                    }
                }
            ]
    },
    "_source": ["u","p"],
    "sort": {
        "_script":{
            "type":"number",
            "script":{
                "lang": "painless",
                "source":"if(doc[\u0027p\u0027].value > 0){return doc[\u0027p\u0027].value-params.factor;}else{return 0 ;}",
                "params" : {
                    "factor" : 9999999
                }
            },
            "order" : "asc"
        }
    },
    "size":1000
}
'
  • 同义词的使用
    curl -X PUT "http://localhost:9200/tests" -H 'Content-Type: application/json' -d'
    {
        "settings": {
            "index":{
                "max_result_window" : 90,
            "analysis" : {
                "analyzer" : {
                    "synonym" : {
                        "tokenizer" : "ik_max_word",
                        "filter" : ["synonym"]
                    }
                },
                "filter" : {
                    "synonym" : {
                        "type" : "synonym",
                        "synonyms_path" : "analysis/synonym.txt"
                    }
                }
            }
            }
        },
        "mappings": {
            "test": {
                "properties": {
                    "n": {
                        "type": "text",
                        "analyzer": "ik_max_word",
                        "search_analyzer": "synonym"
                    }
                }
            }
        }
    }
    '
  • 繁体字统一转换为简体字
curl -X PUT "http://localhost:9200/ps" -H 'Content-Type: application/json' -d'
{
        "settings": {
            "index":{
                "max_result_window": 600,
                "analysis": {
                    "analyzer": {
                        "ik_max_word":{
                            "tokenizer": "ik_max_word",
                            "char_filter": ["tsconvert"]
                        }
                    },
                    "tokenizer" : {
                        "tsconvert" : {
                            "type" : "stconvert",
                            "convert_type" : "t2s"
                        }
                    },
                    "char_filter" : {
                        "tsconvert" : {
                            "type" : "stconvert",
                            "convert_type" : "t2s"
                        }
                    }
                }
        }
    },
    "mappings": {
        "p": {
            "properties": {
                "name": {
                    "type": "text",
                    "analyzer": "ik_max_word",
                    "search_analyzer": "ik_max_word"
                }
            }
        }
    }
}
'
  • 过滤单字
curl -X PUT "http://localhost:9200/ps" -H 'Content-Type: application/json' -d'
{
    "settings": {
        "index":{
            "max_result_window": 600,
            "analysis": {
                "analyzer": {
                    "ik_max_word":{
                        "tokenizer": "ik_max_word",
                        "char_filter": ["tsconvert"]
                    },
                    "synonym": {
                        "tokenizer": "ik_max_word",
                        "filter": ["synonym"],
                        "char_filter": ["tsconvert"]
                    },
                    "synonym_no_one":{
                        "tokenizer": "ik_max_word",
                        "filter": ["synonym", "no_one"],
                        "char_filter": ["tsconvert"]
                    }
                },
                "filter": {
                    "synonym": {
                        "type": "synonym",
                        "synonyms_path": "analysis/synonym.txt"
                    },
                    "no_one": {
                        "type": "length",
                        "min": 2
                    }
                },
                "tokenizer" : {
                    "tsconvert" : {
                        "type" : "stconvert",
                        "convert_type" : "t2s"
                    }
                },
                "char_filter" : {
                    "tsconvert" : {
                        "type" : "stconvert",
                        "convert_type" : "t2s"
                    }
                }
            }
        }
    },
    "mappings": {
        "pt": {
            "properties": {
                "name": {
                    "type": "text",
                    "analyzer": "ik_max_word",
                    "search_analyzer": "synonym"
                }
            }
        }
    }
}'
@copie copie added the ElasticSearch ElasticSearch label Sep 18, 2018
@copie copie self-assigned this Sep 18, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ElasticSearch ElasticSearch
Projects
None yet
Development

No branches or pull requests

1 participant