Elasticsearch 安装与简单使用

1.Search Engine

  • 目前主流的开源搜索引擎主要有两个,一个是基于Java的Apache Lucene,另一个是基于C++的Sphinx。在建立索引所需时间方面,Sphinx只需Lucene时间的50%左右,但是索引文件Sphinx比Lucene要大一倍,即Sphinx采用的是空间换时间的策略。在全文检索速度方面,二者相差不大。全文检索精确度方面,Lucene要优于Sphinx。在加入中文分词引擎的难易程度上,Lucene要优于Sphinx。Lucene的API接口设计的比较通用,输入输出结构都很像数据库的表、记录、字段,所以很多传统的应用的文件、数据库等都可以比较方便的映射到Lucene的存储结构/接口中。因此,选择Lucene作为全文搜索引擎是比较好的选择。

2.Search based on Lucene

  • Lucene只是一个框架,要充分利用它的功能,需要很高学习成本,很少直接直接使用Lucene,Solr和ElasticSearch都是基于Lucene的搜索服务器。ElasticSearch相比于solr拥有一些重要特征:
  • 1.导入性能更好,查询性能与solr持平,因为solr在建索引时会产生io的阻塞,造成搜索性能的下降,但ElasticSearch不会,它是先把索引的内容保存到内存之中,当内存不够时再把索引持久化到硬盘中,同时它还有一个队列,是在系统空闲时自动把索引写到硬盘中
  • 2.ElasticSearch 完全支持Apache Lucene的接近实时的搜索。
  • 3.支持更多的客户端库,如Java,Python,Javascript
  • 4.自包含集群,无需zookeeper即可自动构建集群
  • 5.支持多种插件:例如ElasticSearch-head插件、ik分词插件
  • 6.支持多个存储方式,ElasticSearch的数据文件可以存储在本地文件系统、Hadoop的HDFS和亚马孙的S3

3.ES Vs Relational DB

  • Relational DB--->库(Databases)--->表(Tables)--->行(Rows)--->列(columns)
  • ES--->索引(Indices)--->类型(Types)--->文档(Documents)--->字段(Fields)

4.ElasticSearch Install

  • 版本说明
    Java环境:JDK 1.8.0
    Elasticsearch:2.3.4
    OS环境:windows 7(为了开发调试方便)
  • 下载解压安装包
    下载地址:https://www.elastic.co/downloads/elasticsearch
    下载合适版本的安装包,当前最新的版本是5.0.1,Elasticsearch版本更新太快,直接从2.4.0到了5.0,由于5.x版本安装Elasticsearch-head插件过于复杂,选用的2.x版本即可
    解压后的目录如下
├─bin
├─config
├─lib
└─modules
    ├─lang-expression
    ├─lang-groovy
    └─reindex

单机Elasticsearch不需要修改过多的配置,就能直接使用,如果需要修改,修改config目录下Elasticsearch.yml文件即可,例如数据文件目录、日志文件目录、Elasticsearch服务器的ip和端口号以及JVM堆内存大小

  • 发布成服务
    讲ElasticSearch发布为服务,方便在后台运行和开机启动
    在elasticsearch-2.3.4\bin下执行
    service.bat install ElasticSearch
  • 启动后检查
    启动ElasticSearch服务后,访问http://localhost:9200/,提示服务是否就绪
{
    "name": "D'Ken",
    "cluster_name": "elasticsearch",
    "version": {
        "number": "2.3.4",
        "build_hash": "e455fd0c13dceca8dbbdbb1665d068ae55dabe3f",
        "build_timestamp": "2016-06-30T11:24:31Z",
        "build_snapshot": false,
        "lucene_version": "5.5.0"
    },
    "tagline": "You Know, for Search"
}

5.Basic Operator of ElasticSearch

  • 新建文档
curl -XPUT http://localhost:9200/blog/article/1 -d '{"titile": "New version of ElasticSearch released!", "content":"Version 1.0 released today!", "tags" : ["announce", "elasticsearch"," release"]}'

http://localhost:9200/blog/article/1,blog是index,article是indextype,1代表文档的id,在同一个index、同一个indextype下是唯一的。也可以不指定文档id,通过post方法新建文档,生成的文档id是随机的。如下所示:

curl -XPOST http://localhost:9200/blog/article -d '{"titile": "New version of ElasticSearch released!", "content":"Version 1.0 released today!", "tags" : ["announce", "elasticsearch","release"]}'
  • 检索文档
    已知文档id条件,检索文档
    curl -XGET http://localhost:9200/blog/article/1
    做全文搜索时,当type中的字段比较多,但是我们只需要某些字段时,可以选择结果集要返回的字段
curl -XGET http://localhost:9200/blog/article/_search –d 
‘{
    "fields": [
        "title",
        "author"
    ],
    "query": {
        "match_all": {}
    }
}’
  • 更新文档
    更新文档在ElasticSearch属于比较消耗的操作,需要将之前的建立的索引删掉,重建新的索引,ElasticSearch局部更新操作是通过Groovy脚步实现更新的,在更新之前,需要在elasticsearch.yml中添加
    script.inline: on
    script.indexed: on
    script.engine.groovy.inline.aggs: on
    重启ElasticSearch
    例如,需要根据用户的点击次数,更新某篇文章的浏览量
    POST /blog /article/1/_update
    {
    "script" : "ctx._source.read_count+=1"
    }
  • 删除文档
    删除文档命令
    curl -XDELETE http://localhost:9200/blog/article/1

6.Devlopment Process

  • 引入maven依赖
    在pom文件添加对应版本的依赖包
<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>2.3.4</version>
</dependency>
  • 创建mapping
    mapping是什么?为了能够把日期字段处理成日期,把数字字段处理成数字,把字符串字段处理成全文本(Full-text)或精确的字符串值,Elasticsearch需要知道每个字段里面都包含了什么类型。这些类型和字段的信息存储(包含)在映射(mapping)中
  • 为什么要创建mapping
    虽然Elasticsearch能更够根据数据类型创建自动创建mapping,但是自动创建mapping很可能不符合要求,例如我们想对指定的字段指定分词器
    查看mapping命令
curl  -XGET http://localhost:9200/blog/article/_mapping/

或者通过head插件查看

结果如何

{
    "blog": {
        "mappings": {
            "article": {
                "_ttl": {
                    "enabled": false
                },
                "properties": {
                    "author": {
                        "type": "string",
                        "index": "not_analyzed"
                    },
                    "content": {
                        "type": "string",
                        " analyzer ": "ik "
                    },
                    "publish_time": {
                        "type": "date",
                        "format": "strict_date_optional_time||epoch_millis"
                    },
                    "read_count": {
                        "type": "integer"
                    },
                    "title": {
                        "type": "string",
                        "analyzer": "ik"
                    }
                }
            }
        }
    }
}

主要需要指定字段type类型,对于string类型,需要指定是否需要分词,以及分词器的类型

public class BlogMapping {
    public static XContentBuilder getMapping(){
        XContentBuilder mapping = null;
        try {
            mapping = jsonBuilder()
                    .startObject()
                    //开启倒计时功能
                    .startObject("_ttl")
                    .field("enabled",false)
                    .endObject()
                    .startObject("properties")
                    .startObject("title")
                    .field("type","string")
                    .field("analyzer", "ik")
                    .endObject()
                    .startObject("content")
                    .field("type","string")
                    .field("index","not_analyzed")
                    .endObject()
                    .startObject("author")
                    .field("type","string")
                    .field("index","not_analyzed")
                    .endObject()
                    .startObject("publish_time")
                    .field("type","date")
                    .field("index","not_analyzed")
                    .endObject()
                    .startObject("read_count")
                    .field("type","integer")
                    .field("index","not_analyzed")
                    .endObject()
                    .endObject()
                    .endObject();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return mapping;
    }
}
  • 分析分词结果
    查看分词结果
curl -XGET 'localhost:9200/_analyze' -d '
{
  "analyzer": "ik",
  "text": "百度张亚勤:ABC时代来了,迎战云计算“马拉松”"
}

命令行下官方推荐使用get方法,使用post方法也是可以的,而且命令行环境下对汉字的支持不是很有友好,可以通过head插件或者其他restclient图形界面工具查询分词结果,通过图形界面工具查询务必使用post方法

  • 分词结果如下:
    [百度,百,度,张,亚,勤,abc,时代,来了,迎战,战云,计算,马拉松,马,拉,松]
    分词结果直接决定了,我们应该采用精确匹配还是全文检索,以及我们搜索的关键词能否命中
  • 创建DSL语句
    Elasticsearch检索文件是通过DSL语句,检索文档分为两种分式精确查询和全文检索,在Elasticsearch里面分别对应term query和match query
    term query:
    在给定的字段里查询词或者词组;
    提供的查询词是不分词的(not analyzed),即只有完全包含才算匹配;
    支持boost属性,boost可以提高field和document的相关性;
    match query:
    与term query不同,match query的查询词是被分词处理的(analyzed),即首先分词,然后构造相应的查询,所以应该确保查询的分词器和索引的分词器是一致的;
    与terms query相似,提供的查询词之间默认是or的关系,可以通过operator属性指定;
    match query有两种形式,一种是简单形式,一种是bool形式;
    希望通过[百度,张亚勤,云计算,马拉松]中任意一个搜索词都能够搜到上面的内容,即百度张亚勤:ABC时代来了,迎战云计算“马拉松”
    如果使用term query
GET http://localhost:9200/blog/article/_search
{
  "query": {
    "term": {
      "title": "百度"
    }
  }
}

当title的值为“百度”或者“马拉松”时可以检索到内容,当title为“张亚勤或者云计算时,无法检索到文档,因为分词结果是[百度,百,度,张,亚,勤,abc,时代,来了,迎战,战云,计算,马拉松,马,拉,松],无法匹配“张亚勤”和“云计算”
如果使用match query检索

{
  "query": {
    "match": {
      "title": {
        "query": "张亚勤",
        "operator": "and",
        "minimum_should_match": "3"
      }
    }
  }
}

使用match query检索,ik分词器会把张亚勤分[张,亚,勤],会把三个元素依次和[百度,百,度,张,亚,勤,abc,时代,来了,迎战,战云,计算,马拉松,马,拉,松]里面的数据比对,operator=and,表示每个分词比较结果是与的关系,minimum_should_match=3,表示至少三次匹配成功,此时就能检索到内容
multi_match
如果我们希望两个字段进行匹配,其中一个字段有这个文档就满足的话,使用multi_match,用户输入一个搜索词,我们很少根据有一个字段进行索引,通常是根据某几个字段进行索引,例如我们想根据title或者content字段搜包含“云计算”的内容,那么相应DSL语句:

{
  "query": {
    "multi_match": {
        "query" : "云计算",
        "fields" : ["title", "content"]
    }
  }
}
  • 理解评分机制
    通过DSL语句只是查询到我们需要的结果,但是怎么对查询到的结果进行排序,就需要了解ElasticSearch的评分机制,ElasticSearch默认是安装查询文档的评分排序的。
    详细查看评分的命令
POST http://localhost:9200/blog/article/_search?explain
{
  "query": {
    "match": {
      "title": {
        "query": "罗一笑",
        "operator": "and",
        "minimum_should_match": "3"
      }
    }
  }
}

搜索词:罗一笑
查询时将搜索词分为4个分词(term),罗,一笑,一,笑
每个词有一个评分
罗:0.042223614
一笑:0.11945401
一:0.11945401
笑:0.11945401
根据罗一笑查询到文档的总评分为:0.40058565=0.042223614+0.11945401+0.11945401+0.11945401
对于每一个term的评分规则是:term评分= queryweight * fieldweight
一笑的评分:0.11945401 = 0.54607546 * 0.21875
查询权重query weight = idf * queryNorm

{
    "value": 0.54607546,
    "description": "queryWeight, product of:",
    "details": [
        {
            "value": 1,
            "description": "idf(docFreq=1, maxDocs=2)",
            "details": []
        },
        {
            "value": 0.54607546,
            "description": "queryNorm",
            "details": []
        }
    ]
}

域权重fieldweight= tf * idf * fieldNorm

{
    "value": 0.21875,
    "description": "fieldWeight in 0, product of:",
    "details": [
        {
            "value": 1,
            "description": "tf(freq=1.0), with freq of:",
            "details": [
                {
                    "value": 1,
                    "description": "termFreq=1.0",
                    "details": []
                }
            ]
        },
        {
            "value": 1,
            "description": "idf(docFreq=1, maxDocs=2)",
            "details": []
        },
        {
            "value": 0.21875,
            "description": "fieldNorm(doc=0)",
            "details": []
        }
    ]
}

主要有个两个概念Term Frequency和Inverse document frequency
Term Frequency:某单个关键词(term) 在某文档的某字段中出现的频率次数, 显然, 出现频率越高意味着该文档与搜索的相关度也越高
具体计算公式是 tf(q in d) = sqrt(termFreq)
Inverse document frequency:某个关键词(term) 在索引(单个分片)之中出现的频次. 出现频次越高, 这个词的相关度越低. 相对的, 当某个关键词(term)在一大票的文档下面都有出现, 那么这个词在计算得分时候所占的比重就要比那些只在少部分文档出现的词所占的得分比重要低. 说的那么长一句话, 用人话来描述就是 “物以稀为贵”。
具体计算公式:
idf = 1 + ln(maxDocs/(docFreq + 1))
详细计算公式请参考lucene源码或者参考http://www.hankcs.com/program/java/lucene-scoring-algorithm-explained.html
lucene的评分机制过于复杂,我们可以根据上述公式派生出简单容易理解的规则
匹配的词条越罕见,文档的得分越高
文档的字段越小,文档的得分越高
字段的加权越高,文档的得分越高

官方指导文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容

  • Solr&ElasticSearch原理及应用 一、综述 搜索 http://baike.baidu.com/it...
    楼外楼V阅读 7,254评论 1 17
  • 欢迎访问本人博客:http://wangnan.tech Elasticsearch是一个可伸缩的开源全文搜索和分...
    GhostStories阅读 3,086评论 0 10
  • 昨天,我在公众号里发布了一个讲述你与母亲故事的活动,我从中选择了一篇很喜欢的与大家分享一下: 我是一个自私的女儿,...
    CodyYang阅读 299评论 0 0
  • 12.22.2016 预约的下午两点手术 妈妈和老公陪我去的医院。手术前一个小时我吃了一颗止疼药。到了医院chec...
    dingding001阅读 846评论 0 0
  • 突然想起一个人来,小时候那个因为欺负我被老师当着全班同学的面打了一个耳光的班长。 从五年级一直到碰见他,一直都没有...
    寒氷阅读 1,036评论 0 0