在《Elasticsearch 父子关系维护和检索案例分享》一文中介绍了Elasticsearch 父子关系维护和检索的基本功能,本文接着上篇文档,分享同时返回父子数据Elasticsearch 关联查询案例。
本案例针对elasticsearch 5.x,elasticsearch 6.x版本通过join type来实现父子关系检索,参考文档后面demo。
本文涉及到的技术点:
- inner_hits的使用,通过inner_hits来同时返回父表数据和子表数据
- 父子双向数据检索及结果绑定和遍历
1.准备工作
参考文档《高性能elasticsearch ORM开发库使用介绍》导入和配置es客户端
2.定义带inner_hits的dsl检索语句
在dsl配置文件-esmapper/indexparentchild.xml中增加两个dsl检索语句:
hasChildSearchReturnParent2ndChildren 演示在按照雇员信息检索公司数据时,同时返回符合条件的公司下面的员工信息
hasParentSearchByCountryReturnParent2ndChildren 演示在按照公司信息检索雇员数据时,同时返回符合条件的雇员对应的公司信息
<!--以雇员姓名为条件检索公司信息并返回公司雇员信息-->
<property name="hasChildSearchReturnParent2ndChildren">
<![CDATA[
{
"query": {
"has_child": {
"type": "employee",
"score_mode": "max",
"query": {
"match": {
"name": #[name] ##雇员名称参数
}
},
"inner_hits": {} ## 这是同时返回父子数据的关键所在
}
}
}
]]>
</property>
<!--根据公司所在的国家信息检索员工信息,同时返回员工所属的公司信息-->
<property name="hasParentSearchByCountryReturnParent2ndChildren">
<![CDATA[
{
"query": {
"has_parent": {
"type": "company",
"query": {
"match": {
"country": #[country] ##国家代码参数
}
},
"inner_hits": {} ## 这是同时返回父子数据的关键所在
}
}
}
]]>
</property>
<!--查找顾客信息,演示多子文档父子数据查询功能-->
<property name="hasParentSearchByCountryReturnParent2ndMultiChildren">
<![CDATA[
{
"query": {
"bool": {
"should": [
{
"match_all":{}
}
]
,"must": [
{
"has_child": {
"score_mode": "none",
"type": "diagnosis"
,"query": {
"bool": {
"must": [
{
"term": {
"icd10_code": {
"value": "J00"
}
}
}
]
}
},"inner_hits":{}
}
}
]
,"should": [
{
"has_child": {
"score_mode": "none",
"type": "medical"
,"query": {
"match_all": {}
},"inner_hits":{}
}
}
]
,"should": [
{
"has_child": {
"type": "exam",
"query": {
"match_all": {}
},"inner_hits":{}
}
}
]
}
}
}
]]>
</property>
3.定义检索操作方法
在文件 ParentChildTest.java中增加以下方法
/**
* 检索公司信息,并返回公司对应的雇员信息(符合检索条件的雇员信息)
*/
public void hasChildSearchReturnParent2ndChildren(){
ClientInterface clientUtil = ElasticSearchHelper.getConfigRestClientUtil("esmapper/indexparentchild.xml");
Map<String,Object> params = new HashMap<String,Object>();
params.put("name","Alice Smith");
try {
ESInnerHitSerialThreadLocal.setESInnerTypeReferences(Employee.class);//指定inner查询结果对于雇员类型
ESDatas<Company> escompanys = clientUtil.searchList("company/company/_search","hasChildSearchReturnParent2ndChildren",params,Company.class);
long totalSize = escompanys.getTotalSize();
List<Company> companyList = escompanys.getDatas();//获取符合条件的公司
//查看公司下面的雇员信息(符合检索条件的雇员信息)
for (int i = 0; i < companyList.size(); i++) {
Company company = companyList.get(i);
List<Employee> employees = ResultUtil.getInnerHits(company.getInnerHits(), "employee");
System.out.println(employees.size());
}
}
finally{
ESInnerHitSerialThreadLocal.clean();//清空inner查询结果对于雇员类型
}
}
/**
* 通过公司所在国家检索雇员信息,并返回雇员对应的公司信息
*/
public void hasParentSearchByCountryReturnParent2ndChildren(){
ClientInterface clientUtil = ElasticSearchHelper.getConfigRestClientUtil("esmapper/indexparentchild.xml");
Map<String,Object> params = new HashMap<String,Object>();
params.put("country","UK");
try {
ESInnerHitSerialThreadLocal.setESInnerTypeReferences(Company.class);//指定inner查询结果对于公司类型
ESDatas<Employee> escompanys = clientUtil.searchList("company/employee/_search","hasParentSearchByCountryReturnParent2ndChildren",params,Employee.class);
List<Employee> employeeList = escompanys.getDatas();//获取符合条件的雇员数据
long totalSize = escompanys.getTotalSize();
//查看每个雇员对应的公司信息
for(int i = 0; i < employeeList.size(); i ++) {
Employee employee = employeeList.get(i);
List<Company> companies = ResultUtil.getInnerHits(employee.getInnerHits(), "company");
System.out.println(companies.size());
}
}
finally{
ESInnerHitSerialThreadLocal.clean();//清空inner查询结果对于公司类型
}
}
/**
* 查找顾客信息,演示多子文档父子数据查询功能
*/
public void hasParentSearchByCountryReturnParent2ndMultiChildren(){
ClientInterface clientUtil = ElasticSearchHelper.getConfigRestClientUtil("esmapper/indexparentchild.xml");
Map<String,Object> params = new HashMap<String,Object>();//没有检索条件,构造一个空的参数对象
// params.put("name","Alice Smith");
try {
//设置子文档的类型和对象映射关系
ESInnerHitSerialThreadLocal.setESInnerTypeReferences("exam",Exam.class);//指定inner查询结果对于exam类型和对应的对象类型Exam
ESInnerHitSerialThreadLocal.setESInnerTypeReferences("diagnosis",Diagnosis.class);//指定inner查询结果对于diagnosis类型和对应的对象类型Diagnosis
ESInnerHitSerialThreadLocal.setESInnerTypeReferences("medical",Medical.class);//指定inner查询结果对于medical类型和对应的对象类型Medical
ESDatas<ClientInfo> escompanys = clientUtil.searchList("client_info/basis/_search",
"hasParentSearchByCountryReturnParent2ndMultiChildren",params,ClientInfo.class);
long totalSize = escompanys.getTotalSize();
List<ClientInfo> clientInfos = escompanys.getDatas();//获取符合条件的公司
//查看公司下面的雇员信息(符合检索条件的雇员信息)
for (int i = 0; i < clientInfos.size(); i++) {
ClientInfo clientInfo = clientInfos.get(i);
List<Exam> exams = ResultUtil.getInnerHits(clientInfo.getInnerHits(), "exam");
System.out.println(exams.size());
List<Diagnosis> diagnosiss = ResultUtil.getInnerHits(clientInfo.getInnerHits(), "diagnosis");
System.out.println(diagnosiss.size());
List<Medical> medicals = ResultUtil.getInnerHits(clientInfo.getInnerHits(), "medical");
System.out.println(medicals.size());
}
}
finally{
ESInnerHitSerialThreadLocal.clean();//清空inner查询结果对于雇员类型
}
}
多个子文档:多个子文档必须要设置类型和对应的对象的映射关系,单个子文档不需要
/**
* 查找顾客信息,演示多子文档父子数据查询功能
*/
public void hasParentSearchByCountryReturnParent2ndMultiChildren(){
ClientInterface clientUtil = ElasticSearchHelper.getConfigRestClientUtil("esmapper/indexparentchild.xml");
Map<String,Object> params = new HashMap<String,Object>();//没有检索条件,构造一个空的参数对象
// params.put("name","Alice Smith");
try {
//设置子文档的类型和对象映射关系,多个子文档必须要设置类型和对应的对象的映射关系
ESInnerHitSerialThreadLocal.setESInnerTypeReferences("exam",Exam.class);//指定inner查询结果对于exam类型和对应的对象类型Exam
ESInnerHitSerialThreadLocal.setESInnerTypeReferences("diagnosis",Diagnosis.class);//指定inner查询结果对于diagnosis类型和对应的对象类型Diagnosis
ESInnerHitSerialThreadLocal.setESInnerTypeReferences("medical",Medical.class);//指定inner查询结果对于medical类型和对应的对象类型Medical
ESDatas<ClientInfo> escompanys = clientUtil.searchList("client_info/_search",
"hasParentSearchByCountryReturnParent2ndMultiChildren",params,ClientInfo.class);
long totalSize = escompanys.getTotalSize();
List<ClientInfo> clientInfos = escompanys.getDatas();//获取符合条件的公司
//查看公司下面的雇员信息(符合检索条件的雇员信息)
for (int i = 0; i < clientInfos.size(); i++) {
ClientInfo clientInfo = clientInfos.get(i);
List<Exam> exams = ResultUtil.getInnerHits(clientInfo.getInnerHits(), "exam");
System.out.println(exams.size());
List<Diagnosis> diagnosiss = ResultUtil.getInnerHits(clientInfo.getInnerHits(), "diagnosis");
System.out.println(diagnosiss.size());
List<Medical> medicals = ResultUtil.getInnerHits(clientInfo.getInnerHits(), "medical");
System.out.println(medicals.size());
}
}
finally{
ESInnerHitSerialThreadLocal.clean();//清空inner查询结果对于雇员类型
}
}
说明:
1) 通过ESInnerHitSerialThreadLocal指定了inner_hits中需要返回的数据对象类型
ESInnerHitSerialThreadLocal.setESInnerTypeReferences(Employee.class);//指定inner查询结果对于雇员类型,单个子文档场景只需要设置映射的对象类即可
ESInnerHitSerialThreadLocal.setESInnerTypeReferences(Company.class);//指定inner查询结果对于公司类型,单个子文档场景只需要设置映射的对象类即可
//设置子文档的类型和对象映射关系,多个子文档必须要设置类型和对应的对象的映射关系 ESInnerHitSerialThreadLocal.setESInnerTypeReferences("exam",Exam.class);//指定inner查询结果对于exam类型和对应的对象类型Exam
使用后需要清除:
ESInnerHitSerialThreadLocal.clean();//清空inner查询结果对于雇员类型
2) employee.getInnerHits()和company.getInnerHits()方法都是从ESBaseData继承的方法,bboss会自动将inner_hits检索到的父子关联数据设置到ESBaseData对象中,可以通过getInnerHits()方法获取到对应的数据;
3) ResultUtil.getInnerHits工具方法用于获取父/子关联检索对应的子/父的结果,方法的第二个参数对应inner_hits检索的类型和名称:
List<Employee> employees = ResultUtil.getInnerHits(company.getInnerHits(), "employee");
List<Company> companies = ResultUtil.getInnerHits(employee.getInnerHits(), "company");
4.执行测试方法
通过junit,执行新的测试方法
@Test
public void testFromJson(){
createIndice();
importFromJsonData();
hasChildSearchByBirthday();
this.hasChildSearchByName();
this.hasChildSearchByMinChild();
this.hasParentSearchByCountry();
this.hasChildSearchReturnParent2ndChildren();//本文对应的方法
this.hasParentSearchByCountryReturnParent2ndChildren();//本文对应的方法
}
5.参考文档
完整的demo源码:
5.x父子查询示例:
https://gitee.com/bbossgroups/eshelloword-booter/tree/master/src/test/java/org/bboss/elasticsearchtest/parentchild 6.x父子查询示例:
https://gitee.com/bbossgroups/eshelloword-booter/tree/master/src/test/java/org/bboss/elasticsearchtest/jointype
测试用例对应的工程
https://gitee.com/bbossgroups/eshelloword-booter