精华内容
下载资源
问答
  • word高亮显示重复项
    千次阅读
    2017-11-24 16:50:13

    以下内容来自于此网页,防止过期,搬运过来:


    1、下载地址:

     WordLight for 2008

    下载网址:http://visualstudiogallery.msdn.microsoft.com/ad686131-47d4-4c13-ada2-5b1a9019fb6f

    Highlight all occurrences of selected word  for 2010

    2、解压之后,将解压的文件夹放到

    "C:\Users\%UserName%\Documents\Visual Studio 2008" (for Win7) 

    or "C:\Documents and Settings\%UserName%\My Documents\Visual Studio 2008\Addins" (for WinXP).

     

    3、然后打开VS,工具-选项-环境-外界程序/宏的安全性(英文对照:Tools > Options > Environment > Add-in/Macros Security)

    点击 ‘添加’,将刚才拷贝到vs下的文件夹添加上,确定,然后重新启动VS2008

     

    4、任意选中一个变量,呵呵,有效果了,只是这个是矩形框,不过也可以,只要达到效果就行

     

    5、另外,重新找到 工具,发现下边出现了一个新的项——WordLight setting...在里边可以设置颜色


    更多相关内容
  • 1选择高亮的颜色 2 选择替换,更多 3 选择格式,突出显示 注意替换为后面有突出显示才能正确突出显示,全部替换就行

     

     

    1选择高亮的颜色

    2  选择替换,更多

    3

    选择格式,突出显示

    注意替换为后面有突出显示才能正确突出显示,全部替换就行

     

     

    展开全文
  • 山东专升本计算机Word知识点总结.doc
  • 山东专升本计算机Word知识点总结试卷教案.doc
  • Elasticsearch查询——highlight(高亮显示

    万次阅读 多人点赞 2019-12-24 21:52:02
    Highlighters 高亮显示 日常生活中我们使用搜索工具尝试查询一些信息的时候,常常可以看到返回的结果集中和我们查询条件相符合的字段被特殊的颜色所标记,这就是结果高亮显示。通过高亮显示用户可以明显的发现查询...

    Highlighters 高亮显示

    日常生活中我们使用搜索工具尝试查询一些信息的时候,常常可以看到返回的结果集中和我们查询条件相符合的字段被特殊的颜色所标记,这就是结果高亮显示。通过高亮显示用户可以明显的发现查询匹配的位置,

    ES使用highlight来实现搜索结果中一个或多个字段突出显示。

    高亮显示需要字段中的内容,如果没有存储字段store=true,则加载实际的_source并从_source提取相关字段。

    简单的例子

    下面是一个简单的例子,使用的映射结构

    PUT article/_mapping
    {
        "properties": {
            "article_type": {
                "type": "keyword"
            },
            "article_title": {
                "type": "text"
            },
            "content": {
                "type": "text"
            },
            "date": {
                "type": "date",
                "format": ["yyyy-MM-dd HH:mm:ss"]
            },
            "read_num": {
                "type": "integer"
            },
            "comment_num": {
                "type": "integer"
            }
        }
    }
    

    数据内容就是本人的博客信息

    {"index":{"_index":"article","_id":0}}
    {"article_type": "原创","article_title": "Elasticsearch查询——Profile API(性能分析)","content": "Profile API 性能分析 平时开发的过程中我们可能需要对一些查询操作进行优化,而优化之前的工作就是要对操作的性能进行分析,而ES提供了Profile API来帮助用户进行性能分析。它让用户了解如何在较低的级别执行搜索请求,这样用户就可以理解为什么某些请求比较慢,并采取措施改进它们。","date": "2019-12-22 22:22:29","read_num": 4,"comment_num": 0}
    {"index":{"_index":"article","_id":1}}
    {"article_type": "原创","article_title": "Elasticsearch查询——Search Template(模板查询)","content": "关于版本 内容 版本 Elasticsearch版本 7.2.0 ES模板搜索——Search Template 日常开发中我们可能需要频繁的使用一些类似的查询。为了减少重复的工作,我们可以将这些类似的查询写成模板,在后续只需要传递不同的参数来调用模板获取结果。 模板的保存和删","date": "2019-12-19 23:48:14","read_num": 2,"comment_num": 0}
    {"index":{"_index":"article","_id":2}}
    {"article_type": "原创","article_title": "Elasticsearch查询——URI Search(简单查询字符串)","content": "简单查询字符串——URI Search 实际业务中我们通常都是使用一个完整的请求体从elasticsearch获得数据。然而在有些时候我们为了调试系统或者数据的时候需要临时进行一些简单的查询时候,编写完整的请求体JSON可能会稍微麻烦。而elasticsearch提供了一种基于URI的简单的查询方","date": "2019-12-18 21:58:18","read_num": 10,"comment_num": 0}
    {"index":{"_index":"article","_id":3}}
    {"article_type": "原创","article_title": "Elasticsearch摄取节点(十)——GeoIP以及Grok处理器","content": "IP解析处理器(GeoIP Processor) 处理器作用 GeoIP Processor主要是根据IP地址解析出具体的地理位置信息的处理器。此处理器需要配合Maxmind数据库中的数据。 ingest-geoip模块附带的GeoLite2 City、GeoLite2 Country和GeoLi","date": "2019-12-18 20:52:10","read_num": 4,"comment_num": 0}
    {"index":{"_index":"article","_id":4}}
    {"article_type": "原创","article_title": "Elasticsearch脚本使用","content": "如何使用Elasticsearch脚本 Elasticsearch默认脚本语言为Painless。其他lang插件使您可以运行以其他语言编写的脚本。 语言 沙盒 是否需要插件 备注 painless 是 内置的 默认脚本语言 expression 是 内置的 快速的自定义排名和","date": "2019-12-14 15:10:07","read_num": 11,"comment_num": 0}
    ......
    

    执行下面请求之后在返回内容中除了常规的数据匹配之外还存在highlight对象,此对象中会将查询字段中和查询条件一直的内容使用预设的HTML标签进行包裹。

    GET article/_search
    {
        "size":5,
        "query": {
            "match": {
                "content": "简单的查询"
            }
        },
        "highlight": {
            "boundary_scanner_locale":"zh_CN",
            "fields": {
                "content": {
                    "pre_tags": [
                        "<em>"
                    ],
                    "post_tags": [
                        "</em>"
                    ]
                }
            }
        }
    }
    
    ## 返回内容
    
    "highlight" : {
      "content" : [
    	"<em>简</em><em>单</em><em>查</em><em>询</em>字符串——URI Search 实际业务中我们通常都是使用一个完整<em>的</em>请求体从elasticsearch获得数据。",
    	"然而在有些时候我们为了调试系统或者数据<em>的</em>时候需要临时进行一些<em>简</em><em>单</em><em>的</em><em>查</em><em>询</em>时候,编写完整<em>的</em>请求体JSON可能会稍微麻烦。而elasticsearch提供了一种基于URI<em>的</em><em>简</em><em>单</em><em>的</em><em>查</em><em>询</em>方"
      ]
    }
    

    高亮设置

    下面的参数可以设置在highlight的下一级此时未全局设置,也可以设置在字段的下一级,此时为字段设置。单个字段的设置优先级高于全局设置。比如下面的请求中最终匹配的短语会被

    包裹.

    GET article/_search
    {
        "size":5,
        "query": {
            "match": {
                "content": "简单的查询"
            }
        },
        "highlight": {
            "boundary_scanner_locale":"zh_CN",
            "boundary_scanner":"sentence",
            "fragmenter":"span", 
            "pre_tags": ["<em>"],
            "post_tags": ["</em>"],
            "fields": {
                "content": {
                   "pre_tags": [
                        "<h1>"
                    ],
                    "post_tags": [
                        "</h1>"
                    ] 
                }
            }
        }
    }
    

    高亮参数

    参数说明
    boundary_chars包含每个边界字符的字符串。默认为,! ?\ \ n。
    boundary_max_scan扫描边界字符的距离。默认为20。
    boundary_scanner指定如何分割突出显示的片段,支持chars, sentence, or word三种方式。
    boundary_scanner_locale用来设置搜索和确定单词边界的本地化设置,此参数使用语言标记的形式(“en-US”, “fr-FR”, “ja-JP”)
    encoder表示代码段应该是HTML编码的:默认(无编码)还是HTML (HTML-转义代码段文本,然后插入高亮标记)
    fields指定检索高亮显示的字段。可以使用通配符来指定字段。例如,可以指定comment_*来获取以comment_开头的所有文本和关键字字段的高亮显示。
    force_source根据源高亮显示。默认值为false。
    fragmenter指定文本应如何在突出显示片段中拆分:支持参数simple或者span。
    fragment_offset控制要开始突出显示的空白。仅在使用fvh highlighter时有效。
    fragment_size字符中突出显示的片段的大小。默认为100。
    highlight_query突出显示搜索查询之外的其他查询的匹配项。这在使用重打分查询时特别有用,因为默认情况下高亮显示不会考虑这些问题。
    matched_fields组合多个匹配结果以突出显示单个字段,对于使用不同方式分析同一字符串的多字段。所有的matched_fields必须将term_vector设置为with_positions_offsets,但是只有将匹配项组合到的字段才会被加载,因此只有将store设置为yes才能使该字段受益。只适用于fvh highlighter。
    no_match_size如果没有要突出显示的匹配片段,则希望从字段开头返回的文本量。默认为0(不返回任何内容)。
    number_of_fragments返回的片段的最大数量。如果片段的数量设置为0,则不会返回任何片段。相反,突出显示并返回整个字段内容。当需要突出显示短文本(如标题或地址),但不需要分段时,使用此配置非常方便。如果number_of_fragments为0,则忽略fragment_size。默认为5。
    order设置为score时,按分数对突出显示的片段进行排序。默认情况下,片段将按照它们在字段中出现的顺序输出(order:none)。将此选项设置为score将首先输出最相关的片段。每个高亮应用自己的逻辑来计算相关性得分。
    phrase_limit控制文档中所考虑的匹配短语的数量。防止fvh highlighter分析太多的短语和消耗太多的内存。提高限制会增加查询时间并消耗更多内存。默认为256。
    pre_tags与post_tags一起使用,定义用于突出显示文本的HTML标记。默认情况下,突出显示的文本被包装在和标记中。指定为字符串数组。
    post_tags与pre_tags一起使用,定义用于突出显示文本的HTML标记。默认情况下,突出显示的文本被包装在和标记中。指定为字符串数组。
    require_field_match默认情况下,只突出显示包含查询匹配的字段。将require_field_match设置为false以突出显示所有字段。默认值为true。
    tags_schema设置为使用内置标记模式的样式。
    type使用的高亮模式:unified, plain, or fvh. 默认为 unified。

    高亮参数内容的补充

    boundary_scanner

    指定如何分割突出显示的片段,支持chars, sentence, or word三种方式。默认unified highlighter使用
    sentence,fvh highlighter使用chars。

    • chars

    使用boundary_chars指定的字符突出显示边界。boundary_max_scan设置控制扫描边界字符的距离。只适用于fvh highlighter。

    • sentence

    根据Java的BreakIterator确定,在下一个句子边界处中断突出显示的片段。配置了此参数后上面例子返回内容,可以看到其每个分段都是一个句子

    "highlight" : {
      "content" : [
    	"<em>简</em><em>单</em><em>查</em><em>询</em>字符串——URI Search 实际业务中我们通常都是使用一个完整<em>的</em>请求体从elasticsearch获得数据。",
    	"然而在有些时候我们为了调试系统或者数据<em>的</em>时候需要临时进行一些<em>简</em><em>单</em><em>的</em><em>查</em><em>询</em>时候,编写完整<em>的</em>请求体JSON可能会稍微麻烦。而elasticsearch提供了一种基于URI<em>的</em><em>简</em><em>单</em><em>的</em><em>查</em><em>询</em>方"
      ]
    }
    
    • word

    根据Java的BreakIterator确定,在下一个单词边界处中断突出显示的片段。使用此参数上面的例子返回内容,此时每个分段并不是一个完整的句子。

    "highlight" : {
      "content" : [
    	"<em>简</em><em>单</em><em>查</em><em>询</em>字符串",
    	"实际业务中我们通常都是使用一个完整<em>的</em>请求体从",
    	"然而在有些时候我们为了调试系统或者数据<em>的</em>时候需要临时进行一些<em>简</em><em>单</em><em>的</em><em>查</em><em>询</em>时候",
    	"编写完整<em>的</em>请求体",
    	"<em>的</em><em>简</em><em>单</em><em>的</em><em>查</em><em>询</em>方"
      ]
    }
    

    boundary_scanner_locale

    用来设置搜索和确定单词边界的本地化设置

    1. 可以通过https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html#forLanguageTag-java.lang.String-获得更多语言标签

    2. 默认参数为[Locale.ROOT]{https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html#ROOT}

    fragmenter

    指定文本应如何在突出显示片段中拆分。只适用于plain highlighter。默认为span。

    1. simple:将文本分割成相同大小的片段
    2. span:将文本分割为大小相同的片段,但尽量避免在突出显示的术语之间分割文本。这在查询短语时很有用

    type

    type字段允许强制使用特定的高亮策略。可以配置的参数:unified, plain and fvh。下面是一个使用plain策略的例子:

    GET article/_search
    {
        "query" : {
            "match": { "content": "简单的查询" }
        },
        "highlight" : {
            "fields" : {
                "content" : {"type" : "plain"}
            }
        }
    }
    

    三种不同的高亮策略的区别

    1. unified(通用高亮策略)

    其使用的是Lucene的Unified Highlighter。此高亮策略将文本分解成句子,并使用BM25算法对单个句子进行评分,支持精确的短语和多术语(模糊、前缀、正则表达式)突出显示。这个是默认的高亮策略。

    1. plain (普通高亮策略)

    其使用的是Lucene的standard Lucene highlighter。它试图在理解词的重要性和短语查询中的任何词定位标准方面反映查询匹配逻辑。此高亮策略是和在单个字段中突出显示简单的查询匹配。如果想用复杂的查询在很多文档中突出显示很多字段,还是使用unified

    1. Fast vector highlighter(快速向量策略)

    其使用的是Lucene的Fast Vector highlighter。使用此策略需要在映射中将对应字段中属性term_vector设置为with_positions_offsets。这个策略以后会单独介绍。

    pre_tags和post_tags

    默认情况下,高亮显示将以和包装突出显示的文本。这可以通过设置pre_tags和post_tags来控制,例如:

    GET article/_search
    {
        "size":5,
        "query": {
            "match": {
                "content": "简单的查询"
            }
        },
        "highlight": {
            "boundary_scanner_locale":"zh_CN",
            "fields": {
                "content": {
                   "pre_tags": [
                        "<h1>"
                    ],
                    "post_tags": [
                        "</h2>"
                    ] 
                }
            }
        }
    }
    

    当然也可以使用系统预设的标签模式:

    GET article/_search
    {
        "size":5,
        "query": {
            "match": {
                "content": "简单的查询"
            }
        },
        "highlight": {
            "boundary_scanner_locale":"zh_CN",
            "tags_schema" : "styled",
            "fields": {
                "content": {
                    
                }
            }
        }
    }
    

    fragment_size 和 number_of_fragments

    fragment_size主要控制了每个高亮片段的大小,而number_of_fragments控制了返回多少个高亮片段。下面例子中就是控制返回两个长度10的片段

    GET article/_search
    {
        "query": {
            "bool": {
                "must": [
                    {
                        "match": {
                            "content": "简单的查询"
                        }
                    }
                ]
            }
        },
        "highlight": {
            "boundary_scanner_locale": "zh_CN",
            "fields": {
                "content": {
                  "fragment_size" : 10,
                  "number_of_fragments" : 2
                }
            }
        }
    }
    

    order

    order控制了返回对象中highlight片段的排序。下面例子中返回的高亮片段将会根据分数顺序输出。假如设置了none则是按照顺序输出。

    GET article/_search
    {
        "query": {
            "bool": {
                "must": [
                    {
                        "match": {
                            "content": "简单的查询"
                        }
                    }
                ]
            }
        },
        "highlight": {
            "order" : "score",
            "boundary_scanner_locale": "zh_CN",
            "fields": {
                "content": {}
            }
        }
    }
    

    对一个字段进行查询的时候希望高亮其他字段

    在请求参数highlight中可以设置多个字段,同时需要为每个字段配置highlight_query。通过配置这个字段可以实现对content进行匹配查询同时高亮article_titlecontent字段的内容。同时使用此方法可以实现使用使用查询条件查询,而最终高亮字符串内容。

    GET article/_search
    {
        "query": {
            "match": {
                "content": "查询"
            }
        },
        "highlight": {
            "fields": {
                "content": {
                    "type": "unified",
                    "highlight_query": {
                        "bool": {
                            "must": {
                                "match": {
                                    "content": {
                                        "query": "字符串"
                                    }
                                }
                            },
                            "minimum_should_match": 0
                        }
                    }
                },
                "article_title": {
                    "type": "unified",
                    "highlight_query": {
                        "bool": {
                            "must": {
                                "match": {
                                    "article_title": {
                                        "query": "字符串"
                                    }
                                }
                            },
                            "minimum_should_match": 0
                        }
                    }
                }
            }
        }
    }
    

    highlighter如何确定高亮内容

    为了从查询的词汇中获得搜索片段位置,高亮策略显示需要知道原始文本中每个单词的起始和结束字符偏移量。目前根据模式不同获取这些数据途径不同

    1. 检索列表,如果在映射中index_options设置了offsets,unified会将其中数据应用在文档中,而不会重新分析文本。它直接对文档进行原始查询,并从索引中提取匹配的偏移数据。在字段内容很大的时候,使用此配置很重要,因为它不需要重新分析文本内容。和term_vectors相比,它还需要更少的磁盘空间。
    2. 术语向量,如果在映射中term_vector设置为with_positions_offsets则unified highlighter使用term_vector来突出显示字段。对于大字段(大于1MB)和多术语查询它的速度会比较快。而fvh highlighter总是使用term_vector
    3. 普通的高亮策略(
      Plain highlighting),当没有其他选择的时候,unified highlighter使用此模式,他在内存中创建一个小的索引(index),通过运行Lucene的查询执行计划来访问文档的匹配信息,对需要高亮显示的每个字段和每个文档进行处理。plain highlighter总是使用此策略。注意此方式在大型文本上可能需要大量的时间和内存。在使用此策略时候可以设置分析的文本字符的最大数量限制为1000000。这个数值可以通过修改索引的index.highlight.max_analyzed_offset参数来改变。

    个人水平有限,上面的内容可能存在没有描述清楚或者错误的地方,假如开发同学发现了,请及时告知,我会第一时间修改相关内容。假如我的这篇内容对你有任何帮助的话,麻烦给我点一个赞。你的点赞就是我前进的动力。

    展开全文
  • 大学计算机ACCESS选择题汇总答案高亮.doc
  • 这种结构适用于快速的 全文搜索, 一个索引由文档中所有不重复的列表构成,对于每一个词,都有一个包含它的文档列表。 例 如,现在有两个文档, 每个文档包含如下内容: Study every day, good good up to forever #...

    Elasticsearch

    Elasticsearch安装

    我们需要下载和安装ElasticSearch的服务端和客户端!

    注意:
    ElasticSearch是使用java开发的,且本版本的es需要的jdk版本要是1.8以上,所以安装ElasticSearch 之
    前保证JDK1.8+安装完毕,并正确的配置好JDK环境变量,否则启动ElasticSearch失败。

    下载

    ElasticSearch的官方地址: https://www.elastic.co/products/elasticsearch

    image.png

    官方下载地址:https://www.elastic.co/cn/downloads/elasticsearch (很慢,可以翻墙下载!)

    win下载:https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.6.1.zip

    image.png

    我这里已经帮大家下载好了,Linux 和 window 版的!

    image.png

    我们学习的话使用 window 或者 linux 都是可以的,对于我们 java 开发来说没有区别,只是连接的问
    题!
    Windows更加方便一点!所以我们前期都是用Window安装使用!后面我们再真正的安装到Linux服务器
    上跑项目!

    附上个人网盘下载地址及笔记,后面涉及到需要安装的部分都可以直接从网盘获取,安装即解压。

    链接:https://pan.baidu.com/s/1B7cKgRa0y29tJSxmB6ppAQ 
    提取码:oqo7 
    --llp
    

    windows下安装使用

    image.png

    bin:启动文件
    config:配置文件
    log4j2.properties:日志配置文件
    jvm.options:java虚拟机的配置
    elasticsearch.yml:es的配置文件
    data:索引数据目录
    lib:相关类库Jar包
    logs:日志目录
    modules:功能模块
    plugins:插件
    

    双击ElasticSearch下的bin目录中的elasticsearch.bat启动,控制台显示的日志(等待启动完
    毕!):

    image.png

    然后在浏览器访问:http://localhost:9200 得到如下信息,说明安装成功了:

    image.png

    安装ES的图形化界面插件客户端

    注意:需要NodeJS的环境,我们讲解大前端进阶已经安装过了,没安装的需要安装!
    Head是elasticsearch的集群管理工具,可以用于数据的浏览查询!被托管在github上面!

    地址: https://github.com/mobz/elasticsearch-head/

    1、下载 elasticsearch-head-master.zip
    2、解压之后安装依赖!

    cnpm install
    npm run start
    

    这将启动在端口9100上运行的本地web服务器,为elasticsearch-head服务!访问测试:

    image.png

    # 跨域配置:
    http.cors.enabled: true
    http.cors.allow-origin: "*"
    

    启动ElasticSearch,使用head工具进行连接测试!可以看到默认的集群名就叫elasticsearch

    image.png

    了解

    ELK是Elasticsearch、Logstash、Kibana三大开源框架首字母大写简称。市面上也被成为Elastic
    Stack。其中Elasticsearch是一个基于Lucene、分布式、通过Restful方式进行交互的近实时搜索平台框
    架。像类似百度、谷歌这种大数据全文搜索引擎的场景都可以使用Elasticsearch作为底层支持框架,可
    见Elasticsearch提供的搜索能力确实强大,市面上很多时候我们简称Elasticsearch为es。Logstash是ELK
    的中央数据流引擎,用于从不同目标(文件/数据存储/MQ)收集的不同格式数据,经过过滤后支持输出
    到不同目的地(文件/MQ/redis/elasticsearch/kafka等)。Kibana可以将elasticsearch的数据通过友好
    的页面展示出来,提供实时分析的功能。
    市面上很多开发只要提到ELK能够一致说出它是一个日志分析架构技术栈总称,但实际上ELK不仅仅适用
    于日志分析,它还可以支持其它任何数据分析和收集的场景,日志分析和收集只是更具有代表性。并非
    唯一性。

    image.png

    安装kibana

    Kibana是一个针对Elasticsearch的开源分析及可视化平台,用来搜索、查看交互存储在Elasticsearch索
    引中的数据。使用Kibana,可以通过各种图表进行高级数据分析及展示。Kibana让海量数据更容易理
    解。它操作简单,基于浏览器的用户界面可以快速创建仪表板(dashboard)实时显示Elasticsearch查
    询动态。设置Kibana非常简单。无需编码或者额外的基础架构,几分钟内就可以完成Kibana安装并启动
    Elasticsearch索引监测。
    官网:https://www.elastic.co/cn/kibana
    1、下载Kibana https://www.elastic.co/cn/downloads/kibana (注意版本对应关系)

    image.png

    2、将压缩包解压即可(需要一些时间)!
    3、然后进入到bin目录下,双击kibana.bat启动服务就可以了(需要等待启动完成),ELK基本上都是拆箱即用的

    image.png

    4、然后访问IP:5601,kibana会自动去访问9200,也就是elasticsearch的端口号(当然elasticsearch这
    个时候必须启动着),然后就可以使用kibana了

    image.png

    5、现在是英文的,看着有些吃力,我们配置为中文的!

    中文包在 kibana\x-pack\plugins\translations\translations\zh-CN.json
    只需要在配置文件 kibana.yml 中加入,如果启动失败检查一下编辑的时候件编码格式是否为utf-8

    i18n.locale: "zh-CN"
    

    image.png

    6、重启查看效果!成功切换为中文的了!

    image.png

    至此,elasticsearch、head、kibana安装就完成了。

    ES核心概念

    概述

    在前面的学习中,我们已经掌握了es是什么,同时也把es的服务已经安装启动,那么es是如何去存储数
    据,数据结构是什么,又是如何实现搜索的呢?我们先来聊聊ElasticSearch的相关概念吧!

    集群,节点,索引,类型,文档,分片,映射是什么?

    elasticsearch是面向文档,关系行数据库 和 elasticsearch 客观的对比!

    image.png

    elasticsearch(集群)中可以包含多个索引(数据库),每个索引中可以包含多个类型(表),每个类型下又包
    含多 个文档(行),每个文档中又包含多个字段(列)。
    物理设计:
    elasticsearch 在后台把每个索引划分成多个分片,每分分片可以在集群中的不同服务器间迁移
    逻辑设计:
    一个索引类型中,包含多个文档,比如说文档1,文档2。 当我们索引一篇文档时,可以通过这样的一各
    顺序找到 它: 索引 ▷ 类型 ▷ 文档ID ,通过这个组合我们就能索引到某个具体的文档。 注意:ID不必是整
    数,实际上它是个字 符串。

    文档

    elasticsearch(集群)中可以包含多个索引(数据库),每个索引中可以包含多个类型(表),每个类型下又包
    含多 个文档(行),每个文档中又包含多个字段(列)。
    物理设计:
    elasticsearch 在后台把每个索引划分成多个分片,每分分片可以在集群中的不同服务器间迁移
    逻辑设计:
    一个索引类型中,包含多个文档,比如说文档1,文档2。 当我们索引一篇文档时,可以通过这样的一各
    顺序找到 它: 索引 ▷ 类型 ▷ 文档ID ,通过这个组合我们就能索引到某个具体的文档。 注意:ID不必是整
    数,实际上它是个字 符串。

    类型

    类型是文档的逻辑容器,就像关系型数据库一样,表格是行的容器。 类型中对于字段的定义称为映射,
    比如 name 映 射为字符串类型。 我们说文档是无模式的,它们不需要拥有映射中所定义的所有字段,
    比如新增一个字段,那么elasticsearch是怎么做的呢?elasticsearch会自动的将新字段加入映射,但是这
    个字段的不确定它是什么类型,elasticsearch就开始猜,如果这个值是18,那么elasticsearch会认为它
    是整形。 但是elasticsearch也可能猜不对, 所以最安全的方式就是提前定义好所需要的映射,这点跟关
    系型数据库殊途同归了,先定义好字段,然后再使用,别 整什么幺蛾子。

    索引

    索引是映射类型的容器,elasticsearch中的索引是一个非常大的文档集合。索引存储了映射类型的字段
    和其他设置。 然后它们被存储到了各个分片上了。 我们来研究下分片是如何工作的。
    物理设计 :节点和分片 如何工作
    一个集群至少有一个节点,而一个节点就是一个elasricsearch进程,节点可以有多个索引默认的,如果
    你创建索引,那么索引将会有个5个分片 ( primary shard ,又称主分片 ) 构成的,每一个主分片会有一个
    副本 ( replica shard ,又称复制分片)

    image.png

    上图是一个有3个节点的集群,可以看到主分片和对应的复制分片都不会在同一个节点内,这样有利于某
    个节点挂掉 了,数据也不至于丢失。 实际上,一个分片是一个Lucene索引,一个包含倒排索引的文件
    目录,倒排索引的结构使 得elasticsearch在不扫描全部文档的情况下,就能告诉你哪些文档包含特定的
    关键字。 不过,等等,倒排索引是什 么鬼?

    倒排索引

    elasticsearch使用的是一种称为倒排索引的结构,采用Lucene倒排索作为底层。这种结构适用于快速的
    全文搜索, 一个索引由文档中所有不重复的列表构成,对于每一个词,都有一个包含它的文档列表。 例
    如,现在有两个文档, 每个文档包含如下内容:

    Study every day, good good up to forever # 文档1包含的内容
    To forever, study every day, good good up # 文档2包含的内容
    

    为了创建倒排索引,我们首先要将每个文档拆分成独立的词(或称为词条或者tokens),然后创建一个包
    含所有不重 复的词条的排序列表,然后列出每个词条出现在哪个文档 :

    image.png

    现在,我们试图搜索 to forever,只需要查看包含每个词条的文档

    image.png

    两个文档都匹配,但是第一个文档比第二个匹配程度更高。如果没有别的条件,现在,这两个包含关键
    字的文档都将返回。
    再来看一个示例,比如我们通过博客标签来搜索博客文章。那么倒排索引列表就是这样的一个结构 :

    image.png

    如果要搜索含有 python 标签的文章,那相对于查找所有原始数据而言,查找倒排索引后的数据将会快
    的多。只需要 查看标签这一栏,然后获取相关的文章ID即可。

    如果要搜索含有 python 标签的文章,那相对于查找所有原始数据而言,查找倒排索引后的数据将会快
    的多。只需要 查看标签这一栏,然后获取相关的文章ID即可。

    ES基础操作

    IK分词器插件

    什么时IK分词器

    分词:即把一段中文或者别的划分成一个个的关键字,我们在搜索时候会把自己的信息进行分词,会把
    数据库中或者索引库中的数据进行分词,然后进行一个匹配操作,默认的中文分词是将每个字看成一个
    词,比如 “我爱狂神” 会被分为"我",“爱”,“狂”,“神”,这显然是不符合要求的,所以我们需要安装中文分词
    器ik来解决这个问题。
    IK提供了两个分词算法:ik_smart 和 ik_max_word,其中 ik_smart 为最少切分,ik_max_word为最细
    粒度划分!一会我们测试!

    安装步骤

    1、下载ik分词器的包,Github地址:https://github.com/medcl/elasticsearch-analysis-ik/ (版本要对
    应)
    2、下载后解压,并将目录拷贝到ElasticSearch根目录下的 plugins 目录中。

    image.png

    3、重新启动 ElasticSearch 服务,在启动过程中,你可以看到正在加载"analysis-ik"插件的提示信息,
    服务启动后,在命令行运行 elasticsearch-plugin list 命令,确认 ik 插件安装成功。

    image.png

    4、在 kibana 中测试 ik 分词器,并就相关分词结果和 icu 分词器进行对比。
    ik_max_word : 细粒度分词,会穷尽一个语句中所有分词可能,测试!

    image.png

    ik_smart : 粗粒度分词,优先匹配最长词,只有1个词!

    image.png

    5、我们输入超级喜欢狂神说!发现狂神说被切分了

    如果我们想让系统识别“狂神说”是一个词,需要编辑自定义词库。
    步骤:
    (1)进入elasticsearch/plugins/ik/config目录
    (2)新建一个my.dic文件,编辑内容:

    狂神说

    (3)修改IKAnalyzer.cfg.xml(在ik/config目录下)

    <properties>
    <comment>IK Analyzer 扩展配置</comment>
    <!-- 用户可以在这里配置自己的扩展字典 -->
    <entry key="ext_dict">my.dic</entry>
    <!-- 用户可以在这里配置自己的扩展停止词字典 -->
    <entry key="ext_stopwords"></entry>
    </properties>
    

    修改完配置重新启动elasticsearch,再次测试!
    发现监视了我们自己写的规则文件:

    image.png

    再次测试,发现狂神说变成了一个词:

    image.png

    到了这里,我们就明白了分词器的基本规则和使用了!

    Rest风格说明

    一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交
    互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
    基本Rest命令说明:

    image.png

    基础测试

    1、首先我们浏览器 http://localhost:5601/ 进入 kibana里的Console
    2、首先让我们在 Console 中输入 :

    // 命令解释
    // PUT 创建命令 test1 索引 type1 类型 1 id
    PUT /test1/type1/1
    {
    "name":"狂神说", // 属性
    "age":16 // 属性
    }
    

    返回结果 (是以REST ful 风格返回的 ):

    // 警告信息:不支持在文档索引请求中指定类型
    // 而是使用无类型的端点(/{index}/_doc/{id}, /{index}/_doc,或
    /{index}/_create/{id}){
    "_index" : "test1", // 索引
    "_type" : "type1", // 类型
    "_id" : "1", // id
    "_version" : 1, // 版本
    "result" : "created", // 操作类型
    "_shards" : { // 分片信息
    "total" : 2,
    "successful" : 1,
    "failed" : 0
    },
    "_seq_no" : 0,
    "_primary_term" : 1
    }
    

    那么 name 这个字段用不用指定类型呢。毕竟我们关系型数据库 是需要指定类型的啊 !
    字符串类型
    text 、 keyword
    数值类型
    long, integer, short, byte, double, float, half_float, scaled_float
    日期类型
    date
    te布尔值类型
    boolean
    二进制类型
    binary
    等等…
    4、指定字段类

    PUT /test2
    {
        "mappings":{
            "properties":{
                "name":{
                    "type":"text"
                },
                "age":{
                    "type":"long"
                },
                "birthday":{
                    "type":"date"
                }
            }
        }
    }
    

    输出:

    {
    "acknowledged" : true,
    "shards_acknowledged" : true,
    "index" : "test2"
    }
    

    5、查看一下索引字段

    GET test2

    {
        "test2":{
            "aliases":{
    
            },
            "mappings":{
                "properties":{
                    "age":{
                        "type":"long"
                    },
                    "birthday":{
                        "type":"date"
                    },
                    "name":{
                        "type":"text"
                    }
                }
            },
            "settings":{
                "index":{
                    "creation_date":"1585384302712",
                    "number_of_shards":"1",
                    "number_of_replicas":"1",
                    "uuid":"71TUZ84wRTW5P8lKeN4I4Q",
                    "version":{
                        "created":"7060199"
                    },
                    "provided_name":"test2"
                }
            }
        }
    }
    

    查看一下test3索引:

    GET test3

    返回结果:

    {
        "test3":{
            "aliases":{
    
            },
            "mappings":{
                "properties":{
                    "age":{
                        "type":"long"
                    },
                    "birth":{
                        "type":"date"
                    },
                    "name":{
                        "type":"text",
                        "fields":{
                            "keyword":{
                                "type":"keyword",
                                "ignore_above":256
                            }
                        }
                    }
                }
            },
            "settings":{
                "index":{
                    "creation_date":"1585384497051",
                    "number_of_shards":"1",
                    "number_of_replicas":"1",
                    "uuid":"xESBKF1XTpCAZOgMqBNUbA",
                    "version":{
                        "created":"7060199"
                    },
                    "provided_name":"test3"
                }
            }
        }
    }
    

    我们看上列没有给字段指定类型那么es就会默认给我配置字段类型!
    对比关系型数据库 :
    PUT test1/type1/1 : 索引test1相当于关系型数据库的库,类型type1就相当于表 ,1 代表数据中的主
    键 id
    这里需要补充的是 ,在elastisearch5版本前,一个索引下可以创建多个类型,但是在elastisearch5后,
    一个索引只能对应一个类型,而id相当于关系型数据库的主键id若果不指定就会默认生成一个20位的
    uuid,属性相当关系型数据库的column(列)。
    而结果中的 result 则是操作类型,现在是 created ,表示第一次创建。如果再次点击执行该命令那么
    result 则会是 updated ,我们细心则会发现 _version 开始是1,现在你每点击一次就会增加一次。表示
    第几次更改。
    7、我们在来学一条命令 (elasticsearch 中的索引的情况) :

    GET _cat/indices?v
    

    返回结果:查看我们所有索引的状态健康情况 分片,数据储存大小等等。

    image.png

    8、那么怎么删除一条索引呢(库)呢?

    DELETE /test1
    

    返回:

    {
    "acknowledged" : true # 表示删除成功!
    }
    

    增删改查命令

    第一条数据:

    PUT /kuangshen/user/1
    {
    "name":"狂神说",
    "age":18,
    "desc":"一顿操作猛如虎,一看工资2500",
    "tags":["直男","技术宅","温暖"]
    }
    

    第二条数据 :

    PUT /kuangshen/user/2
    {
    "name":"张三",
    "age":3,
    "desc":"法外狂徒",
    "tags":["渣男","旅游","交友"]
    }
    

    第三条数据:

    PUT /kuangshen/user/3
    {
    "name":"李四",
    "age":30,
    "desc":"mmp,不知道怎么形容",
    "tags":["靓女","旅游","唱歌"]
    }
    

    查看下数据:

    image.png

    注意⚠ :当执行 命令时,如果数据不存在,则新增该条数据,如果数据存在则修改该条数据。
    咱们通过 GET 命令查询一下 :

    GET kuangshen/user/1
    

    返回结果:

    {
        "_index":"kuangshen",
        "_type":"user",
        "_id":"1",
        "_version":1,
        "_seq_no":0,
        "_primary_term":1,
        "found":true,
        "_source":{
            "name":"狂神说",
            "age":18,
            "desc":"一顿操作猛如虎,一看工资2500",
            "tags":[
                "直男",
                "技术宅",
                "温暖"
            ]
        }
    }
    

    如果你想更新数据 可以覆盖这条数据 :

    PUT /kuangshen/user/1
    {
    "name":"狂神说Java",
    "age":18,
    "desc":"一顿操作猛如虎,一看工资2.5",
    "tags":["直男","技术宅","温暖"]
    }
    

    返回结果:

    {
    "_index" : "kuangshen",
    "_type" : "user",
    "_id" : "1",
    "_version" : 2,
    "result" : "updated",
    "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
    },
    "_seq_no" : 3,
    "_primary_term" : 1
    }
    

    已经修改了 那么 PUT 可以更新数据但是。麻烦的是 原数据你还要重写一遍要 这不符合我们规矩。

    更新数据POST

    我们使用 POST 命令,在 id 后面跟 _update ,要修改的内容放到 doc 文档(属性)中即可。

    POST /kuangshen/user/1/_update
    {
    "doc":{
    "name":"狂神说Java",
    "desc":"关注狂神公众号每日更新文章哦"
    }
    }
    

    返回结果:

    {
        "_index":"kuangshen",
        "_type":"user",
        "_id":"1",
        "_version":3,
        "result":"updated",
        "_shards":{
            "total":2,
            "successful":1,
            "failed":0
        },
        "_seq_no":4,
        "_primary_term":1
    }
    

    条件查询_search?q=

    简单的查询,我们上面已经不知不觉的使用熟悉了:

    GET kuangshen/user/1
    

    我们来学习下条件查询 _search?q=

    GET kuangshen/user/_search?q=name:狂神说
    

    通过 _serarch?q=name:狂神说 查询条件是name属性有狂神说的那些数据。
    别忘 了 _search 和 from 属性中间的分隔符 ? 。

    返回结果:

    {
        "took":16,
        "timed_out":false,
        "_shards":{
            "total":1,
            "successful":1,
            "skipped":0,
            "failed":0
        },
        "hits":{
            "total":{
                "value":1,
                "relation":"eq"
            },
            "max_score":1.4229509,
            "hits":[
                {
                    "_index":"kuangshen",
                    "_type":"user",
                    "_id":"1",
                    "_score":1.4229509,
                    "_source":{
                        "name":"狂神说Java",
                        "age":18,
                        "desc":"关注狂神公众号每日更新文章哦",
                        "tags":[
                            "直男",
                            "技术宅",
                            "温暖"
                        ]
                    }
                }
            ]
        }
    }
    

    我们看一下结果 返回并不是 数据本身,是给我们了一个 hits ,还有 _score得分,就是根据算法算出和
    查询条件匹配度高得分就搞。

    构建查询

    GET kuangshen/user/_search
    {
    "query":{
    "match":{
    "name": "狂神"
    }
    }
    }
    

    上例,查询条件是一步步构建出来的,将查询条件添加到 match 中即可。返回结果还是一样的:

    {
        "took":0,
        "timed_out":false,
        "_shards":{
            "total":1,
            "successful":1,
            "skipped":0,
            "failed":0
        },
        "hits":{
            "total":{
                "value":1,
                "relation":"eq"
            },
            "max_score":1.6285465,
            "hits":[
                {
                    "_index":"kuangshen",
                    "_type":"user",
                    "_id":"1",
                    "_score":1.6285465,
                    "_source":{
                        "name":"狂神说Java",
                        "age":18,
                        "desc":"关注狂神公众号每日更新文章哦",
                        "tags":[
                            "直男",
                            "技术宅",
                            "温暖"
                        ]
                    }
                }
            ]
        }
    }
    

    除此之外,我们还可以查询全部:

    GET kuangshen/user/_search #这是一个查询但是没有条件
    GET kuangshen/user/_search
    {
    "query":{
    "match_all": {}
    }
    }
    

    match_all的值为空,表示没有查询条件,就像select * from table_name一样。

    返回结果:全部查询出来了!
    如果有个需求,我们仅是需要查看 name 和 desc 两个属性,其他的不要怎么办?

    GET kuangshen/user/_search
    {
    "query":{
    "match_all": {}
    },
    "_source": ["name","desc"]
    }
    

    如上例所示,在查询中,通过 _source 来控制仅返回 name 和 age 属性。

    {
        "took":1,
        "timed_out":false,
        "_shards":{
            "total":1,
            "successful":1,
            "skipped":0,
            "failed":0
        },
        "hits":{
            "total":{
                "value":3,
                "relation":"eq"
            },
            "max_score":1,
            "hits":[
                {
                    "_index":"kuangshen",
                    "_type":"user",
                    "_id":"2",
                    "_score":1,
                    "_source":{
                        "name":"张三",
                        "desc":"法外狂徒"
                    }
                },
                {
                    "_index":"kuangshen",
                    "_type":"user",
                    "_id":"3",
                    "_score":1,
                    "_source":{
                        "name":"李四",
                        "desc":"mmp,不知道怎么形容"
                    }
                },
                {
                    "_index":"kuangshen",
                    "_type":"user",
                    "_id":"1",
                    "_score":1,
                    "_source":{
                        "name":"狂神说Java",
                        "desc":"关注狂神公众号每日更新文章哦"
                    }
                }
            ]
        }
    }
    

    一般的,我们推荐使用构建查询,以后在与程序交互时的查询等也是使用构建查询方式处理查询条件,
    因为该方 式可以构建更加复杂的查询条件,也更加一目了然

    排序查询

    我们说到排序 有人就会想到:正序 或 倒序 那么我们先来倒序:

    GET kuangshen/user/_search
    {
    "query":{
    "match_all": {}
    },
    "sort": [
    {
    "age": {
    "order": "desc"
    }
    }
    ]
    }
    

    上例,在条件查询的基础上,我们又通过 sort 来做排序,排序对象是 age , order 是 desc 降序。

    {
        "took":0,
        "timed_out":false,
        "_shards":{
            "total":1,
            "successful":1,
            "skipped":0,
            "failed":0
        },
        "hits":{
            "total":{
                "value":3,
                "relation":"eq"
            },
            "max_score":null,
            "hits":[
                {
                    "_index":"kuangshen",
                    "_type":"user",
                    "_id":"3",
                    "_score":null,
                    "_source":{
                        "name":"李四",
                        "age":30,
                        "desc":"mmp,不知道怎么形容",
                        "tags":[
                            "靓女",
                            "旅游",
                            "唱歌"
                        ]
                    },
                    "sort":[
                        30
                    ]
                },
                {
                    "_index":"kuangshen",
                    "_type":"user",
                    "_id":"1",
                    "_score":null,
                    "_source":{
                        "name":"狂神说Java",
                        "age":18,
                        "desc":"关注狂神公众号每日更新文章哦",
                        "tags":[
                            "直男",
                            "技术宅",
                            "温暖"
                        ]
                    },
                    "sort":[
                        18
                    ]
                },
                {
                    "_index":"kuangshen",
                    "_type":"user",
                    "_id":"2",
                    "_score":null,
                    "_source":{
                        "name":"张三",
                        "age":3,
                        "desc":"法外狂徒",
                        "tags":[
                            "渣男",
                            "旅游",
                            "交友"
                        ]
                    },
                    "sort":[
                        3
                    ]
                }
            ]
        }
    }
    

    正序,就是 desc 换成了 asc

    GET kuangshen/user/_search
    {
    "query":{
    "match_all": {}
    },
    "sort": [
    {
    "age": {
    "order": "asc"
    }
    }
    ]
    }
    

    注意:在排序的过程中,只能使用可排序的属性进行排序。那么可以排序的属性有哪些呢?
    数字
    日期
    ID
    其他都不行!

    分页查询

    GET kuangshen/user/_search
    {
    "query":{
    "match_all": {}
    },
    "sort": [
    {
    "age": {
    "order": "asc"
    }
    }
    ],
    "from": 0, # 从第n条开始
    "size": 1 # 返回n条数据
    }
    

    返回结果:

    {
        "took":0,
        "timed_out":false,
        "_shards":{
            "total":1,
            "successful":1,
            "skipped":0,
            "failed":0
        },
        "hits":{
            "total":{
                "value":3,
                "relation":"eq"
            },
            "max_score":null,
            "hits":[
                {
                    "_index":"kuangshen",
                    "_type":"user",
                    "_id":"2",
                    "_score":null,
                    "_source":{
                        "name":"张三",
                        "age":3,
                        "desc":"法外狂徒",
                        "tags":[
                            "渣男",
                            "旅游",
                            "交友"
                        ]
                    },
                    "sort":[
                        3
                    ]
                }
            ]
        }
    }
    

    就返回了一条数据 是从第0条开始的返回一条数据 。可以再测试!
    学到这里,我们也可以看到,我们的查询条件越来越多,开始仅是简单查询,慢慢增加条件查询,增加
    排序,对返回 结果进行限制。所以,我们可以说:对elasticsearch于 来说,所有的查询条件都是可插拔
    的,彼此之间用 分 割。比如说,我们在查询中,仅对返回结果进行限制:

    GET kuangshen/user/_search
    {
    "query":{
    "match_all": {}
    },
    "from": 0, # 从第n条开始
    "size": 1 # 返回n条数据
    }
    

    布尔查询

    先增加一个数据:

    PUT /kuangshen/user/4
    {
    "name":"狂神说",
    "age":3,
    "desc":"一顿操作猛如虎,一看工资2500",
    "tags":["直男","技术宅","温暖"]
    }
    

    must (and)

    我要查询所有 name 属性为“ 狂神 “的数据,并且年龄为18岁的!

    GET kuangshen/user/_search
    
    {
        "query":{
            "bool":{
                "must":[
                    {
                        "match":{
                            "name":"狂神说"
                        }
                    },
                    {
                        "match":{
                            "age":3
                        }
                    }
                ]
            }
        }
    }
    

    我们通过在 bool 属性内使用 must 来作为查询条件!看结果,是不是 有点像 and 的感觉,里面的条件
    需要都满足!

    should (or)
    那么我要查询name为狂神 或 age 为18 的呢?

    GET kuangshen/user/_search
    {
    "query": {
    "bool": {
    "should": [
    {
    "match": {
    "name": "狂神说"
    }
    },
    {
    "match": {
    "age": 18
    }
    }
    ]
    }
    }
    }
    

    返回结果:

    {
        "took":0,
        "timed_out":false,
        "_shards":{
            "total":1,
            "successful":1,
            "skipped":0,
            "failed":0
        },
        "hits":{
            "total":{
                "value":2,
                "relation":"eq"
            },
            "max_score":3.1522982,
            "hits":[
                {
                    "_index":"kuangshen",
                    "_type":"user",
                    "_id":"1",
                    "_score":3.1522982,
                    "_source":{
                        "name":"狂神说Java",
                        "age":18,
                        "desc":"关注狂神公众号每日更新文章哦",
                        "tags":[
                            "直男",
                            "技术宅",
                            "温暖"
                        ]
                    }
                },
                {
                    "_index":"kuangshen",
                    "_type":"user",
                    "_id":"4",
                    "_score":2.4708953,
                    "_source":{
                        "name":"狂神说",
                        "age":3,
                        "desc":"一顿操作猛如虎,一看工资2500",
                        "tags":[
                            "直男",
                            "技术宅",
                            "温暖"
                        ]
                    }
                }
            ]
        }
    }
    

    我们的返回结果 是不是 出现了一个 age : 3的。是不是有点像 or 呢
    must_not (not)
    我想要查询 年龄不是 18 的 数据

    GET kuangshen/user/_search
    {
        "query":{
            "bool":{
                "must_not":[
                    {
                        "match":{
                            "age":18
                        }
                    }
                ]
            }
        }
    }
    

    Fitter
    我要查询 name 为狂神 的,age大于10的数据

    GET kuangshen/user/_search
    {
        "query":{
            "bool":{
                "must":[
                    {
                        "match":{
                            "name":"狂神"
                        }
                    }
                ],
                "filter":{
                    "range":{
                        "age":{
                            "gt":10
                        }
                    }
                }
            }
        }
    }
    

    这里就用到了 filter 条件过滤查询,过滤条件的范围用 range 表示, gt 表示大于,大于多少呢?是10。
    其余操作如下 :
    gt 表示大于
    gte 表示大于等于
    lt 表示小于
    lte 表示小于等于
    要查询 name 是 狂神, age 在 25~30 之间的怎么查?

    GET kuangshen/user/_search
    {
        "query":{
            "bool":{
                "must":[
                    {
                        "match":{
                            "name":"狂神"
                        }
                    }
                ],
                "filter":{
                    "range":{
                        "age":{
                            "gte":25,
                            "lte":30
                        }
                    }
                }
            }
        }
    }
    

    短语检索

    我要查询 tags为男的数据

    GET kuangshen/user/_search
    {
    "query":{
    "match": {
    "tags": "男"
    }
    }
    }
    

    返回了所有标签中带 男 的记录!
    既然按照标签检索,那么,能不能写多个标签呢?又该怎么写呢?

    GET kuangshen/user/_search
    {
    "query":{
    "match": {
    "tags": "男 技术"
    }
    }
    }
    

    返回:只要含有这个标签满足一个就给我返回这个数据了。

    term查询精确查询

    term 查询是直接通过倒排索引指定的 词条,也就是精确查找。

    term和match的区别:
    match是经过分析(analyer)的,也就是说,文档是先被分析器处理了,根据不同的分析器,分析出
    的结果也会不同,在会根据分词 结果进行匹配。
    term是不经过分词的,直接去倒排索引查找精确的值。
    注意 ⚠ :我们现在 用的es7版本 所以我们用 mappings properties 去给多个字段(fields)指定类型的时
    候,不能给我们的 索引制定类型:

    PUT testdb
    {
        "mappings":{
            "properties":{
                "name":{
                    "type":"text"
                },
                "desc":{
                    "type":"keyword"
                }
            }
        }
    }
    // 插入数据
    PUT testdb/_doc/1
    {
        "name":"狂神说Java name",
        "desc":"狂神说Java desc"
    }
    PUT testdb/_doc/2
    {
        "name":"狂神说Java name",
        "desc":"狂神说Java desc2"
    }
    

    上述中testdb索引中,字段name在被查询时会被分析器进行分析后匹配查询。而属于keyword类型不会
    被分析器处理。
    我们来验证一下:

    GET _analyze
    {
    "analyzer": "keyword",
    "text": "狂神说Java name"
    }
    

    结果:

    {
        "tokens":[
            {
                "token":"狂神说Java name",
                "start_offset":0,
                "end_offset":12,
                "type":"word",
                "position":0
            }
        ]
    }
    

    是不是没有被分析啊。就是简单的一个字符串啊。再测试

    GET _analyze
    {
    "analyzer": "standard",
    "text": "狂神说Java name"
    }
    

    结果:

    {
        "tokens":[
            {
                "token":"狂",
                "start_offset":0,
                "end_offset":1,
                "type":"<IDEOGRAPHIC>",
                "position":0
            },
            {
                "token":"神",
                "start_offset":1,
                "end_offset":2,
                "type":"<IDEOGRAPHIC>",
                "position":1
            },
            {
                "token":"说",
                "start_offset":2,
                "end_offset":3,
                "type":"<IDEOGRAPHIC>",
                "position":2
            },
            {
                "token":"java",
                "start_offset":3,
                "end_offset":7,
                "type":"<ALPHANUM>",
                "position":3
            },
            {
                "token":"name",
                "start_offset":8,
                "end_offset":12,
                "type":"<ALPHANUM>",
                "position":4
            }
        ]
    }
    

    那么我们看一下 们字符串是不是被分析了啊。
    总结:keyword 字段类型不会被分析器分析!
    现在我们来查询一下:

    GET testdb/_search // text 会被分析器分析 查询
    {
        "query":{
            "term":{
                "name":"狂"
            }
        }
    }
    GET testdb/_search // keyword 不会被分析所以直接查询
    {
        "query":{
            "match":{
                "desc":"狂神说Java desc"
            }
        }
    }
    

    查找多个精确值(terms)
    官网地址:https://www.elastic.co/guide/cn/elasticsearch/guide/current/_finding_multiple_exact_va
    lues.html

    PUT testdb/_doc/3
    {
    "t1": "22",
    "t2": "2020-4-16"
    }
    PUT testdb/_doc/4
    {
    "t1": "33",
    "t2": "2020-4-17"
    }
    # 查询 精确查找多个值
    GET testdb/_search
    {
        "query":{
            "bool":{
                "should":[
                    {
                        "term":{
                            "t1":"22"
                        }
                    },
                    {
                        "term":{
                            "t1":"33"
                        }
                    }
                ]
            }
        }
    }
    

    除了bool查询之外:

    GET testdb/_doc/_search
    {
    "query": {
    "terms": {
    "t1": ["22", "33"]
    }
    }
    }
    

    高亮显示

    GET kuangshen/user/_search
    {
    "query":{
    "match": {
    "name": "狂神"
    }
    },
    "highlight" :{
    "fields": {
    "name":{}
    }
    }
    }
    

    返回结果:

    #! Deprecation: [types removal] Specifying types in search requests is
    deprecated.
    

    我们可以看到 已 < em>狂神< /em>经帮我们加上了一个< em>标签
    这是es帮我们加的标签。那我·也可以自己自定义样式

    GET kuangshen/user/_search
    
    {
        "took":62,
        "timed_out":false,
        "_shards":{
            "total":1,
            "successful":1,
            "skipped":0,
            "failed":0
        },
        "hits":{
            "total":{
                "value":2,
                "relation":"eq"
            },
            "max_score":1.6472635,
            "hits":[
                {
                    "_index":"kuangshen",
                    "_type":"user",
                    "_id":"4",
                    "_score":1.6472635,
                    "_source":{
                        "name":"狂神说",
                        "age":3,
                        "desc":"一顿操作猛如虎,一看工资2500",
                        "tags":[
                            "直男",
                            "技术宅",
                            "温暖"
                        ]
                    },
                    "highlight":{
                        "name":[
                            "<em>狂</em><em>神</em>说"
                        ]
                    }
                },
                {
                    "_index":"kuangshen",
                    "_type":"user",
                    "_id":"1",
                    "_score":1.4348655,
                    "_source":{
                        "name":"狂神说Java",
                        "age":18,
                        "desc":"关注狂神公众号每日更新文章哦",
                        "tags":[
                            "直男",
                            "技术宅",
                            "温暖"
                        ]
                    },
                    "highlight":{
                        "name":[
                            "<em>狂</em><em>神</em>说Java"
                        ]
                    }
                }
            ]
        }
    }
    

    我们可以看到 已 < em>狂神< /em>经帮我们加上了一个< em>标签
    这是es帮我们加的标签。那我·也可以自己自定义样式

    GET kuangshen/user/_search
    {
    "query":{
    "match": {
    "name": "狂神"
    }
    },
    "highlight" :{
    "pre_tags": "<b class='key' style='color:red'>",
    "post_tags": "</b>",
    "fields": {
    "name":{}
    }
    }
    }
    

    需要注意的是:自定义标签中属性或样式中的逗号一律用英文状态的单引号表示,应该与外部 es 语法 的
    双引号区分开。

    说明:Deprecation

    注意 elasticsearch 在第一个版本的开始 每个文档都储存在一个索引中,并分配一个 映射类型,映射类
    型用于表示被索引的文档或者实体的类型,这样带来了一些问题, 导致后来在 elasticsearch6.0.0 版本中
    一个文档只能包含一个映射类型,而在 7.0.0 中,映 射类型则将被弃用,到了 8.0.0 中则将完全被删
    除。
    只要记得,一个索引下面只能创建一个类型就行了,其中各字段都具有唯一性,如果在创建映射的时
    候,如果没有指定文档类型,那么该索引的默认索引类型是 _doc ,不指定文档id则会内部帮我们生
    成一个id字符串。

    API创建索引及文档

    找文档

    网上的es教程大都十分老旧,而且es的版本众多,个别版本的差异还较大,另外es本身提供多种api,导
    致许多文章各种乱七八糟实例!所以后面直接放弃,从官网寻找方案,这里我使用elasticsearch最新的
    7.6.1版本来讲解:
    1、进入es的官网指导文档 https://www.elastic.co/guide/index.html
    2、找到 Elasticsearch Clients(这个就是客户端api文档)

    image.png

    3、我们使用java rest风格api,大家可以更加自己的版本选择特定的other versions。

    image.png

    4、rest又分为high level和low level,我们直接选择high level下面的 Getting started

    image.png

    5、向下阅读找到Maven依赖和基本配置!

    image.png

    Java REST Client 说明

    Java REST Client 有两种风格:
    Java Low Level REST Client :用于Elasticsearch的官方低级客户端。它允许通过http与Elasticsearch
    集群通信。将请求编排和响应反编排留给用户自己处理。它兼容所有的Elasticsearch版本。(PS:学过
    WebService的话,对编排与反编排这个概念应该不陌生。可以理解为对请求参数的封装,以及对响应结
    果的解析)
    Java High Level REST Client :用于Elasticsearch的官方高级客户端。它是基于低级客户端的,它提供
    很多API,并负责请求的编排与响应的反编排。(PS:就好比是,一个是传自己拼接好的字符串,并且
    自己解析返回的结果;而另一个是传对象,返回的结果也已经封装好了,直接是对象,更加规范了参数
    的名称以及格式,更加面对对象一点)
    (PS:所谓低级与高级,我觉得一个很形象的比喻是,面向过程编程与面向对象编程)

    网上很多教程比较老旧,都是使用TransportClient操作的,在 Elasticsearch 7.0 中不建议使用
    TransportClient,并且在8.0中会完全删除TransportClient。因此,官方更建议我们用Java High Level
    REST Client,它执行HTTP请求,而不是序列号的Java请求。既然如此,这里我们就直接用高级了。

    配置基本项目依赖

    1、新建一个springboot(2.2.5版)项目 kuang-elasticsearch ,导入web依赖即可!
    2、配置es的依赖!

    <properties>
    <java.version>1.8</java.version>
    <!-- 这里SpringBoot默认配置的版本不匹配,我们需要自己配置版本! -->
    <elasticsearch.version>7.6.1</elasticsearch.version>
    </properties>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>
    

    3、继续阅读文档到Initialization ,我们看到需要构建RestHighLevelClient对象;

    RestHighLevelClient client = new RestHighLevelClient(
    RestClient.builder(
    new HttpHost("localhost", 9200, "http"),
    new HttpHost("localhost", 9201, "http"))); // 构建客户端对象
    // 操作....
    // 高级客户端内部会创建低级客户端用于基于提供的builder执行请求。低级客户端维护一个连接池,
    并启动一些线程,因此当你用完以后应该关闭高级客户端,并且在内部它将会关闭低级客户端,以释放这
    些资源。关闭客户端可以使用close()方法:
    client.close(); // 关闭
    

    4、我们编写一个配置类,提供这个bean来进行操作

    package com.llp.elasticsearch.config;
    
    import org.apache.http.HttpHost;
    import org.elasticsearch.client.RestClient;
    import org.elasticsearch.client.RestHighLevelClient;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class RestHighLevelClientConfig {
    
        @Bean
        public RestHighLevelClient restHighLevelClient(){
            RestHighLevelClient client = new RestHighLevelClient(
                    RestClient.builder(
                            new HttpHost("127.0.0.1", 9200, "http")));
            return client;
        }
    
    }
    
    

    APIs 测试

    package com.llp.elasticsearch;
    
    import com.alibaba.fastjson.JSON;
    import com.llp.elasticsearch.entity.User;
    import org.assertj.core.util.Lists;
    import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
    import org.elasticsearch.action.bulk.BulkRequest;
    import org.elasticsearch.action.bulk.BulkResponse;
    import org.elasticsearch.action.delete.DeleteRequest;
    import org.elasticsearch.action.delete.DeleteResponse;
    import org.elasticsearch.action.get.GetRequest;
    import org.elasticsearch.action.get.GetResponse;
    import org.elasticsearch.action.index.IndexRequest;
    import org.elasticsearch.action.index.IndexResponse;
    import org.elasticsearch.action.search.SearchRequest;
    import org.elasticsearch.action.search.SearchResponse;
    import org.elasticsearch.action.support.master.AcknowledgedResponse;
    import org.elasticsearch.action.update.UpdateRequest;
    import org.elasticsearch.action.update.UpdateResponse;
    import org.elasticsearch.client.RequestOptions;
    import org.elasticsearch.client.RestHighLevelClient;
    import org.elasticsearch.client.indices.CreateIndexRequest;
    import org.elasticsearch.client.indices.CreateIndexResponse;
    import org.elasticsearch.client.indices.GetIndexRequest;
    import org.elasticsearch.client.indices.GetIndexResponse;
    import org.elasticsearch.common.unit.TimeValue;
    import org.elasticsearch.common.xcontent.XContentType;
    import org.elasticsearch.index.query.MatchQueryBuilder;
    import org.elasticsearch.index.query.QueryBuilders;
    import org.elasticsearch.search.SearchHit;
    import org.elasticsearch.search.builder.SearchSourceBuilder;
    import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.test.context.SpringBootTest;
    
    import java.io.IOException;
    import java.util.List;
    import java.util.concurrent.TimeUnit;
    
    @SpringBootTest
    class ElasticsearchApplicationTests {
    
        @Autowired
        @Qualifier("restHighLevelClient")
        private RestHighLevelClient client;
    
        /**
         * 创建索引
         * @throws IOException
         */
        @Test
        void testCreateIndex() throws IOException {
            CreateIndexRequest request = new CreateIndexRequest("llp_index");
            CreateIndexResponse createIndexResponse
                    =client.indices().create(request, RequestOptions.DEFAULT);
            System.out.println(createIndexResponse);
        }
    
        /**
         * 获取索引
         * @throws IOException
         */
        @Test
        void testGetIndex() throws IOException {
            GetIndexRequest request = new GetIndexRequest("llp_index");
            GetIndexResponse getIndexResponse = client.indices().get(request,RequestOptions.DEFAULT);
            boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
            System.out.println(exists);
            System.out.println(getIndexResponse);
        }
    
        /**
         * 删除索引
         * @throws IOException
         */
        @Test
        void deleteIndex() throws IOException {
            DeleteIndexRequest request = new DeleteIndexRequest("llp_index");
            AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
            System.out.println(delete.isAcknowledged());
            System.out.println(delete);
        }
    
        //创建文档
        @Test
        void testAddDocument() throws IOException {
            //创建对象
            User user = new User("狂神说",3);
            //创建请求
            IndexRequest request = new IndexRequest("llp_index");
            request.id("1");
            request.timeout(TimeValue.timeValueSeconds(2));
            request.source(JSON.toJSONString(user), XContentType.JSON);
            IndexResponse indexResponse = client.index(request,RequestOptions.DEFAULT);
            System.out.println(indexResponse.toString());
            System.out.println(indexResponse.status());
        }
    
        // 判断此id是否存在这个索引库中
        @Test
        void testIsExists() throws IOException {
            GetRequest getRequest = new GetRequest("llp_index","1");
            getRequest.fetchSourceContext(new FetchSourceContext(false));
            getRequest.storedFields("_none_");
            boolean exists = client.exists(getRequest, RequestOptions.DEFAULT);
            System.out.println(exists);
        }
    
        //获取文档记录
        @Test
        void testGetDocument() throws IOException {
            GetRequest getRequest = new GetRequest("llp_index","1");
            GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
            System.out.println(getResponse.isExists());
            System.out.println(getResponse.getSourceAsString());
            System.out.println(getResponse);
        }
    
        //更新文档记录
        @Test
        void testUpdateDocument() throws IOException {
            UpdateRequest request = new UpdateRequest("llp_index","1");
            request.timeout(TimeValue.timeValueSeconds(2));
            User user = new User("孙悟空",28);
            request.doc(JSON.toJSONString(user),XContentType.JSON);
            UpdateResponse updateResponse = client.update(request,RequestOptions.DEFAULT);
            System.out.println(updateResponse.status());
        }
    
        //删除文档记录
        @Test
        void testDelete() throws IOException {
            DeleteRequest request = new DeleteRequest("llp_index","1");
            request.timeout(TimeValue.timeValueSeconds(2));
            DeleteResponse delete = client.delete(request, RequestOptions.DEFAULT);
            System.out.println(delete.status());
        }
    
        //批量添加数据
        @Test
        void testBulkRequest() throws IOException {
            BulkRequest bulkRequest = new BulkRequest();
            bulkRequest.timeout(TimeValue.timeValueMinutes(2));
            List<User> users = Lists.newArrayList();
            users.add(new User("孙悟空",1));
            users.add(new User("猪八戒",1));
            users.add(new User("沙悟净",1));
            users.add(new User("唐僧",1));
            users.add(new User("白骨精",1));
            users.add(new User("蜘蛛精",1));
            for (int i = 0; i < users.size(); i++) {
                bulkRequest.add(new IndexRequest("llp_index").id(""+(i+1))
                        .source(JSON.toJSONString(users.get(i)),XContentType.JSON));
            }
            BulkResponse bulk = client.bulk(bulkRequest, RequestOptions.DEFAULT);
            //是否添加失败
            System.out.println(bulk.hasFailures());
        }
    
        //查询测试
        @Test
        void testSearch() throws IOException {
            SearchRequest request = new SearchRequest("llp_index");
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    //        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "孙悟空");
            MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name", "孙");
    //        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
            searchSourceBuilder.query(matchQueryBuilder);
            searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
            request.source(searchSourceBuilder);
            SearchResponse searchResponse = client.search(request, RequestOptions.DEFAULT);
            System.out.println(JSON.toJSONString(searchResponse.getHits()));
            for (SearchHit documentFields : searchResponse.getHits()) {
                System.out.println(documentFields.getSourceAsMap());
            }
        }
    
    
    
    
    }
    
    

    实体类

    package com.llp.elasticsearch.entity;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import org.springframework.stereotype.Component;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Component
    public class User {
        private String name;
        private int age;
    }
    

    实战测试

    初始化项目

    1、启动es服务和客户端
    2、使用springboot快速构建服务

    image.png

    3、修改版本依赖!

    <properties>
    <java.version>1.8</java.version>
    <!-- 这里SpringBoot默认配置的版本不匹配,我们需要自己配置版本! -->
    <elasticsearch.version>7.6.1</elasticsearch.version>
    </properties>
    
    

    4、配置 application.properties 文件

    server.port=9090
    # 关闭thymeleaf缓存
    spring.thymeleaf.cache=false
    

    5、导入前端的素材!修改为Thymeleaf支持的格式!

    <html xmlns:th="http://www.thymeleaf.org">
    

    6、编写IndexController进行跳转测试!

    image.png

    jsoup讲解

    1、导入jsoup的依赖

    <dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.13.1</version>
    </dependency>
    

    2、编写一个工具类 HtmlParseUtil

    package com.llp.elasticsearchjd.utils;
    
    import com.llp.elasticsearchjd.pojo.Content;
    import org.apache.commons.codec.Encoder;
    import org.jsoup.Jsoup;
    import org.jsoup.nodes.Document;
    import org.jsoup.nodes.Element;
    import org.jsoup.select.Elements;
    import org.springframework.stereotype.Component;
    
    import java.io.IOException;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.List;
    
    @Component
    public class HtmlParseUtil {
        public static void main(String[] args) throws IOException {
        // jsoup不能抓取ajax的请求,除非自己模拟浏览器进行请求!
        // 1、https://search.jd.com/Search?keyword=java
            String url = "https://search.jd.com/Search?keyword=java";
        // 2、解析网页(需要联网)
            Document document = Jsoup.parse(new URL(url), 30000);
    
            // 3、抓取搜索到的数据!
        // Document 就是我们JS的Document对象,你可以看到很多JS语法
            Element element = document.getElementById("J_goodsList");
        // 4、找到所有的li元素
            Elements elements = element.getElementsByTag("li");
        // 获取京东的商品信息
            for (Element el : elements) {
        // 这种网站,一般为了保证效率,一般会延时加载图片
                //https://img13.360buyimg.com/n1/s200x200_jfs/t1/178411/10/177/290041/607f7540E3f115804/5bb57caab13b6340.jpg
        // String img = el.getElementsByTag("img").eq(0).attr("src");
                String img = el.getElementsByTag("img").eq(0).attr("data-lazy-img");
                String price = el.getElementsByClass("p-price").eq(0).text();
                String title = el.getElementsByClass("p-name").eq(0).text();
                System.out.println(img);
                System.out.println(price);
                System.out.println(title);
                System.out.println("================================");
            }
        }
    
        public List<Content> parseJD(String keyword) throws Exception {
            // jsoup不能抓取ajax的请求,除非自己模拟浏览器进行请求!
            // 1、https://search.jd.com/Search?keyword=java
            String url = "https://search.jd.com/Search?keyword="+keyword;
            // 2、解析网页(需要联网)
            Document document = Jsoup.parse(new URL(url), 30000);
            // 3、抓取搜索到的数据!
            // Document 就是我们JS的Document对象,你可以看到很多JS语法
            Element element = document.getElementById("J_goodsList");
            // 4、找到所有的li元素
            Elements elements = element.getElementsByTag("li");
    
            List<Content> contents = new ArrayList<>();
            // 获取京东的商品信息
            for (Element el : elements) {
                // 这种网站,一般为了保证效率,一般会延时加载图片
                // String img = el.getElementsByTag("img").eq(0).attr("src");
                String img = "https:"+el.getElementsByTag("img").eq(0).attr("data-lazy-img");
                String title = el.getElementsByClass("p-name").eq(0).text();
                String price = el.getElementsByClass("p-price").eq(0).text();
                contents.add(new Content(img,title,price));
            }
            return contents;
        }
    }
    

    3、封装一个实体类保存爬取下来的数据

    package com.llp.elasticsearchjd.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Content {
        private String img;
        private String title;
        private String price;
    }
    
    

    业务编写

    1、导入ElasticsearchClientConfig 配置类

    package com.llp.elasticsearchjd.config;
    
    import org.apache.http.HttpHost;
    import org.elasticsearch.client.RestClient;
    import org.elasticsearch.client.RestHighLevelClient;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class ElasticsearchClientConfig {
        @Bean
        public RestHighLevelClient restHighLevelClient() {
            RestHighLevelClient client = new RestHighLevelClient(
                    RestClient.builder(
                            new HttpHost("127.0.0.1", 9200, "http")));
            return client;
        }
    }
    

    2、编写业务!

    package com.llp.elasticsearchjd.service;
    
    import com.alibaba.fastjson.JSON;
    import com.llp.elasticsearchjd.pojo.Content;
    import com.llp.elasticsearchjd.utils.HtmlParseUtil;
    import org.elasticsearch.action.bulk.BulkRequest;
    import org.elasticsearch.action.bulk.BulkResponse;
    import org.elasticsearch.action.index.IndexRequest;
    import org.elasticsearch.action.search.SearchRequest;
    import org.elasticsearch.action.search.SearchResponse;
    import org.elasticsearch.client.RequestOptions;
    import org.elasticsearch.client.RestHighLevelClient;
    import org.elasticsearch.client.indices.CreateIndexRequest;
    import org.elasticsearch.client.indices.CreateIndexResponse;
    import org.elasticsearch.client.indices.GetIndexRequest;
    import org.elasticsearch.client.indices.GetIndexResponse;
    import org.elasticsearch.common.text.Text;
    import org.elasticsearch.common.unit.TimeValue;
    import org.elasticsearch.common.xcontent.XContentType;
    import org.elasticsearch.index.query.QueryBuilders;
    import org.elasticsearch.index.query.TermQueryBuilder;
    import org.elasticsearch.search.SearchHit;
    import org.elasticsearch.search.builder.SearchSourceBuilder;
    import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
    import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Service;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.TimeUnit;
    
    @Service
    public class ContentService {
        @Autowired
        @Qualifier("restHighLevelClient")
        private RestHighLevelClient client;
    
        public Boolean parseContent(String keyword) throws Exception {
            //获取jd_goods索引
            GetIndexRequest getIndexRequest = new GetIndexRequest("jd_goods");
            boolean exists = client.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
            //判断jd_goods索引是否存在
            if(!exists){
                //不存在则创建索引
                CreateIndexRequest createIndexRequest = new CreateIndexRequest("jd_goods");
                client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
            }
            // 解析查询出来的数据
            List<Content> contents = new HtmlParseUtil().parseJD(keyword);
            // 封装数据到索引库中!
            BulkRequest bulkRequest = new BulkRequest();
            bulkRequest.timeout(TimeValue.timeValueMinutes(2));
            //bulkRequest.timeout("2m");
            for (int i = 0; i < contents.size(); i++) {
                bulkRequest.add(new IndexRequest("jd_goods")
                        .source(JSON.toJSONString(contents.get(i)), XContentType.JSON));
            }
            BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
            return !bulkResponse.hasFailures();
        }
    
        public List<Map<String, Object>> searchContentPage(String keyword, int pageNo, int pageSize) throws IOException {
            // 基本的参数判断!
            if (pageNo <= 1) {
                pageNo = 1;
            }
            //获取jd_goods索引
            GetIndexRequest getIndexRequest = new GetIndexRequest("jd_goods");
            boolean exists = client.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
            //判断jd_goods索引是否存在
            if(!exists){
                //不存在则创建索引
                CreateIndexRequest createIndexRequest = new CreateIndexRequest("jd_goods");
                client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
            }
            // 基本的条件搜索
            SearchRequest searchRequest = new SearchRequest("jd_goods");
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            // 分页
            sourceBuilder.from(pageNo);
            sourceBuilder.size(pageSize);
            // 精准匹配 QueryBuilders 根据自己要求配置查询条件即可!
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title",
                    keyword);
            sourceBuilder.query(termQueryBuilder);
            sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
            // 搜索
            searchRequest.source(sourceBuilder);
            SearchResponse response = client.search(searchRequest,
                    RequestOptions.DEFAULT);
            // 解析结果!
            List<Map<String, Object>> list = new ArrayList<>();
            for (SearchHit documentFields : response.getHits().getHits()) {
                list.add(documentFields.getSourceAsMap());
            }
            return list;
        }
    
        public List<Map<String, Object>> highlightSearch(String keyword, int pageNo, int pageSize) throws IOException {
            // 基本的参数判断!
            if (pageNo <= 1) {
                pageNo = 1;
            }
            //获取jd_goods索引
            GetIndexRequest getIndexRequest = new GetIndexRequest("jd_goods");
            boolean exists = client.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
            //判断jd_goods索引是否存在
            if(!exists){
                //不存在则创建索引
                CreateIndexRequest createIndexRequest = new CreateIndexRequest("jd_goods");
                client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
            }
            // 基本的条件搜索
            SearchRequest searchRequest = new SearchRequest("jd_goods");
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            // 分页
            sourceBuilder.from(pageNo);
            sourceBuilder.size(pageSize);
            // 精准匹配 QueryBuilders 根据自己要求配置查询条件即可!
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title",
                    keyword);
            sourceBuilder.query(termQueryBuilder);
    
            //高亮构建!
            HighlightBuilder highlightBuilder = new HighlightBuilder();//生成高亮查询器
            highlightBuilder.field("title");
            highlightBuilder.requireFieldMatch(false); //如果要多个字段高亮,这项要为false
            highlightBuilder.preTags("<span style=\"color:red\">"); //高亮设置
            highlightBuilder.postTags("</span>");
            sourceBuilder.highlighter(highlightBuilder);
    
            sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
            // 搜索
            searchRequest.source(sourceBuilder);
            SearchResponse response = client.search(searchRequest,
                    RequestOptions.DEFAULT);
            // 解析结果!
            List<Map<String, Object>> list = new ArrayList<>();
            for (SearchHit hit : response.getHits().getHits()) {
                //获取高亮字段
                Map<String, HighlightField> highlightFields =
                        hit.getHighlightFields();
                HighlightField titleField = highlightFields.get("title");
                Map<String, Object> source = hit.getSourceAsMap();
                //千万记得要记得判断是不是为空,不然你匹配的第一个结果没有高亮内容,那么就会报空指针异常,这个错误一开始真的搞了很久
                if(titleField!=null){
                    Text[] fragments = titleField.fragments();
                    String name = "";
                    for (Text text : fragments) {
                        name += text;
                    }
                    source.put("title", name); //高亮字段替换掉原本的内容
                }
                list.add(source);
            }
            return list;
        }
    }
    
    

    3、controller

    package com.llp.elasticsearchjd.controller;
    
    import com.llp.elasticsearchjd.service.ContentService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.List;
    import java.util.Map;
    
    @RestController
    public class ContentController {
        @Autowired
        private ContentService contentService;
    
        @GetMapping("/parse/{keyword}")
        public Boolean parse(@PathVariable("keyword") String keyword) throws Exception {
            return contentService.parseContent(keyword);
        }
    
        //http://localhost:9090/search/java/1/10
        @GetMapping("/search/{keyword}/{pageNo}/{pageSize}")
        public List<Map<String, Object>> search(@PathVariable("keyword") String keyword,
                                                @PathVariable("pageNo") int pageNo,
                                                @PathVariable("pageSize") int pageSize) throws Exception {
            return contentService.searchContentPage(keyword, pageNo, pageSize);
        }
    
        @GetMapping("/highlight/search/{keyword}/{pageNo}/{pageSize}")
        public List<Map<String, Object>> highlightSearch(@PathVariable("keyword") String keyword,
                                                @PathVariable("pageNo") int pageNo,
                                                @PathVariable("pageSize") int pageSize) throws Exception {
            return contentService.highlightSearch(keyword, pageNo, pageSize);
        }
    
    }
    
    

    前端逻辑、搜索高亮

    1、定义导入vue和axios的依赖!

    <script th:src="@{/js/axios.js}"></script>
    <script th:src="@{/js/vue.min.js}"></script>
    

    2、初始化Vue对象,给外层div绑定app对象!

    <script>
    new Vue({
    el: '#app',
    data: {
    keyword: '', // 搜索关键字
    results: [] // 搜索的结果
    }
    })
    </script>
    

    3、绑定搜索框及相关事件!

    image.png

    4、编写方法,获取后端传递的数据!

    <script>
        new Vue({
            el: '#app',
            data: {
                keyword: '',
                results: []
            },
            methods: {
                searchKey(){
                    var keyword = this.keyword;
                    console.log(keyword);
                    axios.get('/highlight/search/'+keyword+"/1/10").then(response=>{
                        console.log(response);
                        this.results = response.data;
                    });
                }
            }
        })
    </script>
    

    5、渲染解析回来的数据!

    image.png

    3、前端vue指令解析html!

    <!--标题-->
    <p class="productTitle">
    <a v-html="result.title"> </a>
    </p>
    

    4、最终效果!

    image.png

    展开全文
  • 通过VIP获取的内部教程,全套完整的 word excel ppt操作教程
  • 一、如果只要选择内容,则可按Ctrl F调出查找对话框(word2013可先按Ctrl H调出替换对话框,再点击上面的查找),然后输入你要选择的内容,点击“在以下项中查找→主文档”,即可选中所有相同内容。 二、 用查找...
  • EditPlus使用技巧

    2021-01-20 20:35:10
    中文的 点击视图,选中文字加亮即可 英文的 点击view,选中Word Highlighting
  • 关于vba操作excel及word的常见demo,包含在rpa操作excel及提取word内容案例,仅供学习使用
  • 如何去掉word修订模式

    万次阅读 2020-12-22 14:22:54
    如何去除word文档中的修订模式工具/原料:word2007软件。1.打开word文档,单击“审阅”菜单,单击“修订”命令启用修订功能,如下图:2.对文档进行的修改都会被标记,如下图:3.单击“修订”命令即可关闭修订功能,...
  • notepad++ 去除 重复

    万次阅读 2013-11-22 11:30:26
    例如1:去除重复行 先安装TextFx插件 在菜单TextFX-->TextFX Tools下面进行操作 1 确定“sort outputs only unique” 该选项 已经选择 2 选择要去除重复行的文本 3 选择sort lines case sensitive" 或者
  • Word2010教程

    2013-11-15 10:48:01
    Word2010教程
  • word2010教程

    2014-10-29 10:01:16
    word2010一些操作,有130内容很丰富,就是有点乱,想要的同学随便拿
  • WORD_应用技巧案例

    2013-11-12 10:40:57
    WORD_应用技巧案例,1. Word文档自我防卫 文档也有安全性问题,尤其对于Office文档,为了让您的文档更安全,Office提供了比较完善的安全和文档保护功能
  • 日常生活中我们使用搜索工具尝试查询一些信息的时候,常常可以看到返回的结果集中和我们查询条件相符合的字段被特殊的颜色所标记,这就是结果高亮显示。通过高亮显示用户可以明显的发现查询匹配的位置,ES使用...
  • UltraEdit技巧总结

    千次阅读 2020-12-20 14:30:08
    调整,添加语法高亮显示 advanced/configuration/syntax Highlighting 点击下边的full path name for word list后边的open 打开的文件如:d:\Program Files\UltraEdit\WORDFILE.TXT 就是UE语法高亮显示的配置文件 /...
  • word2007教程

    2013-11-13 23:34:40
    WORD 2007使用大全,教你如何使用word
  • word2003使用技巧大全

    2013-12-10 14:58:12
    word2003使用技巧大全 在默认情况下,我们用Word打开WPS文档时,系统提示打不开,这是不是就代表Office不能读取WPS文件呢?其实不是,只要你经过一些简单的操作,就能达到目的,下面就是具体的方法。 在默认情况下...
  • 文章目录参考高亮unified高亮显示器plain高亮显示器fvh高亮显示器偏移策略高亮配置高亮实例覆盖全局配置指定高亮查询设置高亮显示器类型配置高亮显示标签source上的高亮高亮显示所有字段组合多字段匹配显式排序高亮...
  • highlighter高亮检索详解 参考官网https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-highlighting.html#highlighting-settings 什么是highlight Highlight您能够从搜索结果中的...
  • Word2002在新建文档任务窗格中显示了你最近打开的4个文档的列表,用这种方式可以非常轻松地打开文档,不过你可能常常会感觉只显示最近的4个文档有些不够用,想在该列表中看到第5个或更多打开过的文档吗?方法如下:...
  • 解决方案:重复3、4步骤; 3.2 安装HighLight后在3步骤中的加载并没有看见HighLight加载?? 解决方案:进入步骤4,选择步骤4中的添加,尝试手动添加HighLight安装路径下的.dll文件,具体哪一个自己try一...
  • word 页码 罗马数字怎么从1开始

    千次阅读 2021-08-01 01:25:09
    word 页码 罗马数字怎么从1开始第一步:在前几页结束的地方点“插入”——>“分隔符”作用是区别前几页和正文的内容;第二步:点击“视图”——>“页眉和页脚”你就能看到页眉和页脚了;还有页眉和页脚的设置...
  • word 使用技巧ppt

    2010-10-18 15:06:52
    word 使用技巧ppt,分享是一种快乐
  • 然后会发现有重复的字体,将活跃的字体都改为“访问”一为“用户”的字体: 重启Microsoft Office for Mac,此时不会出现字体缺失的问题了,字体显示也为Windows下的状态。 2.在macOS下编辑文档注意字体的使用,...
  • Word2003使用技巧大全

    2010-08-17 13:39:56
    Word2003使用技巧大全 Word2003使用技巧大全 Word2003使用技巧大全
  • Lucene笔记+PaodingAnalyzer+高亮显示

    千次阅读 2010-08-25 11:55:00
      [1] (1) Lucene是一个基于Java全文搜索引擎,利用它可以...也就是说不管是MS word, Html ,pdf还是其他什么形式的文件只要你可以从中抽取出文字形式的内容就可以被Lucene所用。 (3)<b

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,828
精华内容 1,931
关键字:

word高亮显示重复项