欢迎大家进群,一起探讨学习
微信公众号,每天给大家提供技术干货
博主技术平台地址
博主开源微服架构前后端分离技术博客项目源码地址,欢迎各位star
SpringBoot整合elasticsearch
pom引入
<!--elasticsearch-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
yml配置
spring:
data:
elasticsearch:
rest:
uris: ["ip:9200"]
elasticsearch:
cluster-name: docker-cluster
cluster-nodes: ip:9300 #配置es节点信息,逗号分隔,如果没有指定,则启动ClientNode
properties:
path:
logs: ./elasticsearch/log #elasticsearch日志存储目录
data: ./elasticsearch/data #elasticsearch数据存储目录
配置只需这么些,接下来就写一些demo来玩一下elaseticsearch
构建Item类
@Document(indexName = "item",type = "docs", shards = 1, replicas = 0)
public class Item {
@Id
private Long id;
//文章使用分词器
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String title; //标题
@Field(type = FieldType.Keyword)
private String category;// 分类
@Field(type = FieldType.Keyword)
private String brand; // 品牌
@Field(type = FieldType.Double)
private Double price; // 价格
}
创建ItemRepository并继承ElasticsearchRepository,有兴趣的可以看一下底层源码
public interface ItemRepository extends ElasticsearchRepository<Item,Long>{
/**
* @Description:根据价格区间查询 自定义查询
* @Param price1
* @Param price2
*/
List<Item> findByPriceBetween(double price1, double price2);
List<Item> findByTitle(String title1);
List<Item> findByTitleIn(Collection<String> ss);
}
创建索引
@RunWith(SpringRunner.class)
@SpringBootTest(classes = BootApplication.class)
public class EsDemoApplicationTest{
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
/**
* @Description:创建索引,会根据Item类的@Document注解信息来创建
*/
@Test
public void testCreateIndex() {
elasticsearchRestTemplate.indexOps(Item.class).create();
}
/**
* @Description:删除索引
*/
@Test
public void testDeleteIndex() {
elasticsearchRestTemplate.indexOps(Item.class).delete();
}
/**
* @Description:判断索引是否存在
*/
@Test
public void testExistIndex() {
elasticsearchRestTemplate.indexOps(Item.class).exists();
}
/**
* @Description:删除
*/
@Test
public void delete() {
elasticsearchRestTemplate.delete(1L, Item.class);
}
/**
* 查询文档数据
*/
@Test
public void getDoc() {
Item item = elasticsearchRestTemplate.get(String.valueOf(1L), Item.class);
System.out.println(item);
}
/**
* 修改文档数据
*/
@Test
public void updateDoc() {
Map<String, Object> map = new HashMap<>();
map.put("title", "abc");
Document doc = Document.from(map);
UpdateQuery updateQuery = UpdateQuery
.builder(String.valueOf(1))
.withDocument(doc)
.build();
IndexCoordinates indexCoordinates = IndexCoordinates.of("item");
elasticsearchRestTemplate.update(updateQuery, indexCoordinates);
}
/**
* 分页搜索数据
* 使用QueryBuilder
* termQuery("key", obj) 完全匹配
* termsQuery("key", obj1, obj2..) 一次匹配多个值
* matchQuery("key", Obj) 单个匹配, field不支持通配符, 前缀具高级特性
* multiMatchQuery("text", "field1", "field2"..); 匹配多个字段, field有通配符忒行
* matchAllQuery(); 匹配所有文件
* idsQuery(); 只查询一个id的
* fuzzyQuery(); 模糊查询 不能用通配符, 找到相似的
*/
@Test
public void search() {
Pageable pageable = PageRequest.of(0, 10);
SortBuilder<FieldSortBuilder> sortBuilder = new FieldSortBuilder("price")
.order(SortOrder.DESC);
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.boolQuery().should(QueryBuilders.fuzzyQuery("title", "360")))
.withPageable(pageable)
.withSort(sortBuilder)
.build();
SearchHits<Item> search = elasticsearchRestTemplate.search(query, Item.class);
System.out.println(search.getSearchHits());
}
/**
* 高亮搜索
*/
@Test
public void highlight() {
String preTag = "<font color='red'>";
String postTag = "</font>";
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchQuery("title", "360"))
.withHighlightFields(new HighlightBuilder.Field("title")
.preTags(preTag)
.postTags(postTag))
.build();
SearchHits<Item> searchHits = elasticsearchRestTemplate.search(query, Item.class);
List<SearchHit<Item>> searchHitList = searchHits.getSearchHits();
List<Map<String, Object>> hlList = new ArrayList<>();
for (SearchHit h : searchHitList) {
List<String> highlightField = h.getHighlightField("title");
String nameValue = highlightField.get(0);
String originalJson = JSON.toJSONString(h.getContent());
JsonParser jj = new GsonJsonParser();
Map<String, Object> myHighLight = jj.parseMap(originalJson);
// 用高亮的搜索结果覆盖原字段值
myHighLight.put("title", nameValue);
System.out.println(myHighLight);
hlList.add(myHighLight);
}
System.out.println(hlList);
}
/**
* 高亮搜索 排序加分页
*/
@Test
public void highlight1() {
String preTag = "<font color='red'>";
String postTag = "</font>";
Pageable pageable = PageRequest.of(0, 10);
SortBuilder<FieldSortBuilder> sortBuilder = new FieldSortBuilder("price")
.order(SortOrder.DESC);
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.fuzzyQuery("title", "360"))
.withPageable(pageable)
.withSort(sortBuilder)
.withHighlightFields(new HighlightBuilder.Field("title")
.preTags(preTag)
.postTags(postTag))
.build();
SearchHits<Item> searchHits = elasticsearchRestTemplate.search(query, Item.class);
List<SearchHit<Item>> searchHitList = searchHits.getSearchHits();
List<Map<String, Object>> hlList = new ArrayList<>();
for (SearchHit h : searchHitList) {
List<String> highlightField = h.getHighlightField("title");
String nameValue = highlightField.get(0);
String originalJson = JSON.toJSONString(h.getContent());
JsonParser jj = new GsonJsonParser();
Map<String, Object> myHighLight = jj.parseMap(originalJson);
// 用高亮的搜索结果覆盖原字段值
myHighLight.put("title", nameValue);
hlList.add(myHighLight);
}
System.out.println(hlList);
}
}
QueryBuilder构造ES查询条件使用规则
第二种实现方式 RestHighLevelClient
package comn.hy.search.service.impl;
import com.hy.search.Stu;
import com.hy.utils.JsonUtils;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
@RestController
public class TestServiceES {
@Resource
private RestHighLevelClient client;
/**
* 创建索引
* @throws IOException
*/
@PostMapping("create-index")
public void createIndex() throws IOException {
CreateIndexRequest request = new CreateIndexRequest("user_index");
CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
System.out.println(response);
}
/**
* 获取索引信息
* @throws IOException
*/
@GetMapping("query-index")
public void queryIndex() throws IOException {
GetIndexRequest request = new GetIndexRequest("user_index");
boolean b = client.indices().exists(request, RequestOptions.DEFAULT);
System.out.println(b);
}
/**
* 删除索引
* @throws IOException
*/
@DeleteMapping("delete-index")
public void deleteIndex() throws IOException {
DeleteIndexRequest request = new DeleteIndexRequest("user_index");
AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
System.out.println(delete.isAcknowledged());
}
/**
* 新增一个文档
* @throws IOException
*/
@PostMapping("add-doc")
public void addDoc() throws IOException {
Stu stu = new Stu(10010L, "绝世风华", 18, 100.5f, true);
IndexRequest request = new IndexRequest("user_index");
// 规则 put /test_index/_doc/1
request.id("1");
request.timeout(new TimeValue(60, TimeUnit.SECONDS));
request.source(JsonUtils.objectToJson(stu),XContentType.JSON);
// 发送
IndexResponse response = client.index(request,RequestOptions.DEFAULT);
System.out.println(response.toString());
System.out.println(response.status());
}
/**
* 查询文档数据
* @throws IOException
*/
@GetMapping("query-doc")
void queryDoc() throws IOException {
GetRequest request = new GetRequest("user_index","1");
request.fetchSourceContext(new FetchSourceContext(false));
request.storedFields("_none_");
// 是否存在???
boolean b = client.exists(request,RequestOptions.DEFAULT);
System.out.println(b);
// 获取
GetResponse response = client.get(request,RequestOptions.DEFAULT);
System.out.println(response.getSourceAsString());
System.out.println(response);
}
/**
* 更新文檔
* @throws IOException
*/
@PutMapping("update-doc")
public void updateDoc() throws IOException {
UpdateRequest updateRequest = new UpdateRequest("user_index","1");
updateRequest.timeout("1s");
Stu stu = new Stu(10010L, "上善若水", 18, 100.5f, true);
updateRequest.doc(JsonUtils.objectToJson(stu),XContentType.JSON);
UpdateResponse response = client.update(updateRequest,RequestOptions.DEFAULT);
System.out.println(response);
System.out.println(response.status());
}
/**
* 删除文档
* @throws IOException
*/
@DeleteMapping("delete-doc")
public void deleteDoc() throws IOException {
DeleteRequest deleteRequest = new DeleteRequest("user_index","1");
deleteRequest.timeout("1s");
DeleteResponse response = client.delete(deleteRequest,RequestOptions.DEFAULT);
System.out.println(response);
System.out.println(response.status());
}
/**
* 批量操作
* @throws IOException
*/
@PostMapping("bulk-operator")
public void bulkOperator() throws IOException {
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("15s");
Stu stu0 = new Stu(10010L, "诸天万界之起源传说", 18, 100.5f, true);
Stu stu1 = new Stu(10011L, "寒夜", 20, 88.5f, true);
Stu stu2 = new Stu(10012L, "陌上千寻雪", 22, 96.5f, false);
Stu stu3 = new Stu(10013L, "可爱的漂亮的小哥哥", 26, 108.5f, false);
Stu stu4 = new Stu(10014L, "灵纪传说", 28, 108.6f, true);
Stu stu5 = new Stu(10015L, "狂剑天下之鸿蒙掌控", 16, 18.5f, false);
Stu stu6 = new Stu(10016L, "逆战次元", 29, 100.5f, true);
ArrayList<Stu> stuList = new ArrayList<>();
stuList.add(stu0);
stuList.add(stu1);
stuList.add(stu2);
stuList.add(stu3);
stuList.add(stu4);
stuList.add(stu5);
stuList.add(stu6);
for(int i = 0;i<=stuList.size();i++){
bulkRequest.add(
new IndexRequest("test.index").
id(""+(i+1)).
source(JsonUtils.objectToJson(stuList.get(i)), XContentType.JSON));
}
// 批量插入
BulkResponse response = client.bulk(bulkRequest,RequestOptions.DEFAULT);
System.out.println(response.hasFailures());
}
/**
* 条件查询
* @throws IOException
*/
@GetMapping("condition-search")
public void conditionSearch() throws IOException {
SearchRequest searchRequest = new SearchRequest("user_index");
// 构造条件
SearchSourceBuilder builder = new SearchSourceBuilder();
// 精确匹配
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name","绝世风华");
// 匹配全部 matchAllQuery();
builder.query(termQueryBuilder);
// 分页
builder.from(0);
builder.size(10);
builder.timeout(TimeValue.MINUS_ONE);
searchRequest.source(builder);
SearchResponse response = client.search(searchRequest,RequestOptions.DEFAULT);
System.out.println(JsonUtils.objectToJson(response.getHits()));
for (org.elasticsearch.search.SearchHit hit : response.getHits().getHits()) {
hit.getSourceAsMap().get("name");
}
}
}
先执行创建索引
索引数据操作
@RunWith(SpringRunner.class)
@SpringBootTest(classes = BootApplication.class)
public class ceshiTest {
@Autowired
private ItemRepository itemRepository;
/**
* @Description:定义新增方法
*/
@Test
public void insert() {
Item item = new Item(1L, "小米手机7", " 手机",
"小米", 3499.00);
itemRepository.save(item);
}
/**
* @Description:定义批量新增方法
*/
@Test
public void insertList() {
List<Item> list = new ArrayList<>();
list.add(new Item(1L, "小米9", "手机", "小米", 3299.00));
list.add(new Item(2L, "华为pro30", "手机", "华为", 3999.00));
list.add(new Item(3L, "一加7", "手机", "一加", 2999.00));
list.add(new Item(4L, "魅族16", "手机", "魅族", 1999.00));
list.add(new Item(5L, "苹果xs", "手机", "苹果", 5099.00));
list.add(new Item(6L, "360pro", "手机", "360", 1099.00));
list.add(new Item(7L, "荣耀V10", "手机", "华为", 899.00 ));
// 接收对象集合,实现批量新增
itemRepository.save(list);
}
/**
* @Description:按照价格区间查询 自定义方法
* 自定义方法
Spring Data 的另一个强大功能,是根据方法名称自动实现功能。
比如:你的方法名叫做:findByTitle,那么它就知道你是根据title查询,然后自动帮你完成,无需写实现类。
当然,方法名称要符合一定的约定 下边为约定
And findByNameAndPrice
Or findByNameOrPrice
Is findByName
Not findByNameNot
Between findByPriceBetween
LessThanEqual findByPriceLessThan
GreaterThanEqual findByPriceGreaterThan
Before findByPriceBefore
After findByPriceAfter
Like findByNameLike
StartingWith findByNameStartingWith
EndingWith findByNameEndingWith
Contains/Containing findByNameContaining
In findByNameIn(Collection<String>names)
NotIn findByNameNotIn(Collection<String>names)
Near findByStoreNear
True findByAvailableTrue
False findByAvailableFalse
OrderBy findByAvailableTrueOrderByNameDesc
* @Author: https://blog.csdn.net/chen_2890
*/
@Test
public void queryByPriceBetween(){
List<Item> list = this.itemRepository.findByPriceBetween(2000.00, 3500.00);
for (Item item : list) {
System.out.println("item = " + item.getTitle());
}
}
@Test
public void queryByTitle(){
List<Item> list = this.itemRepository.findByTitle("华为");
for (Item item : list) {
System.out.println("item = " + item.getTitle());
}
}
@Test
public void queryByTitleTo(){
Collection<String> ss = new ArrayList<>();
ss.add("华为");
ss.add("小米");
List<Item> list = this.itemRepository.findByTitleIn(ss);
for (Item item : list) {
System.out.println("item = " + item.getTitle());
}
}
/**
* @Description:matchQuery底层采用的是词条匹配查询
* @Author: https://blog.csdn.net/chen_2890
*/
@Test
public void testMatchQuery(){
// 构建查询条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 添加基本分词查询
queryBuilder.withQuery(QueryBuilders.matchQuery("title", "华为"));
// 搜索,获取结果
Page<Item> items = this.itemRepository.search(queryBuilder.build());
// 总条数
long total = items.getTotalElements();
System.out.println("获取的总条数 = " + total);
for (Item item : items) {
System.out.println("手机名称是:"+item.getTitle());
}
}
/**
* @Description:
* termQuery:功能更强大,除了匹配字符串以外,还可以匹配
* int/long/double/float/....
* @Author: https://blog.csdn.net/chen_2890
*/
@Test
public void testTermQuery(){
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
builder.withQuery(QueryBuilders.termQuery("price",1099));
// 查找
Page<Item> page = this.itemRepository.search(builder.build());
for(Item item:page){
System.out.println("手机是:"+item.getTitle());
}
}
/**
* @Description:布尔查询 多条件查询
* @Author: https://blog.csdn.net/chen_2890
*/
@Test
public void testBooleanQuery(){
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
builder.withQuery(
QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("title","华为"))
.must(QueryBuilders.matchQuery("brand","华为"))
);
// 查找
Page<Item> page = this.itemRepository.search(builder.build());
for(Item item:page){
System.out.println("手机名称是"+item.getTitle());
}
}
/**
* @Description:布尔查询 多条件查询
* @Author: https://blog.csdn.net/chen_2890
*/
@Test
public void testBlQuery(){
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
builder.withQuery(
QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("title","荣耀"))
.must(QueryBuilders.matchQuery("title","华为"))
);
// 查找
Page<Item> page = this.itemRepository.search(builder.build());
for(Item item:page){
System.out.println("手机名称是"+item.getTitle());
}
}
/**
* @Description:模糊查询
* @Author: https://blog.csdn.net/chen_2890
*/
@Test
public void testFuzzyQuery(){
NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
builder.withQuery(QueryBuilders.fuzzyQuery("title","一"));
Page<Item> page = this.itemRepository.search(builder.build());
for(Item item:page){
System.out.println("手机名称是:"+item.getTitle());
}
}
/**
* @Description:分页查询
* @Author: https://blog.csdn.net/chen_2890
*/
@Test
public void searchByPage(){
// 构建查询条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 添加基本分词查询
queryBuilder.withQuery(QueryBuilders.termQuery("category", "手机"));
// 分页:
int page = 0;
int size = 2;
queryBuilder.withPageable(PageRequest.of(page,size));
// 搜索,获取结果
Page<Item> items = this.itemRepository.search(queryBuilder.build());
// 总条数
long total = items.getTotalElements();
System.out.println("总条数 = " + total);
// 总页数
System.out.println("总页数 = " + items.getTotalPages());
// 当前页
System.out.println("当前页:" + items.getNumber());
// 每页大小
System.out.println("每页大小:" + items.getSize());
for (Item item : items) {
System.out.println(item.getTitle());
}
}
/**
* @Description:排序查询
* @Author: https://blog.csdn.net/chen_2890
*/
@Test
public void searchAndSort(){
// 构建查询条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 添加基本分词查询
queryBuilder.withQuery(QueryBuilders.termQuery("category", "手机"));
// 排序
queryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.DESC));
// 搜索,获取结果
Page<Item> items = this.itemRepository.search(queryBuilder.build());
// 总条数
long total = items.getTotalElements();
System.out.println("总条数 = " + total);
for (Item item : items) {
System.out.println("手机的价格是:"+item.getTitle()+":"+item.getPrice());
}
}
/**
* @Description:按照品牌brand进行分组
* @Author: https://blog.csdn.net/chen_2890
*/
@Test
public void testAgg(){
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 不查询任何结果
queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
// 1、添加一个新的聚合,聚合类型为terms,聚合名称为brands,聚合字段为brand
queryBuilder.addAggregation(
AggregationBuilders.terms("brands").field("brand"));
// 2、查询,需要把结果强转为AggregatedPage类型
AggregatedPage<Item> aggPage = (AggregatedPage<Item>) this.itemRepository.search(queryBuilder.build());
// 3、解析
// 3.1、从结果中取出名为brands的那个聚合,
// 因为是利用String类型字段来进行的term聚合,所以结果要强转为StringTerm类型
StringTerms agg = (StringTerms) aggPage.getAggregation("brands");
// 3.2、获取桶
List<StringTerms.Bucket> buckets = agg.getBuckets();
// 3.3、遍历
for (StringTerms.Bucket bucket : buckets) {
// 3.4、获取桶中的key,即品牌名称
System.out.println(bucket.getKeyAsString());
// 3.5、获取桶中的文档数量
System.out.println(bucket.getDocCount());
}
}
/**
* @Description:嵌套聚合,求平均值
* @Author: https://blog.csdn.net/chen_2890
*/
@Test
public void testSubAgg(){
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 不查询任何结果
queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
// 1、添加一个新的聚合,聚合类型为terms,聚合名称为brands,聚合字段为brand
queryBuilder.addAggregation(
AggregationBuilders.terms("brands").field("brand")
.subAggregation(AggregationBuilders.avg("priceAvg").field("price")) // 在品牌聚合桶内进行嵌套聚合,求平均值
);
// 2、查询,需要把结果强转为AggregatedPage类型
AggregatedPage<Item> aggPage = (AggregatedPage<Item>) this.itemRepository.search(queryBuilder.build());
// 3、解析
// 3.1、从结果中取出名为brands的那个聚合,
// 因为是利用String类型字段来进行的term聚合,所以结果要强转为StringTerm类型
StringTerms agg = (StringTerms) aggPage.getAggregation("brands");
// 3.2、获取桶
List<StringTerms.Bucket> buckets = agg.getBuckets();
// 3.3、遍历
for (StringTerms.Bucket bucket : buckets) {
// 3.4、获取桶中的key,即品牌名称 3.5、获取桶中的文档数量
System.out.println(bucket.getKeyAsString() + ",共" + bucket.getDocCount() + "台");
// 3.6.获取子聚合结果:
InternalAvg avg = (InternalAvg) bucket.getAggregations().asMap().get("priceAvg");
System.out.println("平均售价:" + avg.getValue());
}
}
}