ElasticSearc安装配置使用

转载:相关概念解释

1、Docker安装ES

  • 拉取安装镜像

    docker pull elasticsearch:5.6.8    #安装镜像
    docker run -id --name=myEs -p 9200:9200 -p 9300:9300 elasticsearch:5.6.8      #运行容器,配置名字和端口映射(9200是web端访问端口,9300是客户端软件访问接口)
    

    运行起来之后就可以在浏览器访问到 elasticsearch 了

  • 配置跨域访问

    1. 准备apt 源,下载vim
      • 由于 elasticsearch 容器内部不带 vi 等编辑器,所以需要先下载。下载前需要更新 apt 源。容器内部使用的是国外的源,速度很慢甚至无法使用。替换为 aliyun 的源

      • 在当前目录下准备 sources.list 里面是 aliyun 的 apt 源

        deb http://mirrors.aliyun.com/ubuntu/ focal main restricted
        deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted
        deb http://mirrors.aliyun.com/ubuntu/ focal universe
        deb http://mirrors.aliyun.com/ubuntu/ focal-updates universe
        deb http://mirrors.aliyun.com/ubuntu/ focal multiverse
        deb http://mirrors.aliyun.com/ubuntu/ focal-updates multiverse
        deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
        deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted
        deb http://mirrors.aliyun.com/ubuntu/ focal-security universe
        deb http://mirrors.aliyun.com/ubuntu/ focal-security multiverse
        
      • 复制到容器内部

        docker cp ./sources.list myEs:/etc/apt/sources.list  
        
      • 下载 vim

        apt-get update      #更新 apt 源
        apt-get install vim    # 下载vim (无脑 yes 即可)
        
    2. 修改 elasticsearch.yml 配置文件
      • ​ haha

        http.host: 0.0.0.0
        # Uncomment the following lines for a production cluster deployment
        # 开放任意 ip 访问(添加)
        transport.host: 0.0.0.0
        #discovery.zen.minimum_master_nodes: 1
        #修改集群名字
        cluster.name: my-application
        #配置跨域访问(添加)
        http.cors.enabled: true
        http.cors.allow-origin: "*"
        	#宿主机 ip
        network.host: 192.168.129.3
        
      3、es 占用的资源比较多,所以还需要修改宿主机的以下配置文件
      • 修改允许单个进程打开的最大文件个数的软限制和硬限制 vim /etc/security/limits.conf 追加以下内容:

        * soft nofile 65536
        * hard nofile 65536
        
      • 修改限制一个进程可以拥有的最大虚拟存储区域的数量 vim /etc/sysctl.conf 追加:

        vm.max_map_count=655360
        
      • 修改内核参数生效

        sysctl -p
        
      • 重启虚拟机和容器

        reboot
        
      4、可能遇到容器起来又挂掉的情况,解决办法:
      • 先查看日志:

        docker logs myEs
        
      • 可能的问题:

        1、没有设置 vm.max_map_count=655360   这个的最小限制是 262144 要大于这个值
        
        2、配置文件写错了,无法进入容器。这时可以使用 docker cp 将容器里面的配置文件 copy 出来,修改后再 copy 回去。
        

2、常用的查询语句

#查询所有索引
GET /_cat/indices?v

#增加索引
PUT /user
#给索引添加 mapping
PUT /user/userinfo/_mapping
{
  "properties": {
    "name":{
      "type": "text",
      "analyzer": "standard",
      "search_analyzer": "standard"
    },
    "city":{
      "type": "text",
      "analyzer": "standard",
      "search_analyzer": "standard"
    },
    "age":{
      "type": "long"
    },
    "description":{
      "type": "text",
      "analyzer": "standard",
      "search_analyzer": "standard"
    }
  }  
}

#添加数据
PUT /user/userinfo/1
{
  "name":"李四",
  "city":"哈尔滨",
  "age":19,
  "decrption":"哈尔滨李四"
}

PUT /user/userinfo/2
{
  "name":"王五",
  "city":"四川",
  "age":29,
  "decrption":"王五是四川的"
}

PUT /user/userinfo/3
{
  "name":"赵子龙",
  "city":"常山" ,
  "age":39,
  "decrption":"常山赵子龙"
}
PUT /user/userinfo/4
{
  "name":"老六",
  "city":"四川",
  "age":29,
  "decrption":"老六是四川的",
  "address":"赛博坦"
}
PUT /user/userinfo/5
{
  "name":"老五",
  "city":"四川",
  "age":29,
  "decrption":"老五是四川的",
  "address":"赛博坦"
}
#修改数据
POST /user/userinfo/5
{
  "name":"哈哈"
}

#查询单个  
GET /user/userinfo/5
#查询所有
GET /user/userinfo/_search
#排序
GET /user/userinfo/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "age": {
        "order": "desc"
      }
    }
  ]
}

#分页
GET /user/userinfo/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "age": {
        "order": "desc"
      }
    }
  ],
  "from": 0,
  "size": 3
}

#match 查询 分词 
GET /user/userinfo/_search
{
  "query": {
    "match": {
      "city":  "四川"
    }
  }
}
#term 查询 不 分词 
GET /user/userinfo/_search
{
  "query": {
    "term": {
      "city":{
        "value":"四川"
      }
    }
  }
}

#querystring 查询suo 有索引   
GET _search
{
  "query": {
    "query_string": {
      "default_field": "city",
      "query": "四川"
    }
  }
}

#范围查询
GET _search
{
  "query": {
    "range": {
      "age": {
        "gte": 20,
        "lte": 30
      }
    }
  }
}

#存在exists 属性 address 
GET /user/userinfo/_search
{
  "query": {
    "exists":{
      "field":"address"
    }
  }
}

#布尔查询
#should(or)、must(and)、must_not(not) should 里面是两个对象
GET _search
{
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "city": {
              "value": "四川"
            }
          }
        },
        {
            "range": {
            "age": {
              "gte": 20,
              "lte": 30
            }
          }
        }
      ]
    }
  }
}

#前缀查询
GET /user/userinfo/_search
{
  "query": {
    "prefix": {
      "name": {
        "value": "老"
      }
    }
  }
}

#多域匹配 fields里面含有query的都是    
GET /user/userinfo/_search
{
  "query": {
    "multi_match":{
      "query": "哈尔滨",
      "fields": [
        "city",
        "decrption"
        ]
    }
  }
}

#使用过滤,在单次查询全部后,之后的查询应尽量使用过滤
GET /user/userinfo/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "city": "四川"
          }
        }
      ],
      "filter": {
        "range": {
          "age": {
            "gte": 10,
            "lte": 30
          }
        }
      }
    }
  }
}

#聚合查询
#按颜色聚合
GET /car_index/car/_search
{
  "query": {"match_all": {}},
  "aggs": {
    "group_by_color": {
      "terms": {
        "field": "color"
      }
    }
  }
}

#按颜色、均价聚合
GET /car_index/car/_search
{
  "query": {"match_all": {}},
  "aggs": {
    "group_by_color": {
      "terms": {
        "field": "color"
      },
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "price"
          }
        }
      }
    }
  }
}

3、JAVA 整合 ES

3.1、普通整合

  • 创建文档实体类,就是一条记录的实体。这里有个小注意点,如果添加了带参数的构造方法,要再添加一个无参的构造方法。否则查询到的数据转化为实体类时会报 ClassNotFound 的异常。
public class Article {
    private Integer id;
    private String title;
    private String content;

    public Article(Integer id, String title, String content) {
        this.id = id;
        this.title = title;
        this.content = content;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

}

  • 创建测试。依次创建索引、映射、记录

    public class testIndex {
        Settings settings;
        PreBuiltTransportClient client;
        @Before
        public void before() throws Exception{
            //1、创建 setting
            settings = Settings.builder().put("cluster.name", "my-application").build();
            //2、产生客户端
            client = new PreBuiltTransportClient(settings);
            client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.129.3"),9300));
    
    
        }
    
        /*创建索引*/
        @Test
        public void createIndex(){
            //3、创建索引
            client.admin().indices().prepareCreate("test_index").get();
        }
    
        /*创建 mapping 映射*/
        @Test
        public void createMapping() throws IOException {
            XContentBuilder xContentBuilder = XContentFactory.jsonBuilder().
                    startObject()
                    .startObject("article")
                    .startObject("properties")
                    .startObject("id")
                    .field("type", "integer").field("store", "yes")
                    .endObject()
                    .startObject("title")
                    .field("type", "string").field("store", "yes").field("analyzer", "standard")
                    .endObject()
                    .startObject("content")
                    .field("type", "string").field("store", "yes").field("analyzer", "standard")
                    .endObject()
                    .endObject()
                    .endObject()
                    .endObject();
    
            /*创建映射*/
            client.admin().indices().preparePutMapping("test_index").setType("article").setSource(xContentBuilder).get();
        }
    
        /*创建文档*/
        @Test
        public void createContent() throws Exception{
            XContentBuilder builder = XContentFactory.jsonBuilder()
                    .startObject()
                    .field("id", "2")
                    .field("title", "北方入秋速度明显加快,多地降温可达10到20度")
                    .field("content", "阿联酋一架客机在纽约机场被隔离,10名乘客病倒")
                    .endObject();
    
            /*把文档对象加入索引库*/
            client.prepareIndex()
                    .setIndex("test_index")
                    .setType("article")
                    .setId("2")
                    .setSource(builder)
                    .get();
        }
    
        /*使用实体类转换 json 字符 进行文档添加*/
        @Test
        public void createContentByBean() throws JsonProcessingException {
            Article article = new Article(3,"MH370坠毁在柬埔寨密林?中国一公司调十颗卫星去拍摄","警惕荒唐的死亡游戏!俄15岁少年输掉游戏后用电锯自杀");
            String json = new ObjectMapper().writeValueAsString(article);
    
            client.prepareIndex()
                    .setIndex("test_index")
                    .setId("3")
                    .setType("article")
                    .setSource(json, XContentType.JSON)
                    .get();
        }
    
        /*批量添加测试数据*/
        @Test
        public void createMany() throws JsonProcessingException {
            for (int i = 4; i <=100 ; i++) {
                Article article = new Article(i,"女护士路遇昏迷男子跪地抢救:救人是职责更是本能"+ i,"江西变质营养餐事件已致24人就医多名官员被调查" + i);
                String json = new ObjectMapper().writeValueAsString(article);
    
                client.prepareIndex("test_index","article",""+article.getId())
                        .setSource(json,XContentType.JSON)
                        .get();
            }
        }
    
    
        @After
        public void end(){
            //4、关闭连接
            client.close();
        }
    }
    
  • 查询测试。依次进行 term、queryString、 match、主键、分页、高亮查询

    public class testSearch {
        Settings settings;
        PreBuiltTransportClient client;
    
        @Before
        public void before() throws Exception{
            //1、创建 setting
            settings = Settings.builder().put("cluster.name", "my-application").build();
            //2、产生客户端
            client = new PreBuiltTransportClient(settings);
            client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("192.168.129.3"),9300));
        }
    
        /*term 查询--*/
        @Test
        public void testTerm(){
            //1、构建 queryBuilder
            QueryBuilder query = QueryBuilders.termQuery("title", "女");
            search(query);
        }
    
        /* queryString 查询--*/
        @Test
        public void testQueryString(){
            //1、构建 queryBuilder
            QueryBuilder query = QueryBuilders.queryStringQuery("女护士").defaultField("title");
            search(query);
        }
    
        /* match 查询--*/
        @Test
        public void testMatch(){
            //1、构建 queryBuilder
            QueryBuilder query = QueryBuilders.matchQuery("title", "美丽的女护士");
            search(query);
        }
    
        /* id 查询*/
        @Test
        public void testQueryById(){
            IdsQueryBuilder builder = QueryBuilders.idsQuery().addIds("1", "2");
            search(builder);
        }
    
        /*分页查询   只需要在查询条件里面加入 from 和 size 即可*/
        @Test
        public void testPage(){
            MatchAllQueryBuilder allQuery = QueryBuilders.matchAllQuery();
            SearchResponse response = client.prepareSearch("test_index").setTypes("article").setQuery(allQuery).setFrom(0).setSize(5).get();
    
            //3、遍历输出结果
            SearchHits searchHits = response.getHits();
            Iterator<SearchHit> hitIterator = searchHits.iterator();
            System.out.println("total columns: " + searchHits.getTotalHits());
            while (hitIterator.hasNext()) {
                SearchHit hit = hitIterator.next();
                System.out.println(hit.getSourceAsString());
                System.out.println("document fields : -----------");
                Map<String, Object> map = hit.getSourceAsMap();
                System.out.println(map.get("id"));
                System.out.println(map.get("title"));
                System.out.println(map.get("content"));
            }
        }
    
        /*高亮显示*/
        @Test
        public void testHighLight(){
            //1、创建 builders   (多个属性查寻)
            MultiMatchQueryBuilder allQuery = QueryBuilders.multiMatchQuery("4","title","content");
            //2、创建 HighLight Builder
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("title");
            highlightBuilder.field("content");
            highlightBuilder.preTags("<em>");
            highlightBuilder.postTags("</em>");
            //执行查询
            SearchResponse response = client.prepareSearch("test_index")
                    .setTypes("article")
                    .setQuery(allQuery)
                    .highlighter(highlightBuilder)
                    .get();
    
            //3、遍历输出结果   对每一个 hitIterator 都有
            SearchHits searchHits = response.getHits();
            Iterator<SearchHit> hitIterator = searchHits.iterator();
            System.out.println("total columns: " + searchHits.getTotalHits());
            while (hitIterator.hasNext()) {
                SearchHit hit = hitIterator.next();
                System.out.println(hit.getSourceAsString());
                System.out.println("highlight fields : -----------");
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                highlightFields.entrySet().forEach(set ->{
                    System.out.println(set.getKey()+"\t" + Arrays.toString(set.getValue().getFragments()));
                });
            }
        }
    
    
    	/*提取出的公共部分,组成一个方法*/
        private void search(QueryBuilder query) {
            //2、执行查询
            SearchResponse response = client.prepareSearch("test_index").setTypes("article").setQuery(query).get();
    
            //3、遍历输出结果
            SearchHits searchHits = response.getHits();
            Iterator<SearchHit> hitIterator = searchHits.iterator();
            System.out.println("total columns: " + searchHits.getTotalHits());
            while (hitIterator.hasNext()) {
                SearchHit hit = hitIterator.next();
                System.out.println(hit.getSourceAsString());
                System.out.println("document fields : -----------");
                Map<String, Object> map = hit.getSourceAsMap();
                System.out.println(map.get("id"));
                System.out.println(map.get("title"));
                System.out.println(map.get("content"));
            }
        }
    
        @After
        public void end(){
            //4、关闭连接
            System.out.println("连接关闭了------");
            client.close();
        }
    }
    

3.2 spring-boot 整合

  • 相对于原始的整合方法,spring-boot 提供了 spring-data,自带的 elasticsearch API 可以提供更简便姐的查询方式。

    创建时使用 。我这里使用的是 spring-boot 2.1.2 。更高版本的可能不适用,一些 api 已经改变了。创建时记得加 NOSQl 的 elasticsearch 组件

  • 引入依赖

        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.2.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <groupId>com.kkb</groupId>
        <artifactId>spring-data-elasticsearch</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>spring-data-elasticsearch</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>11</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
  • 添加配置文件 application.yml 来配置 elasticsearch

    spring:
      data:
        elasticsearch:
          cluster-name: my-application
          cluster-nodes: 192.168.129.3:9300
    
  • 创建文档实体类。

  • 通过 @Document 注解来指定索引名 和 类型名。相当于库名和表名。@Id 指定主键,@Field 来指定该属性的存储类型、持久化策略、分词器等信息。这里也不要忘记无参构造的问题

    @Document(indexName = "scm_index",type="article")
    public class Article {
    
        @Id
        @Field(type = FieldType.Long,store = true)
        private long id;
    
        @Field(type = FieldType.Text,store = true,analyzer = "standard")
        private String title;
    
        @Field(type = FieldType.Text,store = true,analyzer = "standard")
        private String content;
    
        public long getId() {
            return id;
        }
    
        public void setId(long id) {
            this.id = id;
        }
    
        public String getTitle() {
            return title;
        }
    
        public void setTitle(String title) {
            this.title = title;
        }
    
        public Article(long id, String title, String content) {
            this.id = id;
            this.title = title;
            this.content = content;
        }
    
        public Article() {
        }
    
        public String getContent() {
            return content;
        }
    
        public void setContent(String content) {
            this.content = content;
        }
        @Override
        public String toString() {
            return "Article{" +
                    "id=" + id +
                    ", title='" + title + '\'' +
                    ", content='" + content + '\'' +
                    '}';
        }
    }
    
  • 使用 自带的 API 进行查询 。继承 ElasticsearchRepository<T,K>

    /*命名规则查询    findBy 属性名+连接词+属性名+(参数)*/
    public interface ArticleDao extends ElasticsearchRepository<Article,Long> {
    
        /*根据 title 查询*/
        public List<Article> findByTitle(String title);
    
        /*根据 title 或 content 查询   like 代表该属性使用 match 查询,否则是 term,两者的区别在于是否分词*/
        public List<Article> findByTitleLikeOrContent(String title,String content);
    
        /*分页查询*/
        public List<Article> findByTitle(String title, Pageable pageable);
    }
    
  • 测试:

    @RunWith(SpringRunner.class)
    @SpringBootTest
    /*这个类竟然要是 public 的*/
    public class SpringDataElasticsearchApplicationTests {
        @Autowired
        private ElasticsearchTemplate template;  
        @Autowired
        private ArticleDao dao;
        /*创建索引*/
        @Test
        public void testAddIndex() {
            //使用带参数的 构造方法会自己创建 mapping,
            template.createIndex(Article.class);
            //template.putMapping(Article.class);
        }
    
        /*添加、修改数据*/
        @Test
        public void testAddDocument(){
            for (int i = 0; i < 10; i++) {
                Article article = new Article((long) i,"女护士路遇昏迷男子跪地抢救:救人是职责更是本能"+i,"这是一个美丽的护士妹妹"+i);
                dao.save(article);
            }
        }
    
        /*查询单个数据   删除也差不多*/
        @Test
        public void testFindById(){
            System.out.println(dao.findById(0L));
        }
    
        /*like 查询*/
        /*使用 like 是 match 分词查询,不使用是 term 不分词查询 */
        @Test
        public void testLike(){
            dao.findByTitleLikeOrContent("女护","女护士").forEach(System.out::println);
        }
    
        /*分页查询*/
        @Test
        public void testPage(){
            Pageable pageable =PageRequest.of(1,5);
            dao.findByTitle("女护士",pageable).forEach(System.out::println);
        }
    
        /*使用本地查询*/
        /*外加添加分页*/
        @Test
        public void testNative(){
            NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
                    .withQuery(QueryBuilders.queryStringQuery("女护士").defaultField("title"))
                    .withPageable(PageRequest.of(1, 5))
                    .build();
    
            template.queryForList(nativeSearchQuery,Article.class).forEach(System.out::println);
        }
    }
    
  • 聚合查询也就是分组查询

    • 创建一个索引实体 car

      @Document(indexName = "car_index",type = "car")
      public class Car {
      
          @Id
          @Field(type = FieldType.Long,store = true)
          private Long id;
          @Field(type = FieldType.Text,store = true,analyzer = "standard")
          private String name;
          @Field(type = FieldType.Text,store = true,analyzer = "standard",fielddata = true)
          private String brand;
          @Field(type = FieldType.Text,store = true,analyzer = "standard",fielddata = true)
          private String color;
          @Field(type = FieldType.Double,store = true)
          private double price;
      
          public Car() {
          }
      
          public Car(Long id, String name, String brand, String color, double price) {
              this.id = id;
              this.name = name;
              this.brand = brand;
              this.color = color;
              this.price = price;
          }
      
          public Long getId() {
              return id;
          }
      
          public void setId(Long id) {
              this.id = id;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public String getBrand() {
              return brand;
          }
      
          public void setBrand(String brand) {
              this.brand = brand;
          }
      
          public String getColor() {
              return color;
          }
      
          public void setColor(String color) {
              this.color = color;
          }
      
          public double getPrice() {
              return price;
          }
      
          public void setPrice(double price) {
              this.price = price;
          }
      
          @Override
          public String toString() {
              return "Car{" +
                      "id=" + id +
                      ", name='" + name + '\'' +
                      ", brand='" + brand + '\'' +
                      ", color='" + color + '\'' +
                      ", price=" + price +
                      '}';
          }
      }
      
    • 继承 ElasticsearchRepository

      public interface carDAO extends ElasticsearchRepository<Car,Long> {
      
      }
      
    • 测试:

      @RunWith(SpringRunner.class)
      @SpringBootTest
      public class unitSearchTest {
          @Autowired
          private carDAO dao;
      
          /*添加数据*/
          @Test
          public void addData(){
              for (int i = 0; i < 10; i=i+2) {
                  dao.save(new Car((long) i,"s1","奔驰"+i,"白色",20+i));
                  dao.save(new Car((long) i+1,"A4L","奥迪"+(i+1),"白色",10+i));
              }
          }
      
      
          /*聚合查询 复杂的查询都使用本地查询*/
          @Test
          public void testUnit(){
              NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder().withQuery(QueryBuilders.matchAllQuery());
              //添加过滤条件    不查询 brand 字段
              queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{},new String[]{"brand"}));
              //添加聚合
              queryBuilder.addAggregation(AggregationBuilders.terms("group_by_color").field("color")
                          .subAggregation(AggregationBuilders.avg("avg_price").field("price")));
      
              //转换结果
              AggregatedPage<Car> aggPage = (AggregatedPage<Car>) dao.search(queryBuilder.build());
              //从结果中获取 bucket 对应的名字的聚合
              StringTerms agg = (StringTerms)aggPage.getAggregation("group_by_color");
              List<StringTerms.Bucket> buckets = agg.getBuckets();
      
              //遍历结果
              buckets.forEach(b->{
                  InternalAvg avg = (InternalAvg) b.getAggregations().getAsMap().get("avg_price");
                  System.out.println("color : "+b.getKeyAsString()+" count :"+b.getDocCount()+"avg_price : "+avg.getValue());
              });
      
          }
      
      }
      
    • 效果同于:

      #按颜色、均价聚合
      GET /car_index/car/_search
      {
        "query": {"match_all": {}},
        "aggs": {
          "group_by_color": {
            "terms": {
              "field": "color"
            },
            "aggs": {
              "avg_price": {
                "avg": {
                  "field": "price"
                }
              }
            }
          }
        }
      }
      
posted @ 2022-04-11 15:05  心是冰冰的  阅读(202)  评论(0)    收藏  举报