ElasticSerch 的连接查询有两种方式实现
- nested
- parent和child关联查询
nested
- 存储结构
nested的方式和其他字段一样,在同一个type里面存储,以数组的方式存储在
type里,格式如下:
PUT index_test/type_info/1000
{
"userId": 1000,
"mobile": "13301020202",
"nick": "梅西",
"vipType": 1,
"vipPoints": 1200,
"regTime": "2018-06-18 12:00:31",
"order": [
{
"status": 1,
"payMethod": 2,
"amount": 100,
"productCount": 3
},
{
"status": 2,
"payMethod": 2,
"amount": 230,
"productCount": 1
}
]
}
order
则为 nested
API查询方式
直接用.
连接对象的属性,如要要查找订单中状态=2的用户,直接使用order.status
GET index_test/type_info/_search
{
"query": {
"term": {
"order.status": 2
}
}
}
parent / child 关联的方式
-
存储结构
parent / child 的存储结果跟nested不一样,是存储在不同的type里,通过parent来关联父子type关系,创建有父子关系的两个类型的时候必须在一个请求中创建
PUT index_test
{
"mappings": {
"type_info": {
"properties": {
"userId": {
"type": "integer"
},
"mobile": {
"type": "keyword"
},
"nick": {
"type": "keyword"
},
"vipType": {
"type": "integer"
},
"vipPoints": {
"type": "integer"
},
"regTime": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
}
}
},
"type_order": {
"_parent": {
"type": "type_info"
},
"properties": {
"amount": {
"type": "scaled_float",
"scaling_factor": 100
},
"payMethod": {
"type": "integer"
},
"status": {
"type": "integer"
},
"productCount": {
"type": "integer"
}
}
}
}
}
通过 _parent
来指定父type
-
造点数据
添加几条用户数据,和普通的type一样,没有任何区别
PUT index_test/type_info/1000
{
"userId": 1000,
"mobile": "13301020202",
"nick": "梅西",
"vipType": 1,
"vipPoints": 1200,
"regTime": "2018-06-18 12:00:31"
}
PUT index_test/type_info/1001
{
"userId": 1001,
"mobile": "151232223",
"nick": "C罗",
"vipType": 1,
"vipPoints": 300,
"regTime": "2018-05-18 12:00:00"
}
PUT index_test/type_info/1002
{
"userId": 1002,
"mobile": "181829282",
"nick": "内马尔",
"vipType": 2,
"vipPoints": 1300,
"regTime": "2018-09-09 12:00:00"
}
添加几条订单数据,通过parent来指定type_info, 以下parent=xxx, xxx指的是父类型文档的_id编号。
PUT index_test/type_order/100?parent=1000
{
"userId": 1000,
"amount": 300,
"payMethod": 2,
"status": 3,
"productCount": 2
}
PUT index_test/type_order/101?parent=1000
{
"userId": 1000,
"amount": 250,
"payMethod": 1,
"status": 2,
"productCount": 1
}
PUT index_test/type_order/102?parent=1001
{
"userId": 1001,
"amount": 56,
"payMethod": 1,
"status": 2,
"productCount": 1
}
PUT index_test/type_order/103?parent=1002
{
"userId": 1002,
"amount": 78,
"payMethod": 2,
"status": 1,
"productCount": 2
}
PUT index_test/type_order/104?parent=1002
{
"userId": 1002,
"amount": 50,
"payMethod": 2,
"status": 1,
"productCount": 2
}
如果用java代码在新增doc的时候设置父类型的方法如下:
import com.alibaba.fastjson.JSON; import org.apache.lucene.search.join.ScoreMode; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.InetSocketTransportAddress; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.search.SearchHit; import org.elasticsearch.transport.client.PreBuiltTransportClient; import java.net.InetAddress; import java.util.HashMap; import java.util.Map; public class ElasticSearchMain { public static void main(String[] args) throws Exception { TransportClient client = new PreBuiltTransportClient(Settings.EMPTY) .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"), 9300)); Map<String, Object> objectMap = new HashMap<>(); objectMap.put("userId", 1001); objectMap.put("amount", 30); objectMap.put("payMethod", 2); objectMap.put("status", 1); objectMap.put("productCount", 2); Map<String, Object> objectMap1 = addMapObjectDocToIndex(client, "index_test", "type_order", "105", objectMap); System.out.println(JSON.toJSONString(objectMap1)); client.close(); } public static Map<String, Object> addMapObjectDocToIndex(TransportClient client, String index, String type, String docId, Map<String, Object> params) { Map<String, Object> result = new HashMap<String, Object>(); boolean flag = false; XContentBuilder source = null; try { source = createMapJson(params); // 存json入索引中 IndexResponse response = null; if (docId == null) { // 使用默认的id response = client.prepareIndex(index, type).setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).setSource(source).get(); } else { response = client.prepareIndex(index, type, docId).setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).setSource(source).setParent("1001").get(); } // 插入结果获取 RestStatus status = response.status(); if (status.getStatus() == 200 || status.getStatus() == 201) { flag = true; } } catch (Exception e) { e.printStackTrace(); } result.put("success", flag); result.put("msg", flag ? "新增成功" : "新增失败"); return result; } /** * 将Map转换成builder * * @param mapParam * @return * @throws Exception */ private static XContentBuilder createMapJson(Map<String, ?> params) throws Exception { XContentBuilder source = XContentFactory.jsonBuilder().startObject(); for (Map.Entry<String, ?> entry : params.entrySet()) { if (entry.getValue() != null && entry.getValue().toString().length() > 0) { source.field(entry.getKey(), entry.getValue()); } } source.endObject(); return source; } }
-
API查询方式
- 通过子type查询父type,返回父type信息
查询下单金额大于60的用户,通过has_child
查询,返回用户信息
GET index_test/type_info/_search
{
"query": {
"has_child": {
"type": "type_order",
"query": {
"range": {
"amount": {
"gte": 60
}
}
}
}
}
}
- 通过父type查子type,返回子type信息
查询vip等级为1的用户下的订单,通过has_parent
查询,返回订单信息
GET index_test/type_order/_search
{
"query": {
"has_parent": {
"parent_type": "type_info",
"query": {
"term": {
"vipType": {
"value": 1
}
}
}
}
}
}
nested 和 parent-child的区别以及使用场景
-
主要区别:
由于存储结构的不同,nested和parent-child的方式有不同的应用场景
nested 所有实体存储在同一个文档,parent-child模式,子type和父type存储在不同的文档里。
所以查询效率上nested要高于parent-child,但是更新的时候nested模式下,es会删除整个文档再创建,而parent-child只会删除你更新的文档在重新创建,不影响其他文档。所以更新效率上parent-child要高于nested。 -
使用场景:
nested:在少量子文档,并且不会经常改变的情况下使用。
比如:订单里面的产品,一个订单不可能会有成千上万个不同的产品,一般不会很多,并且一旦下单后,下单的产品是不可更新的。
parent-child:在大量文档,并且会经常发生改变的情况下使用。
比如:用户的浏览记录,浏览记录会很大,并且会频繁更新