zoukankan      html  css  js  c++  java
  • ElasticSearch7使用指导

    目录结构:

    一、es概述

    二、es安装/head插件安装/kibana安装

    三、es核心概念

    四、IK分词器

    五、RestFul操作

    六、CRUD(增删改查)

    七、Springboot集成es

    ---------------------------------------分割线:正文--------------------------------------------------------

    一、es概述

    1、Doug Cutting

    (1)创建了Lucene,(es与solr基于Lucene), 开源且功能为一套信息检索工具包,jar包

    (2)实现了NDFS(分布式文件存储系统)基于Google的GFS系统

    (3)将NDFS与MapReduce升级改造,重新命名为Hadoop,hadoop之父

    (4)基于Google的BigTable,实现了HBase

    2、ElasticSearch概述 

    (1)概念:开源的分布式全文检索引擎

    (2)优点:近乎实时存储、检索数据,扩展性好,可扩展上百台服务器,处理PB级别的数据,基于Lucene核心,通过RestFul API使全文搜索变得简单

    (3)谁在使用:维基百科,搜狐新闻,Github等等

    3、ES与solr的区别

    (1)ES是RestFul API,而Solr是WebService API

    (2)单纯已有的数据搜索,solr更快

    (3)建立索引后的数据,Solr会产生io阻塞,es有明显的优势

    (4)数据量增加,Solr的搜索效率会变低,es不受影响

     

    二、es安装/head插件安装/kibana安装

     声明:安装条件,JDK1.8+

     1、es下载:

    (1)es客户端:https://www.elastic.co/cn/downloads/elasticsearch

    (2)ik分词器:https://github.com/medcl/elasticsearch-analysis-ik/releases

    (3)kibana:https://www.elastic.co/cn/downloads/kibana

    (4)head插件:https://github.com/mobz/elasticsearch-head/archive/master.zip

    (5)chrome-es插件:https://github.com/mobz/elasticsearch-head/blob/master/crx/es-head.crx

    2、es客户端windows安装

     (1)安装步骤

    elasticsearch-7.12.1-windows-x86_64.zip 解压即可。

    (2)相关文件

    bin:启动文件

    config:配置文件

    - - log4j2.properties:日志配置

    - - elasticsearch.yml:es配置文件,默认9200端口

    - - jvm.options:java虚拟机相关配置

    lib:相关jar包

    modules:功能模块

    plugins:插件!如ik分词器等

    logs:日志

    (3)启动es

    双击bin目录下elasticsearch.bat

     启动成功,默认访问9200端口

     浏览器访问:http://127.0.0.1:9200/ 返回相关配置信息

    3、安装可视化界面:es head的插件

    (1)解压缩:elasticsearch-head-master.zip

    (2)安装node.js:https://nodejs.org/en/

    cmd:node -v验证安装成功

    (3)安装cnpm:

    cmd安装淘宝镜像:npm install -g cnpm -registry=https://registry.npm.taobao.org

     cnpm -v验证安装成功

    (4)安装依赖:

    进入D:elasticsearchelasticsearch-head-master目录;执行cnpm install;

     (5)运行npm run start启动npm

     (6)浏览器访问:http://localhost:9100

    由于跨域无法访问:

     (7)配置可跨域

    配置conf下elasticsearch.yml新增配置

    http.cors.enabled: true
    http.cors.allow-origin: "*"

    重启es服务,再次访问,集群连接正常

    4、Kibana安装

    (1)Kibana安装的版本要与es版本对应:

    (2)启动测试:运行bin下kibana.bat并测试对应的接口

    (3)开发工具 http://localhost:5601

     

     

     (4)配置中文:config下kibana.yml,新增配置并重启服务

    i18n.locale: "zh-CN"

     

     

    三、es核心概念

     1、elasticsearch是面向文档的(一切都是json),与关系型数据库的对比

    RelationDB  -> Elasticsearch

    数据库(database) -> 索引(indices)

    表(tables) -> types(慢慢会被弃用)

    行(rows) -> documents

    字段(columns) -> fields

    2、es物理设计:

    elasticsearch在后台把每个索引划分成多个分片,每个分片在集群中的不同的服务间迁移。

    默认的集群名就是elasticsearch

    3、逻辑设计:

    (1)文档:最小单位,即一条条数据

    (2)类型:text、date等类型

    (3)索引:数据库

    4、es字段类型:

    text:会被分词器解析

    keyword:不会被分词器解析

     

    四、IK分词器

    1、分词:

    把一段中文或别的划分为一个个关键字,我们在搜索时候会把自己的信息进行分词,默认的中文分词器将每个字看成一个词,我们需安装使用中文分词器ik来解决

    2、安装ik分词器:

    (1)下载:https://github.com/medcl/elasticsearch-analysis-ik/releases

    (2)安装:elasticsearch-analysis-ik-7.12.1.zip放入elasticsearch-7.12.1plugins目录下解压缩即可使用:

     (3)重启观察ES,加载插件成功

    [2021-05-12T23:00:05,420][INFO ][o.e.p.PluginsService     ] [DESKTOP-GIEVO4Q] loaded plugin [analysis-ik]
    [2021-05-12T23:00:05,615][INFO ][o.e.e.NodeEnvironment    ] [DESKTOP-GIEVO4Q] using [1] data paths, mounts [[杞�欢 (D:)]], net usable_space [179.3gb], net total_space [300.7gb], types [NTFS]

    (4)验证通过:elasticsearch-plugin list

    D:elasticsearchelasticsearch-7.12.1in>elasticsearch-plugin list
    "warning: usage of JAVA_HOME is deprecated, use ES_JAVA_HOME"
    Future versions of Elasticsearch will require Java 11; your Java version from [C:Program FilesJavajdk1.8.0_271jre] does not meet this requirement. Consider switching to a distribution of Elasticsearch with a bundled JDK. If you are already using a distribution with a bundled JDK, ensure the JAVA_HOME environment variable is not set.
    ik

    3、使用kibana进行测试

    启动kibana进入控制台进行测试

    查看不同的分词器效果

    (1)ik_smart

    GET _analyze
    {
      "analyzer": "ik_smart",
      "text":"软件工程师"
    }

    查看运行结果:

    {
      "tokens" : [
        {
          "token" : "软件",
          "start_offset" : 0,
          "end_offset" : 2,
          "type" : "CN_WORD",
          "position" : 0
        },
        {
          "token" : "工程师",
          "start_offset" : 2,
          "end_offset" : 5,
          "type" : "CN_WORD",
          "position" : 1
        }
      ]
    }

    (2)ik_max_word

    GET _analyze
    {
      "analyzer": "ik_max_word",
      "text":"软件工程师"
    }

    查看运行结果:

    {
      "tokens" : [
        {
          "token" : "软件工程",
          "start_offset" : 0,
          "end_offset" : 4,
          "type" : "CN_WORD",
          "position" : 0
        },
        {
          "token" : "软件",
          "start_offset" : 0,
          "end_offset" : 2,
          "type" : "CN_WORD",
          "position" : 1
        },
        {
          "token" : "工程师",
          "start_offset" : 2,
          "end_offset" : 5,
          "type" : "CN_WORD",
          "position" : 2
        },
        {
          "token" : "工程",
          "start_offset" : 2,
          "end_offset" : 4,
          "type" : "CN_WORD",
          "position" : 3
        },
        {
          "token" : "",
          "start_offset" : 4,
          "end_offset" : 5,
          "type" : "CN_CHAR",
          "position" : 4
        }
      ]
    }

    4、将加入自己需要的词加到分词器的字典中

    打开:elasticsearch-7.12.1pluginsikconfigIKAnalyzer.cfg.xml,配置<entry key="ext_dict"></entry>内添加字段映射

     (1)配置前

    GET _analyze
    {
      "analyzer": "ik_max_word",
      "text":"我的妈"
    }

    查看运行结果:

    {
      "tokens" : [
        {
          "token" : "",
          "start_offset" : 0,
          "end_offset" : 1,
          "type" : "CN_CHAR",
          "position" : 0
        },
        {
          "token" : "",
          "start_offset" : 1,
          "end_offset" : 2,
          "type" : "CN_CHAR",
          "position" : 1
        },
        {
          "token" : "",
          "start_offset" : 2,
          "end_offset" : 3,
          "type" : "CN_CHAR",
          "position" : 2
        }
      ]
    }

    (2)配置

    <entry key="ext_dict">my.dic</entry>

    同目录下新增my.dic -> 我的妈

    (3)配置后重启es,控制台

    {
      "tokens" : [
        {
          "token" : "我的妈",
          "start_offset" : 0,
          "end_offset" : 3,
          "type" : "CN_WORD",
          "position" : 0
        }
      ]
    }

    查看运行结果:

    {
      "tokens" : [
        {
          "token" : "我的妈",
          "start_offset" : 0,
          "end_offset" : 3,
          "type" : "CN_WORD",
          "position" : 0
        }
      ]
    }

     

    五、RestFul索引基本操作

     1、基本Rest命令:

    PUT        |  localhost:9200/索引名称/类型名称/文档id                |   创建文档(指定文档id)

    POST     |  localhost:9200/索引名称/类型名称                          |    创建文档(随机文档id)

    POST     |  localhost:9200/索引名称/类型名称/文档id/_update |   修改文档

    DELETE |  localhost:9200/索引名称/类型名称/文档id               |   删除文档

    GET        |  localhost:9200/索引名称/类型名称/文档id               |   查询文档通过文档id

    POST     |  localhost:9200/索引名称/类型名称/文档id/_search  |   查询所有文档

    2、基础测试-创建索引

    创建一个索引:PUT /索引名/~类型名~/文档id

    PUT /test1/type1/1
    {
      "name":"小白学es",
      "age":3
    }

    查看响应,完成自动增加了索引,类似数据库

    #! [types removal] Specifying types in document index requests is deprecated, use the typeless endpoints instead (/{index}/_doc/{id}, /{index}/_doc, or /{index}/_create/{id}).
    {
      "_index" : "test1",
      "_type" : "type1",
      "_id" : "1",
      "_version" : 1,
      "result" : "created",
      "_shards" : {
        "total" : 2,
        "successful" : 1,
        "failed" : 0
      },
      "_seq_no" : 0,
      "_primary_term" : 1
    }

    3、基本数据类型

    (1)字符串类型:text、keyword

    (2)数值类型:long、integer、short、byte、double、half float、scaled、float

    (3)日期类型:date

    (4)te布尔类型:boolean

    (5)二进制类型:binary

    4、基础测试-创建并指定索引类型

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

    查看运行结果

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

    5、 基础测试-创建/查看默认信息

    (1)创建默认索引即文档

    PUT /test3/_doc/1
    {
      "name":"小白学es",
      "age":18,
      "bithday":"1997-07-07"
    }

    查看运行结果

    {
      "_index" : "test3",
      "_type" : "_doc",
      "_id" : "1",
      "_version" : 1,
      "result" : "created",
      "_shards" : {
        "total" : 2,
        "successful" : 1,
        "failed" : 0
      },
      "_seq_no" : 0,
      "_primary_term" : 1
    }

    插件查看

     (2)查看索引,默认配置字段类型!

    GET test3

    运行结果:

    {
      "test3" : {
        "aliases" : { },
        "mappings" : {
          "properties" : {
            "age" : {
              "type" : "long"
            },
            "bithday" : {
              "type" : "date"
            },
            "name" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            }
          }
        },
        "settings" : {
          "index" : {
            "routing" : {
              "allocation" : {
                "include" : {
                  "_tier_preference" : "data_content"
                }
              }
            },
            "number_of_shards" : "1",
            "provided_name" : "test3",
            "creation_date" : "1621064937104",
            "number_of_replicas" : "1",
            "uuid" : "f_-oPx65RwOk846CysvHRQ",
            "version" : {
              "created" : "7120199"
            }
          }
        }
      }
    }

    6、扩展,查看集群/索引信息:

    查看集群状态:

    GET _cat/health

    运行结果:

    1621065190 07:53:10 elasticsearch yellow 1 1 9 9 0 0 2 0 - 81.8%

    查看索引版本信息等:

    GET _cat/indices?v

    运行结果:

    health status index                           uuid                   pri rep docs.count docs.deleted store.size pri.store.size
    green  open   .kibana_7.12.1_001              aGaAAOZTSQyk_0ZU_lOXsg   1   0         54           23      4.2mb          4.2mb
    yellow open   test2                           3f9X5bcAQXKciY56Il5nfA   1   1          0            0       208b           208b
    yellow open   test3                           f_-oPx65RwOk846CysvHRQ   1   1          1            0      4.2kb          4.2kb
    green  open   .apm-custom-link                ZyIwiavFSJSFTbh2cMd8Tg   1   0          0            0       208b           208b
    green  open   .apm-agent-configuration        FD1qwkXwTTaCT6orvViPRg   1   0          0            0       208b           208b
    green  open   .kibana_task_manager_7.12.1_001 MIUHf-m7S_e8XrjZMduTpA   1   0          9          272    286.7kb        286.7kb
    green  open   .kibana-event-log-7.12.1-000001 qPBR8ea4Th24CnhfxKa3Sg   1   0          6            0     32.6kb         32.6kb
    green  open   .tasks                          uwcYsws9QkqK1CXKQiNI0Q   1   0         10            0       50kb           50kb

    7、基础测试-修改索引/文档

    (1)直接覆盖:PUT

    PUT /test3/_doc/1
    {
      "name":"小白学es2",
      "age":18,
      "bithday":"1997-07-07"
    }

    运行结果:version+1,result为updated

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

    插件查看结果:

     (2)update更新:POST

    POST /test3/_doc/1/_update
    {
      "doc":{
        "name":"小白学es3"
      }
    }

    运行结果:version+1,result为updated

    #! [types removal] Specifying types in document update requests is deprecated, use the endpoint /{index}/_update/{id} instead.
    {
      "_index" : "test3",
      "_type" : "_doc",
      "_id" : "1",
      "_version" : 3,
      "result" : "updated",
      "_shards" : {
        "total" : 2,
        "successful" : 1,
        "failed" : 0
      },
      "_seq_no" : 2,
      "_primary_term" : 1
    }

    插件查看结果:

    8、基础测试-删除索引

    (1)删除索引

    DELETE test1

    查看运行结果:

    {
      "acknowledged" : true
    }

    (2)删除索引中文档

    DELETE test3/_doc/1

    查看运行结果:

    {
      "_index" : "test3",
      "_type" : "_doc",
      "_id" : "1",
      "_version" : 4,
      "result" : "deleted",
      "_shards" : {
        "total" : 2,
        "successful" : 1,
        "failed" : 0
      },
      "_seq_no" : 3,
      "_primary_term" : 1
    }

     

    六、文档CRUD(增删改查)

    1、添加数据:PUT

    PUT /xiaobai/user/1
    {
      "name":"小白",
      "age":30,
      "desc":"一顿操作猛如虎,一看工资2500",
      "tags":["技术宅","直男"]
    }

    查看运行结果:

    #! [types removal] Specifying types in document index requests is deprecated, use the typeless endpoints instead (/{index}/_doc/{id}, /{index}/_doc, or /{index}/_create/{id}).
    {
      "_index" : "xiaobai",
      "_type" : "user",
      "_id" : "1",
      "_version" : 1,
      "result" : "created",
      "_shards" : {
        "total" : 2,
        "successful" : 1,
        "failed" : 0
      },
      "_seq_no" : 0,
      "_primary_term" : 1
    }

    插件查看:

    同样的方法添加一些数据:

    2、获取数据:GET

    GET xiaobai/user/1

    查看运行结果:

    #! [types removal] Specifying types in document get requests is deprecated, use the /{index}/_doc/{id} endpoint instead.
    {
      "_index" : "xiaobai",
      "_type" : "user",
      "_id" : "1",
      "_version" : 1,
      "_seq_no" : 0,
      "_primary_term" : 1,
      "found" : true,
      "_source" : {
        "name" : "小白",
        "age" : "30",
        "desc" : "一顿操作猛如虎,一看工资2500",
        "tags" : [
          "技术宅",
          "直男"
        ]
      }
    }

    3、更新数据:PUT/POST

    (1)PUT修改:全量字段更新

    1号数据name:小白 -> 白神

    PUT /xiaobai/user/1
    {
      "name":"白神",
      "age":"30",
      "desc":"一顿操作猛如虎,一看工资2500",
      "tags":["技术宅","直男"]
    }

    查看运行结果:

    #! [types removal] Specifying types in document index requests is deprecated, use the typeless endpoints instead (/{index}/_doc/{id}, /{index}/_doc, or /{index}/_create/{id}).
    {
      "_index" : "xiaobai",
      "_type" : "user",
      "_id" : "1",
      "_version" : 2,
      "result" : "updated",
      "_shards" : {
        "total" : 2,
        "successful" : 1,
        "failed" : 0
      },
      "_seq_no" : 5,
      "_primary_term" : 2
    }

    (2)POST _update:指定字段更新(推荐使用)

    POST /xiaobai/user/1/_update
    {
      "doc":{
        "name":"小白java"
      }
    }

    查看运行结果:

    #! [types removal] Specifying types in document update requests is deprecated, use the endpoint /{index}/_update/{id} instead.
    {
      "_index" : "xiaobai",
      "_type" : "user",
      "_id" : "1",
      "_version" : 5,
      "result" : "updated",
      "_shards" : {
        "total" : 2,
        "successful" : 1,
        "failed" : 0
      },
      "_seq_no" : 8,
      "_primary_term" : 2
    }

    4、简单的搜索:GET _searcher

    text类型可以根据字段分词搜索,keyword关键词不会处理分词器

    GET /xiaobai/user/_search?q=name:小白

    查看运行结果:

    #! [types removal] Specifying types in search requests is deprecated.
    {
      "took" : 32,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 3,
          "relation" : "eq"
        },
        "max_score" : 1.9252907,
        "hits" : [
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "1",
            "_score" : 1.9252907,
            "_source" : {
              "name" : "小白",
              "age" : "30",
              "desc" : "一顿操作猛如虎,一看工资2500",
              "tags" : [
                "技术宅",
                "直男"
              ]
            }
          },
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "2",
            "_score" : 0.53899646,
            "_source" : {
              "name" : "小黑",
              "age" : "32",
              "desc" : "腹黑男猪脚",
              "tags" : [
                "旅游",
                "渣男",
                "游戏宅"
              ]
            }
          },
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "5",
            "_score" : 0.53899646,
            "_source" : {
              "name" : "小王",
              "age" : "32",
              "desc" : "社会性死亡",
              "tags" : [
                "技术宅",
                "暖男",
                "中年危机"
              ]
            }
          }
        ]
      }
    }

    5、复杂操作搜索: _searcher match - 分词模糊匹配

    {
      "query":{
        "match": {
          "name": "钻石王老五"
        }
      }
    }

    查看运行结果:

    {
        "took": 2,
        "timed_out": false,
        "_shards": {
            "total": 1,
            "successful": 1,
            "skipped": 0,
            "failed": 0
        },
        "hits": {
            "total": {
                "value": 4,
                "relation": "eq"
            },
            "max_score": 3.0311832,
            "hits": [
                {
                    "_index": "xiaobai",
                    "_type": "user",
                    "_id": "6",
                    "_score": 3.0311832,
                    "_source": {
                        "name": "钻石王老五",
                        "age": "32",
                        "desc": "社会性死亡",
                        "tags": [
                            "技术宅",
                            "暖男",
                            "中年危机"
                        ]
                    }
                },
                {
                    "_index": "xiaobai",
                    "_type": "user",
                    "_id": "4",
                    "_score": 1.9277248,
                    "_source": {
                        "name": "王五",
                        "age": "30",
                        "desc": "正宗青子",
                        "tags": [
                            "青年",
                            "吃货",
                            "旅游",
                            "胖子"
                        ]
                    }
                },
                {
                    "_index": "xiaobai",
                    "_type": "user",
                    "_id": "7",
                    "_score": 1.6832076,
                    "_source": {
                        "name": "钻石李老四",
                        "age": "32",
                        "desc": "社会性死亡",
                        "tags": [
                            "技术宅",
                            "暖男",
                            "中年危机"
                        ]
                    }
                },
                {
                    "_index": "xiaobai",
                    "_type": "user",
                    "_id": "5",
                    "_score": 1.2623059,
                    "_source": {
                        "name": "钻石123",
                        "age": "32",
                        "desc": "社会性死亡",
                        "tags": [
                            "技术宅",
                            "暖男",
                            "中年危机"
                        ]
                    }
                }
            ]
        }
    }

    6、复杂操作搜索: _searcher match_phrase - 完整模糊匹配

    GET /xiaobai/user/_search
    {
      "query":{
        "match_phrase": {
          "name": "钻石王老五"
        }
      }
    }

    查看运行结果:

    #! [types removal] Specifying types in search requests is deprecated.
    {
      "took" : 0,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 1,
          "relation" : "eq"
        },
        "max_score" : 3.0311837,
        "hits" : [
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "6",
            "_score" : 3.0311837,
            "_source" : {
              "name" : "钻石王老五",
              "age" : "32",
              "desc" : "社会性死亡",
              "tags" : [
                "技术宅",
                "暖男",
                "中年危机"
              ]
            }
          }
        ]
      }
    }

    7、复杂操作搜索: _searcher term - 完成精确匹配

    GET xiaobai/user/_search
    {
      "query":{
        "term": {
          "name.keyword": "钻石王老五"
        }
      }
    }

    查看运行结果:

    #! [types removal] Specifying types in search requests is deprecated.
    {
      "took" : 0,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 1,
          "relation" : "eq"
        },
        "max_score" : 1.2039728,
        "hits" : [
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "3",
            "_score" : 1.2039728,
            "_source" : {
              "name" : "钻石王老五",
              "age" : "19",
              "desc" : "社会性死亡",
              "tags" : [
                "技术宅",
                "暖男"
              ]
            }
          }
        ]
      }
    }

    8、复杂操作搜索: _searcher _source - 过滤展示结果

    GET /xiaobai/user/_search
    {
      "query":{
        "match_phrase": {
          "name": "钻石王老五"
        }
      },
        "_source":["name","desc"]
    }

    查看运行结果:

    #! [types removal] Specifying types in search requests is deprecated.
    {
      "took" : 11,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 1,
          "relation" : "eq"
        },
        "max_score" : 3.0311837,
        "hits" : [
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "6",
            "_score" : 3.0311837,
            "_source" : {
              "name" : "钻石王老五",
              "desc" : "社会性死亡"
            }
          }
        ]
      }
    }

    9、复杂操作搜索: _searcher sort - 排序(desc/asc)

    GET xiaobai/user/_search
    {
      "query":{
        "match_phrase": {
          "name": "钻石"
        }
      },
        "sort":[
          {
            "age":{
              "order":"desc"
            }
          }
          ]
    }

    查看运行结果:

    #! [types removal] Specifying types in search requests is deprecated.
    {
      "took" : 1,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 3,
          "relation" : "eq"
        },
        "max_score" : null,
        "hits" : [
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "2",
            "_score" : null,
            "_source" : {
              "name" : "钻石李老四",
              "age" : "32",
              "desc" : "社会性死亡",
              "tags" : [
                "技术宅",
                "暖男",
                "中年危机"
              ]
            },
            "sort" : [
              32
            ]
          },
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "4",
            "_score" : null,
            "_source" : {
              "name" : "钻石123",
              "age" : "28",
              "desc" : "废物一个",
              "tags" : [
                "技术宅"
              ]
            },
            "sort" : [
              28
            ]
          },
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "3",
            "_score" : null,
            "_source" : {
              "name" : "钻石王老五",
              "age" : "19",
              "desc" : "社会性死亡",
              "tags" : [
                "技术宅",
                "暖男"
              ]
            },
            "sort" : [
              19
            ]
          }
        ]
      }
    }

    10、复杂操作搜索: _searcher from size - 分页查询

    GET xiaobai/user/_search
    {
      "query":{
        "match_phrase": {
          "name": "钻石"
        }
      },
        "from":0,
        "size":2
    }

    查看运行结果:

    #! [types removal] Specifying types in search requests is deprecated.
    {
      "took" : 0,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 3,
          "relation" : "eq"
        },
        "max_score" : 0.77691567,
        "hits" : [
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "4",
            "_score" : 0.77691567,
            "_source" : {
              "name" : "钻石123",
              "age" : "28",
              "desc" : "废物一个",
              "tags" : [
                "技术宅"
              ]
            }
          },
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "2",
            "_score" : 0.62774795,
            "_source" : {
              "name" : "钻石李老四",
              "age" : "32",
              "desc" : "社会性死亡",
              "tags" : [
                "技术宅",
                "暖男",
                "中年危机"
              ]
            }
          }
        ]
      }
    }

    11、复杂操作搜索: _searcher bool - 联合查询 -must - 所有条件均符合

    GET xiaobai/user/_search
    {
      "query":{
        "bool": {
          "must":[{
            "match":{
              "name":"钻石王老五"
            }
          },
          {
            "match":{
              "age":28
            }
          }
            ]
        }
      }
    }

    查看运行结果:

    #! [types removal] Specifying types in search requests is deprecated.
    {
      "took" : 1,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 1,
          "relation" : "eq"
        },
        "max_score" : 1.7769157,
        "hits" : [
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "4",
            "_score" : 1.7769157,
            "_source" : {
              "name" : "钻石123",
              "age" : "28",
              "desc" : "废物一个",
              "tags" : [
                "技术宅"
              ]
            }
          }
        ]
      }
    }

    12、复杂操作搜索:_searcher bool - 联合查询 - should - 满足任意一个条件

    GET xiaobai/user/_search
    {
      "query":{
        "bool": {
          "should":[{
            "match":{
              "name":"小白"
            }
          },
          {
            "match":{
              "age":28
            }
          }
            ]
        }
      }
    }

    查看运行结果:

    #! [types removal] Specifying types in search requests is deprecated.
    {
      "took" : 1,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 2,
          "relation" : "eq"
        },
        "max_score" : 2.9761126,
        "hits" : [
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "1",
            "_score" : 2.9761126,
            "_source" : {
              "name" : "小白",
              "age" : 30,
              "desc" : "一顿操作猛如虎,一看工资2500",
              "tags" : [
                "技术宅",
                "直男"
              ]
            }
          },
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "4",
            "_score" : 1.0,
            "_source" : {
              "name" : "钻石123",
              "age" : "28",
              "desc" : "废物一个",
              "tags" : [
                "技术宅"
              ]
            }
          }
        ]
      }
    }

    13、复杂操作搜索:_searcher bool - 联合查询 - must_not - 不满足

    GET xiaobai/user/_search
    {
      "query":{
        "bool": {
          "must_not":[{
            "match":{
              "name":"小白"
            }
          },
          {
            "match":{
              "age":28
            }
          }
            ]
        }
      }
    }

    查看运行结果:

    #! [types removal] Specifying types in search requests is deprecated.
    {
      "took" : 1,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 2,
          "relation" : "eq"
        },
        "max_score" : 0.0,
        "hits" : [
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "2",
            "_score" : 0.0,
            "_source" : {
              "name" : "钻石李老四",
              "age" : "32",
              "desc" : "社会性死亡",
              "tags" : [
                "技术宅",
                "暖男",
                "中年危机"
              ]
            }
          },
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "3",
            "_score" : 0.0,
            "_source" : {
              "name" : "钻石王老五",
              "age" : "19",
              "desc" : "社会性死亡",
              "tags" : [
                "技术宅",
                "暖男"
              ]
            }
          }
        ]
      }
    }

    14、复杂操作搜索:_searcher bool - 联合查询 - filter - 过滤

    gt(>) gte(>=) lt(<) lte(<=)

    GET xiaobai/user/_search
    {
      "query":{
        "bool": {
          "must":[{
            "match":{
              "name":"钻石"
            }
          }
            ],"filter": 
              {"range": {
                "age": {
                  "gte": 10,
                  "lte": 30
                }
              }}
      
        }
      }
    }

    查看运行结果:

    #! [types removal] Specifying types in search requests is deprecated.
    {
      "took" : 1,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 2,
          "relation" : "eq"
        },
        "max_score" : 0.77691567,
        "hits" : [
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "4",
            "_score" : 0.77691567,
            "_source" : {
              "name" : "钻石123",
              "age" : "28",
              "desc" : "废物一个",
              "tags" : [
                "技术宅"
              ]
            }
          },
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "3",
            "_score" : 0.62774795,
            "_source" : {
              "name" : "钻石王老五",
              "age" : "19",
              "desc" : "社会性死亡",
              "tags" : [
                "技术宅",
                "暖男"
              ]
            }
          }
        ]
      }
    }

    15、复杂操作搜索:_searcher match - 匹配多个标签分词(空格隔开)

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

    查看运行结果:

    #! [types removal] Specifying types in search requests is deprecated.
    {
      "took" : 1,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 4,
          "relation" : "eq"
        },
        "max_score" : 0.6987428,
        "hits" : [
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "1",
            "_score" : 0.6987428,
            "_source" : {
              "name" : "小白",
              "age" : 30,
              "desc" : "一顿操作猛如虎,一看工资2500",
              "tags" : [
                "技术宅",
                "直男"
              ]
            }
          },
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "3",
            "_score" : 0.6987428,
            "_source" : {
              "name" : "钻石王老五",
              "age" : "19",
              "desc" : "社会性死亡",
              "tags" : [
                "技术宅",
                "暖男"
              ]
            }
          },
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "2",
            "_score" : 0.5337937,
            "_source" : {
              "name" : "钻石李老四",
              "age" : "32",
              "desc" : "社会性死亡",
              "tags" : [
                "技术宅",
                "暖男",
                "中年危机"
              ]
            }
          },
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "4",
            "_score" : 0.38828292,
            "_source" : {
              "name" : "钻石123",
              "age" : "28",
              "desc" : "废物一个",
              "tags" : [
                "技术宅"
              ]
            }
          }
        ]
      }
    }

    16、复杂操作搜索:_searcher bool term - 多个条件精确匹配

    GET xiaobai/user/_search
    {
      "query":{
        "bool": {
          "should":[{
            "term":{
              "name":"test01"
            }
          },
          {
            "term":{
              "name":"test02"
            }
          }
            ]}
        }
      }

    查看运行结果:

    #! [types removal] Specifying types in search requests is deprecated.
    {
      "took" : 0,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 2,
          "relation" : "eq"
        },
        "max_score" : 1.8809632,
        "hits" : [
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "4",
            "_score" : 1.8809632,
            "_source" : {
              "name" : "test01",
              "age" : "28",
              "desc" : "废物一个",
              "tags" : [
                "技术宅"
              ]
            }
          },
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "5",
            "_score" : 1.8809632,
            "_source" : {
              "name" : "test02",
              "age" : "28",
              "desc" : "废物一个",
              "tags" : [
                "技术宅"
              ]
            }
          }
        ]
      }
    }

    17、复杂操作搜索:_searcher highlight - (自定义高亮显示)

    GET xiaobai/user/_search
    {
      "query":{
          "match":{
            "name":"钻石" 
          }
        },
        "highlight":{
          "pre_tags": "<p class='key' style='color:red'>", 
          "post_tags": "</p>", 
          "fields": {
            "name":{}
          }
        }
      }

    查看运行结果:

    #! [types removal] Specifying types in search requests is deprecated.
    {
      "took" : 1,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 2,
          "relation" : "eq"
        },
        "max_score" : 1.3250337,
        "hits" : [
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "2",
            "_score" : 1.3250337,
            "_source" : {
              "name" : "钻石李老四",
              "age" : "32",
              "desc" : "社会性死亡",
              "tags" : [
                "技术宅",
                "暖男",
                "中年危机"
              ]
            },
            "highlight" : {
              "name" : [
                "<p class='key' style='color:red'>钻</p><p class='key' style='color:red'>石</p>李老四"
              ]
            }
          },
          {
            "_index" : "xiaobai",
            "_type" : "user",
            "_id" : "3",
            "_score" : 1.3250337,
            "_source" : {
              "name" : "钻石王老五",
              "age" : "19",
              "desc" : "社会性死亡",
              "tags" : [
                "技术宅",
                "暖男"
              ]
            },
            "highlight" : {
              "name" : [
                "<p class='key' style='color:red'>钻</p><p class='key' style='color:red'>石</p>王老五"
              ]
            }
          }
        ]
      }
    }

     

    七、Springboot集成es

     参考Java REST Client官网文档:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/index.html

    1、原生的依赖与初始化方法

    <dependency>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>elasticsearch-rest-high-level-client</artifactId>
        <version>7.12.1</version>
    </dependency>

    2、配置基本的项目:

    (1)创建sprint-boot module

     

     (2)module选择elasticsearch

     (3)查看pom对应的依赖包

     

     

     (4)调整导入es版本依赖与已本地安装的版本保持一致

     pom -> spring-boot-starter-parent -> pring-boot-dependencies -> <elasticsearch.version>7.9.3</elasticsearch.version>

    pom中调整为7.12.1,刷新

        <properties>
            <java.version>1.8</java.version>
            <spring-native.version>0.9.2</spring-native.version>
            <!-- 自定义es版本依赖,保证与本地一致 -->
            <elasticsearch.version>7.12.1</elasticsearch.version>
        </properties>

    查看后修改成功

     (5)构建RestHighLevelClient对象

    package com.testbk.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;
    
    //找到RestHighLevelClient对象,注入spring中,分析一波源码
    @Configuration //xml -bean
    public class ElasticSearchClientConfig {
        // spring < beans id="restHighLevelClient" class=RestHighLevelClient>
        @Bean
        public RestHighLevelClient restHighLevelClient(){
            RestHighLevelClient client = new RestHighLevelClient(
                    RestClient.builder(
                            new HttpHost("127.0.0.1",9200,"http")
                    )
            );
            return client;
        }
    }

    3、索引API操作

    (1)测试创建索引

        //测试索引的创建 Request
        @Test
        void testCreatIndex() throws IOException {
            //1、创建索引请求
            CreateIndexRequest request = new CreateIndexRequest("mrwhite_index");
            //2、客户端创建请求,indicesClient,请求后获取响应
            CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);
            System.out.println(createIndexResponse);
        }

    查看es插件,索引创建成功

    (2)测试获取索引 -> 判断索引是否存在

        //测试获取索引
        @Test
        void testExistIndex() throws IOException {
            GetIndexRequest request = new GetIndexRequest("mrwhite_index");
            boolean exists = client.indices().exists(request,RequestOptions.DEFAULT);
            System.out.println(exists);
        }

    查看运行结果:

    true

    (3)测试删除索引

        //测试删除索引
        @Test
        void testDeleteIndex() throws IOException {
            DeleteIndexRequest request = new DeleteIndexRequest("mrwhite_index");
            AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
            System.out.println(delete.isAcknowledged());
        }

    查看运行结果:同时索引被删除

    true

    4、文档API操作

     (1)测试添加文档

    创建用户对象

    package com.testbk.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Component
    public class User {
        private String name;
        private int age;
    }

    创建索引并向索引内添加文档

        //测试添加文档
        @Test
        void testAddDocument() throws IOException {
            //创建对象
            User user = new User("小白",30);
            //创建请求
            IndexRequest request = new IndexRequest("mrwhite_index");
            //规则  PUT /mrwhite_index/_doc/1
            request.id("1");
            request.timeout(TimeValue.timeValueSeconds(1));
            //将我们的数据放入请求json,添加fastjson依赖
            request.source(JSON.toJSONString(user), XContentType.JSON);
            //客户端发送请求
            IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);
            System.out.println(indexResponse.toString());
            System.out.println(indexResponse.status());
        }

    查看运行结果:

    IndexResponse[index=mrwhite_index,type=_doc,id=1,version=1,result=created,seqNo=0,primaryTerm=1,shards={"total":2,"successful":1,"failed":0}]
    CREATED

    (2)获取文档,判断是否存在

        //获取文档,判断是否存在 get /index/doc/1
        @Test
        void testIsExists() throws IOException {
            GetRequest getRequest = new GetRequest("mrwhite_index","1");
            //不获取返回的_source的上下文
            getRequest.fetchSourceContext(new FetchSourceContext(false));
            getRequest.storedFields("_none_");
            boolean exists = client.exists(getRequest, RequestOptions.DEFAULT);
            System.out.println(exists);
        }

    查看运行结果:

    true

    (3)获取文档的信息

        //获取文档的信息
        @Test
        void testGetDocument() throws IOException {
            GetRequest getRequest = new GetRequest("mrwhite_index","1");
            GetResponse documentFields = client.get(getRequest, RequestOptions.DEFAULT);
            System.out.println(documentFields.getSourceAsString());//打印文档的内容
            System.out.println(documentFields);
        }

    查看运行结果:

    {"age":30,"name":"小白"}
    {"_index":"mrwhite_index","_type":"_doc","_id":"1","_version":1,"_seq_no":0,"_primary_term":1,"found":true,"_source":{"age":30,"name":"小白"}}

    (4)更新文档的信息

        //更新文档的信息
        @Test
        void testUpdateDocument() throws IOException {
            UpdateRequest updateRequest = new UpdateRequest("mrwhite_index","1");
            updateRequest.timeout("1s");
            User user = new User("小白",32);
            updateRequest.doc(JSON.toJSONString(user),XContentType.JSON);
            UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT);
            System.out.println(updateResponse.status());
        }

    查看运行结果:OK

    (5)删除文档的信息

    	//删除文档的信息
    	@Test
    	void testDeleteDocument() throws IOException {
    		DeleteRequest request  = new DeleteRequest("mrwhite_inex", "1");
    		request.timeout("1s");
    		DeleteResponse deleteResponse = client.delete(request, RequestOptions.DEFAULT);
    		System.out.println(deleteResponse.status());
    	}
    

    查看运行结果:OK,对应文档被删除掉

     (6)批量操作文档

        //批量插入数据
        @Test
        void testBulkRequest() throws IOException {
            BulkRequest bulkRequest  = new BulkRequest();
            bulkRequest.timeout("10s");
            ArrayList<User> userList = new ArrayList<>();
            userList.add(new User("xiaobai",15));
            userList.add(new User("xiaobai",16));
            userList.add(new User("xiaobai",17));
            userList.add(new User("xiaobai",18));
            userList.add(new User("xiaobai",19));
            userList.add(new User("xiaobai",20));
            userList.add(new User("xiaobai",21));
            for (int i = 0; i <userList.size() ; i++) {
           //id不用的话会随机生成id bulkRequest.add(
    new IndexRequest("mrwhite_index").id(""+(i+1)).source(JSON.toJSONString(userList.get(i)),XContentType.JSON)); } BulkResponse bulkResponse=client.bulk(bulkRequest,RequestOptions.DEFAULT); System.out.println(bulkResponse.hasFailures()); //打印是否失败,fasle代表成功 }

    返回false:

     

     

     (7)查询文档

        //查询文档
        @Test
        void testSearch() throws IOException {
            SearchRequest searchRequest = new SearchRequest("mrwhite_index");
            //构建搜索条件
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            //精确匹配
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name","xiaobai");
            //匹配所有
            //MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAll();
            sourceBuilder.query(termQueryBuilder);
            sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
            searchRequest.source(sourceBuilder);
            SearchResponse searchResponse = client.search(searchRequest,RequestOptions.DEFAULT);
            System.out.println(JSON.toJSONString(searchResponse.getHits()));
            for(SearchHit documentFileds:searchResponse.getHits().getHits()){
                System.out.println(documentFileds.getSourceAsMap());
            }
        }

    查看运行结果:

    {"fragment":true,"hits":[{"fields":{},"fragment":false,"highlightFields":{},"id":"1","matchedQueries":[],"primaryTerm":0,"rawSortValues":[],"score":0.06453852,"seqNo":-2,"sortValues":[],"sourceAsMap":{"name":"xiaobai","age":15},"sourceAsString":"{"age":15,"name":"xiaobai"}","sourceRef":{"fragment":true},"type":"_doc","version":-1},{"fields":{},"fragment":false,"highlightFields":{},"id":"2","matchedQueries":[],"primaryTerm":0,"rawSortValues":[],"score":0.06453852,"seqNo":-2,"sortValues":[],"sourceAsMap":{"name":"xiaobai","age":16},"sourceAsString":"{"age":16,"name":"xiaobai"}","sourceRef":{"fragment":true},"type":"_doc","version":-1},{"fields":{},"fragment":false,"highlightFields":{},"id":"3","matchedQueries":[],"primaryTerm":0,"rawSortValues":[],"score":0.06453852,"seqNo":-2,"sortValues":[],"sourceAsMap":{"name":"xiaobai","age":17},"sourceAsString":"{"age":17,"name":"xiaobai"}","sourceRef":{"fragment":true},"type":"_doc","version":-1},{"fields":{},"fragment":false,"highlightFields":{},"id":"4","matchedQueries":[],"primaryTerm":0,"rawSortValues":[],"score":0.06453852,"seqNo":-2,"sortValues":[],"sourceAsMap":{"name":"xiaobai","age":18},"sourceAsString":"{"age":18,"name":"xiaobai"}","sourceRef":{"fragment":true},"type":"_doc","version":-1},{"fields":{},"fragment":false,"highlightFields":{},"id":"5","matchedQueries":[],"primaryTerm":0,"rawSortValues":[],"score":0.06453852,"seqNo":-2,"sortValues":[],"sourceAsMap":{"name":"xiaobai","age":19},"sourceAsString":"{"age":19,"name":"xiaobai"}","sourceRef":{"fragment":true},"type":"_doc","version":-1},{"fields":{},"fragment":false,"highlightFields":{},"id":"6","matchedQueries":[],"primaryTerm":0,"rawSortValues":[],"score":0.06453852,"seqNo":-2,"sortValues":[],"sourceAsMap":{"name":"xiaobai","age":20},"sourceAsString":"{"age":20,"name":"xiaobai"}","sourceRef":{"fragment":true},"type":"_doc","version":-1},{"fields":{},"fragment":false,"highlightFields":{},"id":"7","matchedQueries":[],"primaryTerm":0,"rawSortValues":[],"score":0.06453852,"seqNo":-2,"sortValues":[],"sourceAsMap":{"name":"xiaobai","age":21},"sourceAsString":"{"age":21,"name":"xiaobai"}","sourceRef":{"fragment":true},"type":"_doc","version":-1}],"maxScore":0.06453852,"totalHits":{"relation":"EQUAL_TO","value":7}}
    {name=xiaobai, age=15}
    {name=xiaobai, age=16}
    {name=xiaobai, age=17}
    {name=xiaobai, age=18}
    {name=xiaobai, age=19}
    {name=xiaobai, age=20}
    {name=xiaobai, age=21}

     

  • 相关阅读:
    从程序员到项目经理(5):程序员加油站 不是人人都懂的学习要点
    从程序员到项目经理(14):项目经理必须懂一点“章法”
    从程序员到项目经理(17):你不是一个人在战斗思维一换天地宽
    从程序员到项目经理(19):想改变任何人都是徒劳的
    tp5时间戳转换日期格式
    win10让人愤怒的磁盘占用100%问题
    uniapp将时间日期格式化的组件unidateformat的用法
    Asp.Net大型项目实践系列第二季(一)哥欲善其事,必先利其器...
    Asp.Net大型项目实践(9)ExtJs实现系统框架页(非iframe,附源码,在线demo)
    Asp.Net大型项目实践(10)基于MVC Action粒度的权限管理(在线demo,全部源码)
  • 原文地址:https://www.cnblogs.com/mrwhite2020/p/14716102.html
Copyright © 2011-2022 走看看