springboot 的 elasticsearch 版本:
7.15.2
前情提要:
1.首先要理解 elasticsearch 对于【数据类型】很严格,如果字段类型不规范,在 检索/排序/聚合 时候类型不正确就会出现报错或者查不到数据的问题。所以在一般String类型插入结构如下:
这样的结构,不仅可以支持分词查询,也可以进行精准匹配、对该字段排序、对该字段进行聚合。一箭双雕,通常String都建议用这样的结构。
2.如果是数值类型(Integer、Long、Double)更适合进行排序、聚合、精准匹配。如果确定某类的某个属性是【数值】,最好不要使用String接收(并非完全不可使用),用数值类型接收更合理。如下:
3.如果某个类的属性是Object类型,在进行检索、聚合等操作时,es的type类型不必做其它操作,如下:
像 tradeType、map 这两个属性属于Object类型,在es中不必特殊指定类型,如下:
4.es中的【nested】类型,特定用在某属性是数组类型,而且该属性在es结构中必须要加上【nested】类型,否在在检索、聚合时候就会出现报错或查不到数据问题,以下:
es中结构如下:
subjectList 便是数组类型,并且泛型是包装类型,在es中都需要指定该字段是【nested】。
5.非常注意:如果某属性的泛型是基本类型(比如:List< String >、List< Integer > )那es的类型可不是【nested】,而是keyword或Integer 这种。如下:
es中的结构:
当然会有朋友问,es中貌似也支持array类型,但是我用的版本在使用 array 创建索引时会报错:No handler for type [array]。所以我这边会用keyword类型。
具体支持类型,可参考:Elasticsearch数据类型及其属性
准备索引:
通过es控制台或者postman都可以进行索引操作,类型mysql可视化工具对mysql数据库表结构操作类型,只不过,es对于创建后的索引的字段类型不可改变,如果一定要变更字段类型,就需要删除索引重新创建,这就是为什么对于要插入的数据类型严格控制的原因。
1.创建索引:createIndex():
当你制作好一个java类,并且把属性的类型都规范好,这时候可直接通过该方法创建索引,如果该类中有数组类型的属性:
1.如果数组泛型是基本类型 则 传入 arrayField 参数,在es中type=keyword。
2.如果数组泛型是包装类型 则需要指定 nestedField 参数的属性名,否则在插入数据后会发现,该数组类型的es中的type不是 nested类型。
2.删除索引:deleteIndex(String indexName):
不解释了,没啥好说的。
3.是否存在指定索引:existIndex(String indexName):
不解释了,没啥好说的。
完整示例:
工具类用的json格式化工具:
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
java实体类:
@Data
public class UserIndexReqDTO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* [描述] 用户id
*/
private Long id;
private String userName;
private Integer sex;
private Integer age;
/** 用户多个手机号码 */
private List<String> phoneList;
/** 用户科目列表信息 */
private List<SubjectDTO> subjectList;
}
@Data
class SubjectDTO {
/** 科目id */
private Integer id;
/** 科目名称 */
private String subjectName;
/** 科目分值 */
private Integer subjectScore;
}
1.创建索引示例:
@Resource
private EsClientUtil2 esClientUtil2;
private String indexName = "user_index";
@Override
public BaseResponse<Object> addMore( @RequestBody List<UserIndexReqDTO> list){
// 如果指定索引不存在,则按要求创建
if(!esClientUtil2.existIndex(indexName)){
esClientUtil2.createIndex(indexName,Arrays.asList("phoneList"),Arrays.asList("subjectList"));
}
Assert.notEmpty(list,"list is empty");
List<JSONObject> collect = list.stream()
.map(item -> JSONObject.parseObject(JSONObject.toJSONString(item), JSONObject.class))
.collect(Collectors.toList());
boolean addDoc = esClientUtil2.multiAddDoc(indexName, collect);
return BaseResponse.ofSuccess(addDoc);
}
2.准备测试数据:
[
{
"id": 48,
"userName": "小明",
"sex": 1,
"age": 17,
"phoneList": [
"15800000001",
"15800000002"
],
"subjectList": [
{
"id": 62,
"subjectName": "语文",
"subjectScore": 68
}
]
},
{
"id": 49,
"userName": "史泰龙小红",
"sex": 0,
"age": 10,
"phoneList": [
"18732000000",
"18732000001"
],
"subjectList": [
{
"id": 62,
"subjectName": "语文",
"subjectScore": 68
},
{
"id": 63,
"subjectName": "数学",
"subjectScore": 30
}
]
},
{
"id": 50,
"userName": "小黑",
"sex": 1,
"age": 14,
"phoneList": [
"13200000000",
"13200000001"
],
"subjectList": [
{
"id": 62,
"subjectName": "语文",
"subjectScore": 68
},
{
"id": 64,
"subjectName": "英语",
"subjectScore": 80
}
]
}
]
3.根据条件查询:
public BaseResponse<Object> search() {
int pageNum = 1;
int pageSize = 100;
Map<String,List<String>> likeMap = new HashMap<>(3);
Map<String,Object> signMap = new HashMap<>(3);
Map<String,List<String>> multipleMap = new HashMap<>(3);
Map<String,List<Long>> rangeMap = new HashMap<>(3);
List<String> idList = new ArrayList<>();
// 根据值模糊查询 匹配多字段
//likeMap.put("小明",Arrays.asList("name","nickName"));
// 根据值精准匹配 匹配单字段
//signMap.put("sex","1");
//signMap.put("phoneList",Arrays.asList("15800000001","18732000001"));
// 根据值精准匹配 匹配多字段
//multipleMap.put("小红",Arrays.asList("name","nickName"));
// 区间匹配 18<= age <= 40
//rangeMap.put("age",Arrays.asList(18L,40L));
// 根据esId进行检索
//idList = Arrays.asList("1","2","3");
//嵌套查询
Map<String,String> key = new HashMap<>(1);
Map<String,List<String>> value = new HashMap<>(1);
//key.put("subjectList", "subjectList.id");
//value.put("subjectList.id",Arrays.asList("63"));
List<NestedQueryBuilder> nestedListQuery = esClientUtil2.getNestedListQuery(key, value);
BoolQueryBuilder builder = esClientUtil2.getQueryBuilderAnd(likeMap, signMap, multipleMap, rangeMap, nestedListQuery, idList);
PageResult<String> search = esClientUtil2.search(EsConstant.ESALES_USER_COLLECT_INDEX,
builder, null,
null, null, null, pageNum, pageSize);
List<UserIndexReqDTO> result = new ArrayList<>();
for (String s : search.getList()) {
result.add(JSONObject.parseObject(s, UserIndexReqDTO.class));
}
return BaseResponse.ofSuccess(result);
}
4.根据条件聚合:
public BaseResponse<Object> group() {
Map<String,List<String>> likeMap = new HashMap<>(3);
Map<String,Object> signMap = new HashMap<>(3);
Map<String,List<String>> multipleMap = new HashMap<>(3);
Map<String,List<Long>> rangeMap = new HashMap<>(3);
List<String> idList = new ArrayList<>();
// 根据值模糊查询 匹配多字段
//likeMap.put("小明",Arrays.asList("name","nickName"));
// 根据值精准匹配 匹配单字段
//signMap.put("sex","1");
//signMap.put("phoneList",Arrays.asList("15800000001","18732000001"));
// 根据值精准匹配 匹配多字段
//multipleMap.put("小红",Arrays.asList("name","nickName"));
// 区间匹配 18<= age <= 40
//rangeMap.put("age",Arrays.asList(18L,40L));
// 根据esId进行检索
//idList = Arrays.asList("1","2","3");
//嵌套查询
Map<String,String> key = new HashMap<>(1);
Map<String,List<String>> value = new HashMap<>(1);
//key.put("subjectList", "subjectList.id");
//value.put("subjectList.id",Arrays.asList("63"));
List<NestedQueryBuilder> nestedListQuery = esClientUtil2.getNestedListQuery(key, value);
BoolQueryBuilder builder = esClientUtil2.getQueryBuilderAnd(likeMap, signMap, multipleMap, rangeMap, nestedListQuery, idList);
// 根据字段进行聚合
List<Map<String, Object>> sex = esClientUtil2.group(EsConstant.ESALES_USER_COLLECT_INDEX,
"sex",null, builder,
null, null, null);
for (Map<String, Object> map : sex) {
System.out.println(map);
}
// 根据 嵌套 字段进行聚合
List<Map<String, Object>> subjectIdList = esClientUtil2.group(EsConstant.ESALES_USER_COLLECT_INDEX,
null,Arrays.asList("subjectList","subjectList.id"), builder,
null, null, null);
for (Map<String, Object> map : subjectIdList) {
System.out.println(map);
}
return BaseResponse.ofSuccess(sex);
}
4-1.after翻页:
public BaseResponse<Object> search() {
Map<String,List<String>> likeMap = new HashMap<>(3);
Map<String,Object> signMap = new HashMap<>(3);
Map<String,List<String>> multipleMap = new HashMap<>(3);
Map<String,List<Long>> rangeMap = new HashMap<>(3);
List<String> idList = new ArrayList<>();
// 根据值模糊查询 匹配多字段
//likeMap.put("小明",Arrays.asList("name","nickName"));
// 根据值精准匹配 匹配单字段
//signMap.put("sex","1");
//signMap.put("phoneLis