ElasticSearch7.4.2

一、基本概念

二、docker安装ElasticSearch

1、下载镜像文件

docker pull elasticsearch:7.4.2 存储和检索数据

若报错则使用:docker pull docker.elastic.co/elasticsearch/elasticsearch:7.4.2

docker pull kibana:7.4.2 可视化检索数据

若报错则使用:docker pull docker.elastic.co/kibana/kibana:7.4.2

2、创建实例

1、ElasticSearch

mkdir -p /mydata/elasticsearch/config

mkdir -p /mydata/elasticsearch/data

echo "http.host: 0.0.0.0">>/mydata/elasticsearch/config/elasticsearch.yml

chmod -R 777 /mydata/elasticsearch/ 保证权限


docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \

-e "discovery.type=single-node" \

-e ES_JAVA_OPTS="-Xms64m -Xmx512m" \

-v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \

-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \

-v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \

-d docker.elastic.co/elasticsearch/elasticsearch:7.4.2

特别注意:

-e ES_JAVA_OPTS=-“Xms256m-Xmx256m”\测试环境下,设置ES的初始内存和最大内存,否则导致过大启动不了ES

2、Kibana
注意:ELASTICSEARCH_HOSTS配置自己虚拟机es的ip地址

docker run --name kibana -e ELASTICSEARCH_HOSTS=http://192.168.xx.xx:9200 -p  5601:5601 -d docker.elastic.co/kibana/kibana:7.4.2

开机自启

docker update --restart=always 容器名称

–restart=always : 开机启动,失败也会一直重启;

–restart=on-failure:10 : 表示最多重启10次

systemctl restart docker

三、初步检索

1、_cat

GET/_cat/nodes:查看所有节点

GET/_cat/health:查看es健康状况

GET/_cat/master:查看主节点

GET/_cat/indices:查看所有索引show databases;

2、索引一个文档(保存)

保存一个数据,保存在哪个索引的哪个类型下,指定用哪个唯一标识PUT customer/external/1;在customer索引下的external类型下保存1号数据为

PUT customer/external/1
{
	"name":"John Doe"
}

PUT和POST都可以,POST新增。如果不指定id,会自动生成id。指定id就会修改这个数据,并新增版本号

PUT可以新增可以修改。PUT必须指定id;由于PUT需要指定id,我们一般都用来做修改操作,不指定id会报错。

3、查询文档

GET customer/external/1

结果:
{
    "_index": "customer", //在哪个索引
    "_type": "external",  //在哪个类型
    "_id": "1",           //记录id
    "_version": 2,        //版本号
    "_seq_no": 1,         //并发控制字段,每次更新就会+1,用来做乐观锁
    "_primary_term": 2,   //同上,主分片重新分配,如重启,就会变化
    "found": true,
    "_source": {          //真正的内容
        "name": "John Doe"
    }
}
更新携带 ?if_seq_no=1&if_primary_term=1

4、更新文档

POST /customer/external/1/_update

{
	"doc":{
		"name": "john Doew"
	}
}

或者

POST /customer/external/1
{
    "name": "john Doe2"
}

或者

PUT /customer/external/1
{
    "name": "john Doe2"
}
●不同:POsT操作会对比源文档数据,如果相同不会有什么操作,文档version不增加PUT操作,总会将数据重新保存并增加version版本;
带update对比元数据如果一样就不进行任何操作。
看场景;对于大并发更新,不带update;
对于大并发查询偶尔更新,带update:对比更新,重新计算分配规则。
●更新同时增加属性
POST customer/external/1/_update
{
	"doc":{
		"name": "john Doew",
        "age": 20
	}
}
PUTPOST不带_update也可以

5、删除文档&索引

DELETE customer/external/1

DELETE customer

6、bulk批量API (postman中不能操作)

POST customer/external/_bulk
{"index":{"_id":"1"}}
{"name":"john Doe"}
{"index":{"_id":2}}
{"name":"jane Doe"}
语法格式
{action:{metadata}}\n
{request body}\n

{action:{metadata}}\n
{request body}\n
复杂实例:
POST /_bulk
{"delete":{"_index":"website","_type":"blog","_id":123}}
{"create":{"_index":"website","_type":"blog","_id":123}}
{"title":"My first blog post"}
{"index": {"_index":"website","_type":"blog"}}
{"title":"My second blog post"}
{"update":{"_index":"website","_type":"blog","_id":123}}
{"doc":{"title":"My updated blog post"}}

bulkAPI以此按顺序执行所有的action(动作)。如果一个单个的动作因任何原因而失败,它将继续处理它后面剩余的动作。当bulk API返回时,它将提供每个动作的状态(与发送的顺序相同),所以您可以检查是否一个指定的动作是不是失败了。

四、进阶检索

1、SearchAPI

ES支持两种基本方式检索:

● 一个是通过使用REST request URI发送搜索参数(uri+检索参数)

● 另一个是通过使用REST request body来发送它们(uri+请求体)

1)、检索信息

● 一切检索从_search开始

GET bank/_search        检索bank下的所有信息,包括type和docs
GET bank/_search?q=*&sort=account_number:asc    请求参数方式检索
响应结果解释:
took-Elasticsearch 执行搜索的时间(毫秒)
time_out -告诉我们搜索是否超时
_shards -告诉我们多少个分片被搜索了,以及统计了成功/失败的搜索分片
hits -搜索结果
hits.total -搜索结果
hits.hits -实际的搜索结果数组(默认为前10的文档)
sort -结果的排序key(键)(没有则按score排序)
score和max_score -相关性得分和最高得分(全文检索用)

● uri+请求体进行检索

GET bank/_search
{
	"query":{
		"match_all":{}
	},
	"sort":[
		"account_number":{
			"order":"desc"
		}
	]
}
HTTP客户端工具(POSTMAN),get请求不能携带请求体,我们变为post也是一样的我们POST一个JsoN风格的查询请求体到_search API。
需要了解,一旦搜索的结果被返回,Elasticsearch就完成了这次请求,并且不会维护任何服务端的资源或者结果的cursor(游标)

2、Query DSL

1)、基本语法格式

Elasticsearch提供了一个可以执行查询的Json风格的DSL(domain-specific language领域特定语言)。这个被称为Query DSL。该查询语言非常全面,并且刚开始的时候感觉有点复杂,真正学好它的方法是从一些基础的示例开始的。

● 一个查询语句的典型结构

{
	QUERY_NAME:{
		ARGUMENT:VALUE,
		ARGUMENT:VALUE,...
	}
}

● 如果是针对某个字段,那么它的结构如下:

{
	QUERY_NAME:{
		FIELD_NAME:{
			ARGUMENT:VALUE,
			ARGUMENT:VALUE,...
		}
	}
}
GET bank/_search
{
  "query": {
    "match_all": {}
  },
  "from":0,
  "size":5,
  "sort": [
    "account_number": {
      "order": "desc"
    }
  ]
}

● query 定义如何查询
● match_all 查询类型【代表查询所有的所有】,es中可以在query中组合非常多的查询类型完成复杂查询
● 除了query参数之外,我们也可以传递其他的参数以改变查询结果。如sort、size
● from+size限定,完成分页功能
● sort排序,多字段排序,会在前序字段相等时后续字段内部排序,否则以前序为准
2)、返回部分字段
GET bank/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "balance": {
        "order": "desc"
      }
    }
  ],
  "from": 0,
  "size": 5, 
  "_source": ["address","age"]
}
3)、match【匹配查询】

● 基本类型(非字符串),精确匹配

GET bank/_search
{
	"query":{
		"match":{
			"account_mumber":"20"
		}
	}
}
match返回account_mumber=20的

● 字符串,全文检索

GET bank/_search
{
  "query": {
    "match": {
      "address": "mill lane"
    }
  }
}
match返回address含有"mill lane"或“mill”或“lane”的词,并给出相关性得分
全文检索按照评分进行排序,会对检索条件进行分词匹配
4)、match_phrase【短语匹配】

将需要匹配的值当成一个整体单词(不分词)进行检索

GET bank/_search
{
  "query": {
    "match_phrase": {
      "address": "mill lane"
    }
  }
}
查出address中包含mill lane的所有记录,并给出相关性得分
5)、multi_match【多字段匹配】
GET bank/_search
{
  "query": {
    "multi_match": {
      "query": "mill",
      "fields": ["state","address"]
    }
  }
}
state或者address包含mill
6)、bool【复合查询】

bool用来做复合查询:

复合语句可以合并任何其它查询语句,包括复合语句,了解这一点是很重要的。这就意味着,复合语句之间可以互相嵌套,可以表达非常复杂的逻辑。


● must:必须达到must列举的所有条件

● must_not:必须不达到must列举的所有条件

● should:满不满足所有条件都可以,满足条件后会增加相关性评分

GET bank/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "gender": "m"
          }
        },
        {
          "match": {
            "address": "mill"
          }
        }
      ],
      "must_not": [
        {
          "match": {
            "age": "28"
          }
        }
      ],
      "should": [
        {
          "match": {
            "lastname": "Wallace"
          }
        }
      ]
    }
  }
}
7)、filter【结果过滤】

并不是所有的查询都需要产生分数,特别是那些仅用于"filtering”(过滤)的文档。为了不计算分数Elasticsearch会自动检查场景并且优化查询的执行。

GET bank/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "gender": "m"
          }
        },
        {
          "match": {
            "address": "mill"
          }
        }
      ],
      "must_not": [
        {
          "match": {
            "age": "28"
          }
        }
      ],
      "should": [
        {
          "match": {
            "lastname": "Wallace"
          }
        }
      ],
      "filter": {
        "range": {
          "age": {
            "gte": 10,
            "lte": 40
          }
        }
      }
    }
  }
}
8)、term

和match一样。匹配某个属性的值。全文检索字段用match,其他非text字极匹配用term

image-20250303175003971

## 非文本字段
GET bank/_search
{
  "query": {
    "term": {
      "age": 38
    }
  }
}
## 精确匹配 必须输出有198 Mill的字段
GET bank/_search
{
  "query": {
    "match": {
      "address.keyword": "198 Mill"
    }
  }
}
## 模糊匹配 输出含有198 Mill字段
GET bank/_search
{
  "query": {
    "match_phrase": {
      "address": "198 Mill"
    }
  }
}
9)、aggregations(执行聚合)

聚合提供了从数据中分组和提取数据的能力。最简单的聚合方法大致等于SQL GROUPBYSQL聚合函数。在Elasticsearch中,您有执行搜索返回hits(命中结果),并且同时返回聚合结果,把一个响应中的所有hits(命中结果)分隔开的能力。这是非常强大且有效的,您可以执行查询和多个聚合,并且在一次使用中得到各自的(任何一个的)返回结果,使用一次简洁和简化的API来避免网络往返。

搜索address中包含mill的所有人的年龄分布以及平均年龄,但不显示这些人的详情

GET bank/_search
{
  "query": {
    "match": {
      "address": "mill"
    }
  },
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "age",
        "size": 10
      }
    },
    "avg_agg":{
      "avg": {
        "field": "age"
      }
    }
  },
  "size":0
}

size:0  不显示搜索数据
aggs:   执行聚合。聚合语法如下
"aggs": {
    "aggs_name 这次聚合的名字,方便展示在结果集中": {
      “AGG_TYPE 聚合的类型(avg,term,terms)”:{}
      }
    },

复杂:

按照年龄聚合,并且请求这些年龄段的这些人的平均薪资

GET bank/_search
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "ageAgg": {
      "terms": {
        "field": "age",
        "size": 100
      },
      "aggs": {
        "ageAvg": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  },
  "size": 0
}

复杂:查出所有年龄分布,并且这些年龄段中M的平均薪资和F的平均薪资以及这个年龄中段的总体平均薪资

GET  bank/_search
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "ageAgg": {
      "terms": {
        "field": "age",
        "size": 100
      },
      "aggs": {
        "genderAgg": {
          "terms": {
            "field": "gender.keyword",
            "size": 2
          },
          "aggs": {
            "balanceAgg": {
              "avg": {
                "field": "balance"
              }
            }
          }
        },
        "balanceAgg":{
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  },
  "size": 0
}

3、Mapping

1)、字段类型

image-20250303192104845

2)、映射

Mapping(映射)

Mapping是用来定义一个文档(document),以及它所包含的属性((field)是如何存储和索引的。比如,使用mapping来定义:

●哪些字符串属性应该被看做全文本属性(full text fields)。

●哪些属性包含数字,日期或者地理位置。

●文档中的所有属性是否都能被索引(_all配置)。

●日期的格式。

●自定义映射规则来执行动态添加属性

●查看mapping信息:

GET bank/_mapping

●修改mapping信息

image-20250303193333155

3)、新版本改变

Es7及以上移除了type的概念。

  • 关系型数据库中两个数据表示是独立的,即使他们里面有相同名称的列也不影响使用,但ES中不是这样的。elasticsearch是基于ucene开发的搜索引擎,而ES中不同type下名称相同的fled最终在Lucene中的处理方式是一样的。

    • 两个不同type下的两个user_name,在ES同一个索引下其实被认为是同一个filed,你必须在两个不同的type中定义相同的fled映射。否则,不同type中的相同字段名称就会在处理中出现冲突的情况,导致ucene处理效率下降。

    • 去掉type就是为了提高ES处理数据的效率。

Elasticsearch 7.x

●URL中的type参数为可选。比如,索引一个文档不再要求提供文档类型。

Elasticsearch 8.x

●不再支持URL中的type参数。

解决:

1)、将索引从多类型迁移到单悉型,每种类型文档一个独立索

2)、将已存在的索引下的类型数据,全部迁移到指定位置即可。详见数据迁移

1、创建映射
1、创建索引并指定映射
PUT /my-index
{
  "mappings":{
    "properties": {
      "age": {"type": "integer"},
      "email": {"type": "keyword"},
      "name": {"type": "text"}
    }
  }
}
2、添加新的字段映射
## 新增一个不被索引的keyword字段
PUT /my-index/_mapping
{
  "properties":{
    "employee-id":{
      "type":"keyword",
      "index":false
    }
  }
}
3、更新映射

对于已经存在的映射字段,我们不能更新。更新必须创建新的索引进行数据迁移

4、数据迁移(不用type,老的数据可以迁移过来)

先创建出new_twitter的正确映射。然后使用如下方式进行数据迁移

POST _reindex  [固定写法]
{
	"source":{
		"index":"teitter"
	},
	"dest":{
		"index":"new_teitter"
	}
}
将旧索引的type下的数据进行迁移
POST _reindex
{
	"source":{
		"index":"teitter",
		"type":"tweet"
	},
	"dest":{
		"index":"new_teitter"
	}
}

4、分词

一个tokenizer(分词器)接收一个字符流,将之分割为独立的tokens(词元,通常是独立的单词),然后输出tokens流。

例如,whitespace tokenizer遇到空白字符时分割文本。它会将文本"Quick brown fox!"分割为**[Quick,brown,fox!]**。

tokenizer(分词器)还负责记录各个term(词条)的顺序或position位置(用于phrase短语和word proximity词近邻查询),以及term(词条)所代表的原始word(单词)的start(起始)和end(结束)的character offsets(字符偏移量)(用于高亮显示搜索的内容)。Elasticsearch提供了很多内置的分词器,可以用来构建custom analyzers(自定义分词器)。

1)、安装ik分词器

注意:不能用默认elasticsearch-plugin install xxx.zip 进行自动安装

https://github.com/medcl/elasticsearch-analysis-ik/releases?after=v7.4.2 对应es版本安装

image-20250304094301434

2)、测试分词器

使用默认

GET my_index/_analyze
{
	"text":"我是中国人"
}

使用分词器

GET my_index/_analyze
{
  "analyzer": "ik_smart",
  "text": "我是中国人"
}

GET /_analyze
{
  "analyzer": "ik_max_word",
  "text": "我是中国人"
}
3)、自定义词库

修改/usr/share/elasticsearch/plugins/ik/config/中的IKAnalyzer.cfg.xml

/usr/share/elasticsearch/plugins/ik/config

<?xml version="1.0"encoding="UTF-8"?>
<IDOCTYPE properties SYSTEM"http://java.sun.com/dtd/properties.dtd">
<properties>
	<comment>IK Analyzer扩展配置</comment>
	<!用户可以在这里配置自己的扩展字典->
	<entry key="ext dict"></entry>
	<!-用户可以在这里配置自己的扩展停止词字典->
	<entry key="ext stopwords"></entry>
	<!-用户可以在这里配置远程扩展字典>
	<entry key="remote_ext_dict">http://192.168.128.130/fenci/myword.txt</entry>
	<!-用户可以在这里配置远程展停正词字典>
	<!-<entry key="remote_ext_stopwords">words_location</entry>->
</properties>
原来的xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
        <comment>IK Analyzer 扩展配置</comment>
        <!--用户可以在这里配置自己的扩展字典 -->
        <entry key="ext_dict"></entry>
         <!--用户可以在这里配置自己的扩展停止词字典-->
        <entry key="ext_stopwords"></entry>
        <!--用户可以在这里配置远程扩展字典 -->
        <!-- <entry key="remote_ext_dict">words_location</entry> -->
        <!--用户可以在这里配置远程扩展停止词字典-->
        <!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>

更新完成后,es只会对新增的数据用新词分词。历史数据是不会重新分词的。如果想要历史数据重新分词。需要执行:

POST my_index/_update_by_query?conflicts=proceed

五、ElasticSearch-Rest-Client

1)、9300:TCP

●spring-data-elasticsearch:transport-api.jar:

​ ■springboot.版本不同,transport-api.jar不同,不能适配es版本

​ ■7.x已经不建议使用,8以后就要废弃

2)、9200:HTTP

●JestClient:非官方,更新慢

●RestTemplate:模拟发HTTP请求,ES很多操作需要自己封装,麻烦

●HttpClient:同上

●Elasticsearch-Rest-Client:官方RestClient,封装了ES操作,API层次分明,上手简单

最终选择Elasticsearch-Rest-Client(elasticsearch-rest-high-level-client)

[Maven Repository | Java REST Client 7.4] | Elastic

注意:elasticsearch-rest-high-level-client在8.0版本被废除,替换成Java API Client.

1、Springboot整合

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

2、编写配置

配置通用请求选项和给容器中注入一个RestHighLevelClient的bean

@Configuration
public class GulimallElasticConfig {
    public static final RequestOptions COMMON_OPTIONS;
    static {
        RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
//        builder.addHeader("Authorization", "Bearer " + TOKEN);
//        builder.setHttpAsyncResponseConsumerFactory(
//                new HttpAsyncResponseConsumerFactory
//                        .HeapBufferedResponseConsumerFactory(30 * 1024 * 1024 * 1024));
        COMMON_OPTIONS = builder.build();
    }
    @Bean
    public RestHighLevelClient esRestClient(){
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(
                // 配置自己虚拟机的ip地址
                        new HttpHost("192.xxx.xx.xx", 9200, "http")));
        return client;
    }
}

3、使用

  1. 添加存储数据到es
@Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 测试存储数据到es
     * 更新也可以
     */
    @Test
    void indexDate() throws IOException {
        IndexRequest indexRequest = new IndexRequest("users");
        indexRequest.id("1");
//        indexRequest.source("userName","zhangsan","age",18,"gender","男");
        User user = new User();
        user.setUserName("zicanyue");
        user.setAge(18);
        user.setGender("男");
        String jsonString = JSON.toJSONString(user);
        indexRequest.source(jsonString, XContentType.JSON);// 要保存的内容
        // 执行操作
        IndexResponse index = restHighLevelClient.index(indexRequest, GulimallElasticConfig.COMMON_OPTIONS);
        // 提前有用的响应数据
        System.out.println(index);
    }
    @Data
    class User{
        private String userName;
        private String gender;
        private Integer age;

    }

删除等简单操作查文档

2、测试复杂检索

@Autowired
    private RestHighLevelClient restHighLevelClient;
    @Data
    @ToString
    class Account {
        private int account_number;
        private int balance;
        private String firstname;
        private String lastname;
        private int age;
        private String gender;
        private String address;
        private String employer;
        private String email;
        private String city;
        private String state;
    }

    /**
     * 测试复杂检索
     * @throws IOException
     */
    @Test
    void searchDate() throws IOException {
        // 1、创建检索请求
        SearchRequest searchRequest = new SearchRequest();
        // 指定索引
        searchRequest.indices("newbank");
        //指定DSL,检索条件
        // SearchSourceBuilder 封装的检索条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchRequest.source(searchSourceBuilder);
        // 1.1)、构造检索条件
//        searchSourceBuilder.query();
//        searchSourceBuilder.from();
//        searchSourceBuilder.size();
//        searchSourceBuilder.aggregation();
        // 1.2)、按照年龄的值分布进行聚合
        searchSourceBuilder.query(QueryBuilders.matchQuery("address","mill"));
        TermsAggregationBuilder ageAgg = AggregationBuilders.terms("ageAgg")
                .field("age")
                .size(10);
        searchSourceBuilder.aggregation(ageAgg);
        // 1.3)、计算平均薪资
        AvgAggregationBuilder balanceAvg = AggregationBuilders.avg("balanceAvg").field("balance");
        searchSourceBuilder.aggregation(balanceAvg);
        System.out.println("检索条件:"+searchSourceBuilder.toString());

        // 2、执行检索
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, GulimallElasticConfig.COMMON_OPTIONS);
        // 3、分析结果 searchResponse
        System.out.println(searchResponse.toString());
//        Map map = JSON.parseObject(searchResponse.toString(), Map.class);
        // 3.1)、获取所有查到的数据
        SearchHits hits = searchResponse.getHits();
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit searchHit : searchHits) {
//            searchHit.getIndex();
//            searchHit.getId();
            String sourceAsString = searchHit.getSourceAsString();
            Account account = JSON.parseObject(sourceAsString, Account.class);
            System.out.println("accout:"+account);
        }
        // 3.2)、获取这次检索到的分析信息:
        Aggregations aggregations = searchResponse.getAggregations();
        Terms ageAgg1 = aggregations.get("ageAgg");
        ageAgg1.getBuckets().forEach(bucket -> {
            System.out.println("年龄:"+bucket.getKeyAsString()+"===>"+bucket.getDocCount());
        });
        Avg balanceAvg1 = aggregations.get("balanceAvg");
        System.out.println("平均薪资:"+balanceAvg1.getValueAsString());
    }

“index”: false 不可以被索引

“doc_values”: false 不可以被聚合

冗余字段都使用上述的字段,只是用来看而不是用于聚合分析

"skuImg": {
        "type": "keyword",
        "index": false,  
        "doc_values": false
      },

若不使用nested,es-数组就会扁平化处理

image-20250306165701180

nested:嵌入式的,当其中的数据为数组时使用这个

	"catalogName": {
        "type": "keyword",
        "index": false,
        "doc_values": false
      },
      "attrs": {
        "type": "nested",
        "properties": {
          "attrId": {
            "type": "long"
          },
          "attrName": {
            "type": "keyword",
            "index": false,
            "doc_values": false
          },
          "attrValue": {
            "type": "keyword"
          }
        }

六、附录-安装nginx

●随便启动一个nginx实例,只是为了复制出配置

​ ■docker run -p 80:80 --name nginx -d nginx:1.10

●将容器内的配置文件拷贝到当前目录:docker container cp nginx:/etc/nginx .

​ ■别忘了后面的点

●修改文件名称:mv nginx conf把这个conf移动到/mydata/nginx下

●终止原容器:docker stop nginx

●执行命令删除原容器:docker rm $Containerld

●创建新的nginx;执行以下命令

docker run -p 80:80 --name nginx \

-v /mydata/nginx/html:/usr/share/nginx/html \

-v /mydata/nginx/logs:/var/log/nginx \

-v /mydata/nginx/conf:/etc/nginx \

-d nginx:1.10
},


若不使用nested,es-数组就会扁平化处理

[外链图片转存中...(img-jFWE8suE-1741325780317)]

nested:嵌入式的,当其中的数据为数组时使用这个

```json
	"catalogName": {
        "type": "keyword",
        "index": false,
        "doc_values": false
      },
      "attrs": {
        "type": "nested",
        "properties": {
          "attrId": {
            "type": "long"
          },
          "attrName": {
            "type": "keyword",
            "index": false,
            "doc_values": false
          },
          "attrValue": {
            "type": "keyword"
          }
        }

六、附录-安装nginx

●随便启动一个nginx实例,只是为了复制出配置

​ ■docker run -p 80:80 --name nginx -d nginx:1.10

●将容器内的配置文件拷贝到当前目录:docker container cp nginx:/etc/nginx .

​ ■别忘了后面的点

●修改文件名称:mv nginx conf把这个conf移动到/mydata/nginx下

●终止原容器:docker stop nginx

●执行命令删除原容器:docker rm $Containerld

●创建新的nginx;执行以下命令

docker run -p 80:80 --name nginx \

-v /mydata/nginx/html:/usr/share/nginx/html \

-v /mydata/nginx/logs:/var/log/nginx \

-v /mydata/nginx/conf:/etc/nginx \

-d nginx:1.10

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐