一、问题描述

    使用 SpringBoot 整合 Elasticsearch ,实现分组查询,本来程序运行得好好的,抽取出部分代码后,突然报错(真的是突然,并没有改变逻辑,只是抽取代码出来)。
    控制台打印的日志里的关键信息是这样的:

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed;
nested exception is Failed to execute phase [dfs], all shards failed; shardFailures {[9jF_AW-9TfqyX3gVtGpGTQ][skuinfo][0]:
RemoteTransportException[[9jF_AW-][172.17.0.6:9300][indices:data/read/search[phase/dfs]]]; nested: IllegalArgumentException[Fielddata is disabled on text fields by default. Set fielddata=true on [categoryName] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead.]; }{[9jF_AW-9TfqyX3gVtGpGTQ][skuinfo][1]: RemoteTransportException[[9jF_AW-][172.17.0.6:9300][indices:data/read/search[phase/dfs]]]; nested: IllegalArgumentException[Fielddata is disabled on text fields by default. Set fielddata=true on [categoryName] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead.]; }{[9jF_AW-9TfqyX3gVtGpGTQ][skuinfo][2]:
… …
    它是说,需要将映射到 Elasticsearch 索引库中的类的属性 categoryName 设置为 fielddate=true,以便通过反转索引,在内存中加载 fielddata 域数据。但是这可能会占用大量内存。或者使用关键字字段代替。

二、解决方法

     我先在代码中进行了设置:
在这里插入图片描述
    但是并没有在 Elasticsearch 中生效😥,还是报这个错误,还是要去 Elasticsearch 中直接设置的,使用 kibana 执行 DSL 语句:
在这里插入图片描述

我的代码是这样的:

public Map<String, Object> search(Map<String, String> searchMap) {

        // 搜索条件构建对象
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
    
        AggregatedPage<SkuInfo> page = elasticsearchTemplate.queryForPage(
                builder.build(), SkuInfo.class);

        // 添加聚合操作
        builder.addAggregation(AggregationBuilders.terms("skuCategory").field("categoryName"));

        AggregatedPage<SkuInfo> page1 = elasticsearchTemplate.queryForPage(
                builder.build(), SkuInfo.class);
                
        StringTerms stringTerms = page1.getAggregations().get("skuCategory");

        List<String> categoryList=new ArrayList<>();
        for(StringTerms.Bucket bucket:stringTerms.getBuckets()){
            String categoryName=bucket.getKeyAsString();
            categoryList.add(categoryName);
        }

        Map<String, Object> resultMap = new HashMap<String, Object>();
        resultMap.put("categoryList",categoryList);
        
        return resultMap;
    }

    主要是根据 categoryName 进行分组,把 categoryName 索引集中的数据都取出来,运行结果:
在这里插入图片描述
    夸了个大张,错误解决了,但是它居然进行了分词,这样分成一个字一个字的形式,并不是我要的效果。
     刚刚报错的地方有一句 “Alternatively use a keyword field instead.”,再看看此时的索引信息,执行 GET /skuinfo/_mapping
在这里插入图片描述
     可以看到,它是有 field 的,而且是 keyword 类型的,我们就按照报错信息提示的,用 keyword 进行替代:

builder.addAggregation(AggregationBuilders.terms("skuCategory").field("categoryName.keyword"));

运行结果:
在这里插入图片描述
既然用了 keyword,那么应该也就不需要把 fileddata 设置为 true 了,因为报错信息里也说了,这样会占用大量内存:

PUT skuinfo/_mapping/docs
{
  "properties": {
    "categoryName": { 
      "type":     "text"
    }
  }
}

     再执行程序,查询出来的是没有分词的结果。
    

    这时,又发现了一个奇怪的现象,在代码里明明是把 categoryName 设置成 keyword 类型的,但是在 Elasticsearch 里,看到它却是 text 类型,而且有 keyword 域,我们只能通过手动选择 keyword 进行查询。
     对于 keyword 类型,在存储数据时候,是不会分词的,而 text 类型,在建立索引存储数据时候,会自动分词,并生成索引(这是很智能的,但在有些字段里面是没用的,所以对于有些字段使用 text 则浪费了空间)。
    后来,我删除了 skuinfo 这个索引,重新向索引库导入了数据,发现 categoryName 域又变成了:
在这里插入图片描述
     猜测出现这种现象的愿意可能是 Elasticsearch 会对索引类型进行动态映射,所以手动把 text 类型的动态映射关闭掉:

PUT /索引名/索引类型/_mapping
{
  "dynamic":false
}

在这里插入图片描述

     这样的话,域的类型应该就是 text 不变了。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐