以下代码相关示例,使用elasticsearch
7.6.2
+ spring-boot2.3.2-RELEASE
spring-data-elasticsearch提供的ElasticsearchRepository很好用,能有效的对数据进行简单CRUD的同时,还对@Document修饰的model类字段在初始化时进行动态生成。
例如如下被@Document生成的实体类,在使用@Field声明了相关字段,并且创建了对应ElasticsearchRepository
的实现类后,启动项目时,实体类对应的es索引就会被自动创建出来:
@Document(indexName = "demo-product", refreshInterval = "5s")
@Getter
@Setter
@NoArgsConstructor
@ToString
public class Product {
@Id
private Long id;
//商品名
@MultiField(mainField = @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart"), otherFields = @InnerField(type = FieldType.Keyword, suffix = "raw", ignoreAbove = 256))
private String productName;
//商品描述
@Field(type = FieldType.Text, analyzer = "ik_smart")
private String description;
//数据更新时间
@Field(type = FieldType.Date_Nanos, index = false)
private Long utime;
//品牌名称
@Field(type = FieldType.Keyword)
private String brandName;
//分类名称
@Field(type = FieldType.Keyword)
private String categoryName;
}
public interface ProductRepository extends ElasticsearchRepository {
}
使用GET /demo-product/_mapping
命令可查看到自动生成的索引字段结构:
{
"demo-product" : {
"mappings" : {
"properties" : {
"brandName" : {
"type" : "keyword"
},
"categoryName" : {
"type" : "keyword"
},
"description" : {
"type" : "text",
"analyzer" : "ik_smart"
},
"productName" : {
"type" : "text",
"fields" : {
"raw" : {
"type" : "keyword",
"ignore_above" : 256
}
},
"analyzer" : "ik_max_word",
"search_analyzer" : "ik_smart"
},
"utime" : {
"type" : "date_nanos",
"index" : false
}
}
}
}
}
然而如何让它能自动生成一些实体类字段额外的属性呢?
比如Product
这个实体类对应的索引虽然创建完成了,但是后续使用ProductRepository
的save方法往索引中推数据时,会发现索引结构发生了变化:
@Test
void save() {
Product product = new Product();
product.setProductName("test product1");
product.setBrandName("test brand1");
product.setCategoryName("test category1");
product.setId(1l);
product.setDescription("test product description1");
product.setUtime(Instant.now().getEpochSecond());
productRepository.save(product);
}
再次使用GET /demo-product/_mapping
命令查看索引字段结构:
{
"demo-product" : {
"mappings" : {
"properties" : {
"_class" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"brandName" : {
"type" : "keyword"
},
"categoryName" : {
"type" : "keyword"
},
"description" : {
"type" : "text",
"analyzer" : "ik_smart"
},
"id" : {
"type" : "long"
},
"productName" : {
"type" : "text",
"fields" : {
"raw" : {
"type" : "keyword",
"ignore_above" : 256
}
},
"analyzer" : "ik_max_word",
"search_analyzer" : "ik_smart"
},
"utime" : {
"type" : "date_nanos",
"index" : false
}
}
}
}
}
多出来的_class
字段从哪里来的呢?
其实是因为Product实例被保存到es时,会被序列化处理为json结构字符串(jackson),而类信息会被放置在固定字段_class上,用以在取回数据时反序列化为对应类实例,而由于我们没有在es中定义过_class
这个字段,因此es在接收到数据后会按照默认方案,生成string字段对应的text+keyword结构。
如果想自己在实体类中定义好这个字段,不让es自动生成,要怎么处理?
我的方案是在实体类中定义一个不能被操作的占位
字段:
//在Product类中追加如下内容:
//预配置索引的_class字段
@Field(name = "_class", type = FieldType.Keyword, index = false)
//配置Ignore,使jackson在序列化示例时跳过该字段
@JsonIgnore
//下面配置使得lombok不会为该字段生成getter、setter方法,也就不会有数据操作
@Getter(value = AccessLevel.NONE)
@Setter(value = AccessLevel.NONE)
private String clazz;
然后清理掉es索引,重启应用后,会发现在新的索引结构中已经存在了_class字段:
{
"demo-product" : {
"mappings" : {
"properties" : {
"_class" : {
"type" : "keyword",
"index" : false
},
"brandName" : {
"type" : "keyword"
},
"categoryName" : {
"type" : "keyword"
},
"description" : {
"type" : "text",
"analyzer" : "ik_smart"
},
"id" : {
"type" : "long"
},
"productName" : {
"type" : "text",
"fields" : {
"raw" : {
"type" : "keyword",
"ignore_above" : 256
}
},
"analyzer" : "ik_max_word",
"search_analyzer" : "ik_smart"
},
"utime" : {
"type" : "date_nanos",
"index" : false
}
}
}
}
}
不过说实话,这么做的意义不是很大 ,我只是强迫症犯了罢了...
如果朋友们有什么更好的方法,欢迎留言告知~