zoukankan      html  css  js  c++  java
  • elasticsearch 连接查询 基于es5.1.1

    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
          }
        }
      }
    }
    }
    }
     这个查询出来的结果是梅西和内马尔两个客户:
     
    {
      "took": 2,
      "timed_out": false,
      "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
      },
      "hits": {
        "total": 2,
        "max_score": 1,
        "hits": [
          {
            "_index": "index_test",
            "_type": "type_info",
            "_id": "1000",
            "_score": 1,
            "_source": {
              "userId": 1000,
              "mobile": "13301020202",
              "nick": "梅西",
              "vipType": 1,
              "vipPoints": 1200,
              "regTime": "2018-06-18 12:00:31"
            }
          },
          {
            "_index": "index_test",
            "_type": "type_info",
            "_id": "1002",
            "_score": 1,
            "_source": {
              "userId": 1002,
              "mobile": "181829282",
              "nick": "内马尔",
              "vipType": 2,
              "vipPoints": 1300,
              "regTime": "2018-09-09 12:00:00"
            }
          }
        ]
      }
    }

    但是以上内马尔其实是有一个订单数量大于60和一个订单数量小于60,但是内马尔也被查出来了,测试结果说明,应该是只要有这样的子类型匹配到了,这个父类型的doc就会出来。

     
    java api查询:查询payMethod为2的用户
     
     
    
    
    import com.alibaba.fastjson.JSON;
    import org.apache.lucene.search.join.ScoreMode;
    import org.elasticsearch.action.search.SearchResponse;
    import org.elasticsearch.client.transport.TransportClient;
    import org.elasticsearch.common.settings.Settings;
    import org.elasticsearch.common.transport.InetSocketTransportAddress;
    import org.elasticsearch.index.query.BoolQueryBuilder;
    import org.elasticsearch.index.query.QueryBuilder;
    import org.elasticsearch.index.query.QueryBuilders;
    import org.elasticsearch.search.SearchHit;
    import org.elasticsearch.transport.client.PreBuiltTransportClient;

    import java.net.InetAddress;

    public class ElasticSearchMain {

    public static void main(String[] args) throws Exception {
    TransportClient client = new PreBuiltTransportClient(Settings.EMPTY)
    .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"), 9300));
    //继续添加其他地址

    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

    boolQuery.must(QueryBuilders.rangeQuery("amount").gte(60));
    QueryBuilder hasChildQueryBuilder = QueryBuilders.hasChildQuery("type_order", boolQuery, ScoreMode.Avg);

    SearchResponse response = client.prepareSearch("index_test").setTypes("type_info").setQuery(hasChildQueryBuilder)
    .setSize(10000).execute().actionGet();

    for (SearchHit hit : response.getHits().getHits()) {
    System.out.println(JSON.toJSONString(hit.getSource()));
    }
    //on shutdown
    client.close();
    }

    }
     
     
     
     
    • 通过父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:在大量文档,并且会经常发生改变的情况下使用。 
      比如:用户的浏览记录,浏览记录会很大,并且会频繁更新

  • 相关阅读:
    同步IO,异步IO,阻塞,非阻塞的定义与区别
    RocketMQ之NameServer学习笔记
    RocketMQ消息发送的队列选择与容错策略
    RocketMQ详解
    JVM(HotSpot) 7种垃圾收集器的特点及使用场景
    dubbo SPI设计
    dubbo集群容错之LoadBalance
    dubbo服务引用与集群容错
    dubbo服务暴露过程
    内存溢出排查基本步骤
  • 原文地址:https://www.cnblogs.com/chenmz1995/p/11261655.html
Copyright © 2011-2022 走看看