24、ElasticSearch

安装ElasticSearch

  • 安装1.8及以上的jdk
  • https://www.elastic.co/cn/downloads/elasticsearch 下载对应版本
  • 上传到云服务器 /usr/local/software/ 目录下
  • tar -zxvf elasticsearch-7.12.1-linux-x86_64.tar.gz解压
  • cd /usr/local/software/elasticsearch-7.12.1/bin
  • 执行./elasticsearch报错
    java.lang.RuntimeException: can not run elasticsearch as root
  • 新建es用户
  • cd /usr/local/software
  • groupadd esgroup
  • useradd esuser -g esgroup -p 123456
  • chown -R esuser:esgroup elasticsearch-7.12.1/
  • su - esuser
  • ./elasticsearch 前台启动
  • ./elasticsearch -d 后台启动 需要等一段时间
  • 配置跨域访问 /usr/local/software/elasticsearch-7.12.1/config/elasticsearch.yml 最后添加下面两行
http.cors.enabled: true
http.cors.allow-origin: "*"
  • curl 127.0.0.1:9200 成功会有相应信息

  • 如果出现 max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]错误 编辑 /etc/sysctl.conf,追加以下内容:vm.max_map_count=655360保存后,执行:sysctl -p

  • 出现 max file descriptors [65535] for elasticsearch process is too low, increase to at least [65536] 切换到root用户编辑 /etc/security/limits.conf,追加以下内容

    soft nofile 65536
    hard nofile 65536
    
  • 默认只允许本机访问 配合Nginx 实现外网访问

   upstream es_pool{
        server 127.0.0.1:9200;
    }

    upstream es_header_pool{
        server 127.0.0.1:9100;
    }

    server {
        listen       80;
        server_name  localhost;

        location /es/ {
            proxy_pass http://es_pool/;
            proxy_set_header Host $host;
        	  proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

        location /es_header/ {
            proxy_pass http://es_header_pool/;
            proxy_set_header Host $host;
        	  proxy_set_header X-Real-IP $remote_addr;
        	  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
     }
  • 允许内网访问的 elasticsearch.yml 配置文件
cluster.name: my-application
node.name: node-1
network.host: 0.0.0.0
http.port: 9200
discovery.seed_hosts: ["127.0.0.1", "[::1]"]
cluster.initial_master_nodes: ["node-1"]

http.cors.allow-origin: "*"
http.cors.enabled: true
http.cors.allow-headers : X-Requested-With,X-Auth-Token,Content-Type,Content-Length,Authorization
http.cors.allow-credentials: true

#安装ElasticSearch-Head

  • 通过https://nodejs.org/en/download/ 下载nodejs版本

  • 上传到/usr/local/software下面

  • tar -xvf node-v14.17.0-linux-x64.tar.xz 解压

  • export PATH=$PATH:/usr/local/software/node-v14.17.0-linux-x64/bin

  • ln -s /usr/local/software/node-v14.17.0-linux-x64/bin/npm /usr/local/bin/
    ln -s /usr/local/software/node-v14.17.0-linux-x64/bin/node /usr/local/bin/

  • 执行node -v 打印版本 成功

  • 通过 https://github.com/mobz/elasticsearch-head 下载最新版本

  • yum install -y unzip zip 安装unzip解压

  • 上传到/usr/local/software/ unzip elasticsearch-head-master.zip

  • npm install -g grunt-cli 安装依赖

  • yum install bzip2 //可能会报解压错误 没有忽略即可

  • npm install cnpm -g --registry=https://registry.npm.taobao.org 使用npm因为网络原因总是报错

  • cd elasticsearch-head-master/ 执行 cnpm install

  • 修改Gruntfile.js

connect: {
	server: {
		options: {
			hostname: '*', # 添加这一行
			port: 9100,
			base: '.',
			keepalive: true
		}
	}
}
  • 修改_site下面的app.js 4388行左右将localhost替换成ElasticSearch的服务器地址
this.base_uri = this.config.base_uri || this.prefs.get("app-base_uri") || "http://192.168.0.199:9200";
  • npm run start 启动
  • nohup npm run start & 后台启动

安装 Kibana

  • https://www.elastic.co/cn/downloads/kibana 下载 版本一定要和ElasticSearch一致
  • 上传到服务器 /usr/local/software 并解压
  • cd /usr/local/software
  • mv kibana-7.12.1-linux-x86_64/ kibana
server.host: "192.168.0.199"
elasticsearch.hosts: ["http://192.168.0.199:9200"]
i18n.locale: "zh-CN"
  • 执行bin目录下的kibana ./kibana --allow-root
  • 访问 http://192.168.0.199:5601/

ElasticSearch 术语

  • 索引index —— 拥有相似文档的集合
  • 类型type —— 一个索引中可以定义多个类型
  • 字段field —— 相当于数据库表中的字段
  • 映射mapping —— 处理数据方式和规格方面做一些限制
  • 文档document —— 一个文档是一个可被索引的基础单元
  • 分片 —— 由于单台机器无法存储大量数据,ES 可以将一个索引中的数据切分为多个分片(Shard),分布在多台服务器上存储。
  • 复制 —— 备份

操作

  • 使用PUT请求添加text索引 http://192.168.0.199/es/test
  • 新建索引的时候设置mapping信息 注意 7.4 默认不在支持指定索引类型 http://192.168.0.199/es/test1 下面是请求体
{
    "mappings": {
        "properties": {
            "id": {
                "type": "long",
                "store": true
            },
            "title": {
                "type": "text",
                "store": true,
                "analyzer": "standard"
            },
            "content": {
                "type": "text",
                "store": true,
                "analyzer": "standard"
            }
        }
    }
}
  • 查看索引对应的映射 类型默认都是_doc
{
  "mappings": {
    "_doc": {
      "properties": {
        "content": {
          "type": "text",
          "store": true,
          "analyzer": "standard"
        },
        "id": {
          "type": "long",
          "store": true
        },
        "title": {
          "type": "text",
          "store": true,
          "analyzer": "standard"
        }
      }
    }
  }
}
  • 索引创建完成后POST请求添加mapping信息 http://192.168.0.199/es/test/db1/_mapping?include_type_name=true 在test下面新增db1结果如下
{
  "mappings": {
    "db1": {
      "properties": {
        "content": {
          "type": "text",
          "store": true,
          "analyzer": "standard"
        },
        "id": {
          "type": "long",
          "store": true
        },
        "title": {
          "type": "text",
          "store": true,
          "analyzer": "standard"
        }
      }
    }
  }
}
  • DELETE请求删除索引库 http://192.168.0.199/es/test1
  • 添加文档(一条数据就是一个文档)http://192.168.0.199/es/test/db1/1 在db1类型下设置id为1的数据 下面是请求体 保持和映射一致就行 如果db1后面不加/1 会自动生成一个随机字符串id 操作时一般保持id字段和请求体里面的id字段保持一致
{
    "id":1,
    "title":"文档标题",
    "content":"文档内容"
}
  • id设置相同数据会被覆盖 用来实现修改文档

  • DELETE方法删除文档 http://192.168.0.199/es/test/db1/1

  • 查询

    • 根据id查询 GET请求 http://192.168.0.199/es/test/db1/1 即可

    • 关键字 POST http://192.168.0.199/es/test/db1/_search 只能使用一个汉字

      {
          "query":{
              "term":{
                  "title":"文" //在title字段上查询关键字有文的
              }
          }
      }
      
    • queryString查询 路径同上

        {
            "query_string":{
                "term":{
                    "default_field":"title",
                    "query":"文档"                
                }
            }
        }
    
    • 先分词 如果title中包含文 或者 档 的都会被查出来
    • 查看分词情况 GET 请求 http://192.168.0.199/es/test/_analyze
    {
      "analyzer" : "standard",
      "text": ["文档标题2"]
    }
    
    • 关键词查询 like
     {
            "query":{
                "query_string":{ //先对查询内容分词
                    "query":"title:(\"题2\")",
                    "default_operator": "and"
                }
            }
        }
    

    or

    {
        "query": {
            "match_phrase": {
                "title": "题2"
            }
        }
    }
    

安装中文分词器IK

ElasticSearch内置分词器

  1. standard分词器:(默认分词器)它会将词汇单元转换成小写形式,并除去停用词(a、an、the等)和标点符号,支持中文采用的方法为单字切分。
  2. simple分词器:首先会通过非字母字符来分割文本信息,然后将词汇单元统一成小写形式,该分词器会去掉数据类型的字符。
  3. Whitespace分词器:仅仅是去掉空格,对字符没有lowcase化,不支持中文;并且不对生成的词汇单元进行其他的标准化处理。
  4. language分词器:特定语言的分词器,不支持中文

中文分词器的安装

  • https://github.com/medcl/elasticsearch-analysis-ik/releases 下载最新版本的zip包
  • cd /usr/local/software/elasticsearch-7.12.1/plugins/
  • mkdir ik
  • 把文件移动到ik目录下
  • cd ik/
  • unzip elasticsearch-analysis-ik-7.12.1.zip
  • 切换用户重启 elasticsearch
  • 新建映射的时候就需使用ik分析器
  • ik_smart(最小) 和 ik_max_word(最细)的区别 举例分析程序员 ik_smart 会解析成程序员 ik_max_word 会解析成三个分别是 程序员 程序 员
{
  "mappings": {
    "db1": {
      "properties": {
        "content": {
          "type": "text",
          "store": true,
          "analyzer": "ik_smart"
        },
        "id": {
          "type": "long",
          "store": true
        },
        "title": {
          "type": "text",
          "store": true,
          "analyzer": "ik_max_word"
        }
      }
    }
  }
}

集群

修改config\elasticsearch.yml文件添加

#集群名称,保证唯一
cluster.name: my‐elasticsearch
#节点名称,必须不一样
node.name: node‐1 # node‐2 node‐3
#必须为本机的ip地址
network.host: 192.168.0.199 # 0.200  0.201
#服务端口号
http.port: 9200
#服务TCP端口号
transport.tcp.port: 9300
#设置集群自动发现机器ip集合
discovery.zen.ping.unicast.hosts:["192.168.0.199:9300","192.168.0.200:9300","192.168.0.201:9302"]

SpringBoot 整合

  • 创建工程 并引入依赖 内部引用的es版本必须要和服务器安装的匹配
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    <version>2.3.10.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
  • application.yml配置
spring:
  elasticsearch:
    rest:
      uris: 192.168.0.199:9200
      username: my-application
  #   uris:192.168.0.199:9200,192.168.0.200:9200,192.168.0.201:9200,
  • 实体与document对应类
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;

@Document(indexName = "stu") //索引名称
@Data
public class Stu {
    @Id
    private Long stuId; //会创建相同的文档id 否则自动生成

    @Field(store = true)
    private String name;

    @Field(store = true)
    private Integer age;
}
  • 实体类中新加四个字段 会自动添加到索引里面
@Field(store = true)
private Float money;

@Field(store = true, type = FieldType.Keyword)
private String sign;

@Field(store = true)
private String description;

@Field(type = FieldType.Date, format = DateFormat.date_time)
private Date recordTime;
  • 关于常用方法 2.3.10 版本中的
@RunWith(SpringRunner.class)
@SpringBootTest
public class ESTest {
    /**
     * 一般不建议用Spring来创建/更新 删除索引
     * 一般针对文档进行CRUD操作
     */
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    /**
     * 创建/更新索引 如果存在 就更新 不会再创建 一般不使用
     */
    @Test
    public void createIndexStu() {
        Stu stu = new Stu();
        stu.setStuId(1006L);
        stu.setAge(33);
        stu.setName("李七");
        stu.setRecordTime(new Date());
        IndexQuery indexQuery = new IndexQueryBuilder().withObject(stu).build();
        IndexCoordinates indexCoordinates = elasticsearchRestTemplate.getIndexCoordinatesFor(Stu.class);
        elasticsearchRestTemplate.index(indexQuery, indexCoordinates);
    }

    /**
     * 更新文档
     */
    @Test
    public void updateStuDoc() {
        Document document = Document.create();
        document.put("name", "李四");
        document.put("age", 20);
        UpdateQuery updateQuery = UpdateQuery.builder("1000").withDocument(document).build();
        elasticsearchRestTemplate.update(updateQuery, elasticsearchRestTemplate.getIndexCoordinatesFor(Stu.class));
    }

    /**
     * 查询 + 高亮显示
     */
    @Test
    public void queryStuDoc() {
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        //必须满足的条件 加keyword不会拆分 不加会拆分成李和四
//        boolQueryBuilder.must().add(new TermQueryBuilder("name.keyword", "李四"));
//        boolQueryBuilder.must().add(new TermQueryBuilder("name", "四"));
        // 查询3天内数的数据
        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("recordTime");
        rangeQueryBuilder.gte(LocalDateTime.now().minusDays(3));
//        或者下面的
//        rangeQueryBuilder
//                .from("2021-05-16T00:00:00.000Z")
//                .to("2021-05-19T23:59:59.000Z");
//        boolQueryBuilder.must().add(rangeQueryBuilder);


        // 分页查询20条
        PageRequest pageRequest = PageRequest.of(0, 20, Sort.by("stuId").descending());

        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
        searchQueryBuilder
                .withQuery(boolQueryBuilder)
                // 亲测使用.keyword有值但是高亮数据size = 0
                .withHighlightBuilder(new HighlightBuilder().field("name")
                        .preTags("<font color='red'>")
                        .postTags("</font>"))
                .withPageable(pageRequest);


        Query searchQuery = searchQueryBuilder.build();

        SearchHits<Stu> result = elasticsearchRestTemplate.search(searchQuery, Stu.class);
        List<SearchHit<Stu>> hitList = result.getSearchHits();
        System.out.println("hit size -> {}" + hitList.size());
        hitList.forEach(hit -> {
            Map<String, List<String>> highlightFields = hit.getHighlightFields();
            List<String> nameHighlight = highlightFields.get("name");
            if (nameHighlight != null) {
                for (String text : nameHighlight) {
                    hit.getContent().setName(text);
                }
            }
            System.out.println("返回数据:" + hit.getContent().toString());
        });
    }

    /**
     * 删除文档
     */
    @Test
    public void delIndexDoc() {
        elasticsearchRestTemplate.delete("1000", elasticsearchRestTemplate.getIndexCoordinatesFor(Stu.class));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要将一组时间按24小时制进行分组统计,可以使用Elasticsearch的Terms Aggregation和Date Histogram Aggregation。首先将时间字段设置为date类型,然后使用Date Histogram Aggregation按小时分组,再在内部使用Terms Aggregation统计每个小时的数量。 以下是一个按24小时制进行分组统计的例子: 假设我们有一个索引,包含以下文档: ```json [ { "time": "2022-06-01T00:00:00Z" }, { "time": "2022-06-01T01:00:00Z" }, { "time": "2022-06-01T01:30:00Z" }, { "time": "2022-06-01T02:00:00Z" }, { "time": "2022-06-01T03:00:00Z" }, { "time": "2022-06-01T04:00:00Z" }, { "time": "2022-06-01T05:00:00Z" }, { "time": "2022-06-01T06:00:00Z" } ] ``` 我们可以使用以下查询来按24小时制进行分组统计: ```json { "aggs": { "hourly": { "date_histogram": { "field": "time", "interval": "hour", "format": "HH" }, "aggs": { "count": { "value_count": { "field": "time" } } } } } } ``` 该查询使用Date Histogram Aggregation按小时分组,并设置“format”参数为“HH”,表示按24小时制进行分组。然后在内部使用Terms Aggregation统计每个小时的数量。 查询结果如下: ```json { "aggregations" : { "hourly" : { "buckets" : [ { "key_as_string" : "00", "key" : 0, "doc_count" : 1, "count" : { "value" : 1 } }, { "key_as_string" : "01", "key" : 1, "doc_count" : 2, "count" : { "value" : 2 } }, { "key_as_string" : "02", "key" : 2, "doc_count" : 1, "count" : { "value" : 1 } }, { "key_as_string" : "03", "key" : 3, "doc_count" : 1, "count" : { "value" : 1 } }, { "key_as_string" : "04", "key" : 4, "doc_count" : 1, "count" : { "value" : 1 } }, { "key_as_string" : "05", "key" : 5, "doc_count" : 1, "count" : { "value" : 1 } }, { "key_as_string" : "06", "key" : 6, "doc_count" : 1, "count" : { "value" : 1 } } ] } } } ``` 可以看到,每个小时的数量都被正确地统计了。 ### 回答2: Elasticsearch是一个开源的分布式搜索与分析引擎,提供了强大的全文搜索、日志分析以及数据可视化功能。在Elasticsearch中,我们可以按照需求进行数据的分组统计,包括按照24小时制进行分组统计。 要按照24小时制进行分组统计,我们可以结合Elasticsearch的聚合(Aggregation)功能和日期范围查询(Range Query)来实现。下面是实现的步骤: 1. 创建一个索引,并确定需要统计的字段类型为日期类型。 2. 使用聚合功能进行分组统计,可以使用date_histogram聚合来按照特定的时间间隔进行分组。在这里,我们可以将时间间隔设置为1小时来实现按照24小时制进行分组。 3. 在聚合中使用range参数来指定时间范围,确保只统计在指定的时间范围内的数据。 4. 可以选择统计指定字段的计数、求和、平均值等等。 以下是一个示例的查询语句,用于按照24小时制进行分组统计: ``` GET /your_index/_search { "size": 0, "aggs": { "hourly_stats": { "date_histogram": { "field": "your_date_field", "interval": "1h", "format": "HH:mm" }, "aggs": { "your_statistic": { // 指定需要统计的字段和统计类型 "your_statistic_type": { "field": "your_field" } } } } }, "query": { "range": { "your_date_field": { "gte": "start_time", "lte": "end_time" } } } } ``` 在以上示例中,我们使用了date_histogram聚合按照1小时的时间间隔进行分组统计。同时,我们使用了range查询来限定需要统计的时间范围。在聚合内部,我们可以指定需要统计的字段和统计类型,如计数、求和等等。 通过以上的查询语句,我们可以获得按照24小时制进行分组统计的结果,并进行进一步的数据分析和可视化处理。 ### 回答3: Elasticsearch 是一个流行的分布式搜索和分析引擎,可以用于搜索、分析和存储大量数据。要按24小时制进行分组统计,在 Elasticsearch 中可以使用时间字段和聚合操作来实现。 首先,确保你的数据中有一个时间字段,例如一个包含时间戳的字段。可以通过在文档中添加一个字段,例如 "@timestamp" 来保存时间戳。 接下来,你可以使用 Elasticsearch 的日期直方图聚合(date histogram aggregation)来按24小时制分组统计。该聚合操作可以将数据按指定的时间间隔划分为不同的时间段,并计算每个时间段的统计值。 例如,假设你的索引名为 "my_index",时间字段为 "@timestamp",你可以使用以下查询来按24小时制进行分组统计: ``` GET my_index/_search { "size": 0, "aggs": { "hourly_stats": { "date_histogram": { "field": "@timestamp", "interval": "1h", "format": "HH:mm" }, "aggs": { "avg_price": { "avg": { "field": "price" } } } } } } ``` 该查询将返回按小时分组的统计数据,统计每个时间段内的平均价格。 需要注意的是,要正确地按24小时制进行分组统计,需要设置 "format" 参数为 "HH:mm" 这样的时间格式。根据你的需求,你可以调整 "interval" 参数来更细粒度地划分时间段,例如分钟、秒等。 总结起来,通过使用 Elasticsearch 的日期直方图聚合(date histogram aggregation),并设置合适的时间格式和间隔,就可以按照24小时制进行分组统计。使用类似的聚合操作,你还可以进行更多统计和分析,例如最大值、最小值、总和等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值