<?php
/**
  * @method 全文搜索
  * @author soul
  * @copyright 
  */
class B2cElastic extends B2cBase
{
	public $index = 'b2citem';
	public $client = null;

	public function __construct()
	{
		parent::__construct();
		$this->client = Elasticsearch\ClientBuilder::create()->setHosts(mvc::$cfg['ES'])->build();
	}

	/**
	  * @method 创建索引
	  * @author soul 
	  * @return
	  */
	public function createIndex()
	{
		try{
			$params = [
				'index' => $this->index,
				'body' => [
					'settings' => [
						'number_of_shards' => 1,
						'number_of_replicas' => 0
					],
					'mappings' => [
						'_source' => [
							'enabled' => true
						],
						'properties' => [
							'pro_id' => [
								'type' => 'integer',
							],
							'shop_id' => [
								'type' => 'integer',
							],
							'pro_word' => [
								'type' => 'text'
							]
						]
					]
				]
			];
			$response = $this->client->indices()->create($params);
		}catch (Exception $e) {
			return LibFc::ReturnData(false, 'ES检索引擎不可用：'.$e->getMessage());
		}
		return LibFc::ReturnData(true, true); 
	}


	/**
	  * @method 删除索引
	  * @author soul 
	  * @return
	  */
	public function delIndex()
	{
		try{
			$this->client->indices()->delete(['index'=>$this->index]);
		}catch (Exception $e) {
			return LibFc::ReturnData(false, 'ES检索引擎不可用：'.$e->getMessage());
		}
		return LibFc::ReturnData(true, true);
	}


	/**
	  * @method 重置配件全文搜索  $shopId && $proIdArr 同时为空时，重置所有
	  * @param  $shopId int 指定店铺
	  * @param  $proIdArr array 指定配件ID
	  * @author soul 
	  * @return
	  */
	public function updateItemWord($shopId = 0, $proIdArr = [])
	{
		set_time_limit(0);
		$proArr = [];
		$now = time();
		$where = ' and (
			pro_shop_id IN (SELECT aps_shop_id FROM sh_shop_app WHERE aps_etime > '.$now.') 
			OR pro_shop_id IN (SELECT ssu_shop_id FROM sh_super WHERE ssu_style = "B2C" AND ssu_etime >'.$now.') 
			OR pro_shop_id IN (SELECT wr_uid FROM `wx_robot` WHERE wr_etime > '.$now.')
		)';
		if(!empty($shopId)) $where .= sprintf(" and pro_shop_id =%d", $shopId);
		if(!empty($proIdArr)){
			$proIdArr = array_unique(array_map(function($item) {return (int) $item;}, $proIdArr));
			$where .= sprintf(" and pro_id in (%s) ",implode(',', $proIdArr));
		}
		$per = 5000;
		$lastId = 0;
		for($page = 1; $page < 1000; $page++){
			$table = 'bc_product
			join bc_product_info on pri_pro_id=pro_id
			join bc_part on pro_part_id=part_id
			left join bc_part_small on pas_id = pro_pas_id';
			$res = $this->get($table, ['pro_id','pro_shop_id','pro_name','pri_code', 'pri_keyword', 'part_name', 'part_keyword','pas_name'], $where.sprintf(" and pro_id > %d order by pro_id limit %d", $lastId, $per));
			$count = count($res['data']);
			if($count > 0)
			{
				$proArr = array_column($res['data'], null, 'pro_id');
				// 获取店铺分类
				$sortArr = [];
				$table = 'bc_product_sort 
				join sh_sort on sort_id = prs_sort_id';
				$res = 	$this->get($table,['prs_pro_id,sort_name,sort_keyword'],sprintf(" AND prs_pro_id IN (%s)", implode(',', array_keys($proArr))));
				foreach($res['data'] as $v){
					$sortArr[$v['prs_pro_id']][] = $v['sort_name'];
					if(!empty($v['sort_keyword'])) $sortArr[$v['prs_pro_id']][] = $v['sort_keyword'];
				}
				
				$add = [];
				foreach($proArr as $pid=>$v){
					if($pid > $lastId) $lastId = $pid;
					$v['pro_word'] = $v['pro_name'].','.$v['part_name'].','.$v['pri_keyword'];
					if(!empty($v['pas_name'])) $v['pro_word'] .= ','.$v['pas_name'];
					//if(!empty($v['part_keyword'])) $v['pro_word'] .= ','.$v['part_keyword'];
					if($sortArr[$pid]) $v['pro_word'] .= ','.implode(',',$sortArr[$pid]);
					$v['pro_word'] = LibFc::fromatKeyword($v['pro_word']);
					$add[] =  ['index'=>['_index' =>$this->index, '_id'=>$v['pro_id']]];
					$add[] = [
						'pro_id' => $v['pro_id'],  
						'shop_id' => $v['pro_shop_id'],
						'pro_word' => $v['pro_word'],
					];
				}
				if($proIdArr || $shopId) $this->delItemWord(array_keys($proArr));
				$this->client->bulk(['body'=>$add]);
			}
			if($count < $per) break;
		}
		return LibFc::ReturnData(true, true); 
	}

	/**
	  * @method 删除配件索引
	  * @param  $proIdArr array 指定配件ID
	  * @author soul 
	  * @return
	  */
	public function delItemWord($proIdArr)
	{
		if(!empty($proIdArr)){
			$params = [
				'index' => $this->index,
				'body'  => [
					'query' => [  
						"bool"=>[ 
						  "must"=>[
							"terms"=>["pro_id"=>$proIdArr]
						]
					  ]
					]
				]
			];
			try{
				$this->client->deleteByQuery($params);
			}catch (Exception $e) {
				return LibFc::ReturnData(false, 'ES检索引擎不可用：'.$e->getMessage());
			}
		}
		return LibFc::ReturnData(true, true); 
	}

	/**
	  * @method 搜索
	  * @param  $params array ['word'=>'', 'shop_id'=>'选填', 'pro_id'=>[1,2,3...], 'count'=>10000, 'match'=>0|1优先精准匹配]
	  * @author soul 
	  * @return
	  */
	public function searchProId($params)
	{
		if(empty($params['count'])) $params['count'] = 10000;
		$re = ["、",",","，",";","；","\n","\t","\r", ' '];
		$keyword = str_replace($re, ',', $params['word']);
		$wordArr = explode(',', $keyword);
		$must = [["match"=>['pro_word' =>$params['word']]]];	
		//限制指定产品ID
		if(!empty($params['pro_id'])) $must[] = ["ids"=>['values' =>$params['pro_id']]];
		//限制店铺
		if(!empty($params['shop_id'])) $must[] = ["match"=>['shop_id' =>$params['shop_id']]];
		if(count($must) > 1){
			$query = ["bool"=>["must"=>$must]];
		}else{
			$query = $must[0];
		}
		$blparams = [
			'index' => $this->index,
			'body' => [ 
				'query' => $query,
				'size'=>(int)$params['count']
			]
		];
		$response = $this->client->search($blparams);
		$hits = $response['hits']['hits'];
		//相关性有问题，优先排有重叠的
		$newhits = [];
		foreach($hits as $k=>$v){
			foreach($wordArr as $vv){
				//if(mb_strlen($vv) > 1 && $this->findCommonSubstrings($v['_source']['pro_word'], $vv)){
				if(mb_strlen($vv) > 1 && stripos($v['_source']['pro_word'], $vv) !== false){
					$newhits[] = $v;
					unset($hits[$k]);
					break;
				}
			}
		}
		/*if(empty($newhits)){
			foreach($hits as $k=>$v){
				$newhits[] = $v;
			}
		}*/
		$proIds = array_column($newhits, '_id');
		//var_dump($wordArr, $must, $proIds, $newhits);
		//echo ceil((microtime(true) - $time)*1000), 'ms=>';
		return LibFc::ReturnData(true, $proIds);
	}


	function findCommonSubstrings($str1, $str2)
	{
		$result = [];
		$len1 = mb_strlen($str1);
		$prevSubstring = '';
		$start = -1;
		$end = -1;
		for ($i = 0; $i < $len1; $i++) {
			for ($j = $i + 1; $j < $len1; $j++) {
				$substring = mb_substr($str1, $i, $j - $i + 1);

				if (mb_strpos($str2, $substring) !== false) {
					if ($substring === $prevSubstring) {
						$end = $j;
					} else {
						if ($start !== -1) {
							$result[] = mb_substr($str1, $start, $end - $start + 1);
						}
						$start = $i;
						$end = $j;
						$prevSubstring = $substring;
					}
				}
			}
		}

		if ($start !== -1) {
			$result[] = mb_substr($str1, $start, $end - $start + 1);
		}
		$result = array_unique($result);
		$result = array_filter($result, function ($substring) {
			return mb_strlen($substring) >= 2;
		});
		return $result;
	}
}