搜索引擎优化定义(为什么说一个试题是多个index,而question却是question?)
优采云 发布时间: 2021-09-28 17:43搜索引擎优化定义(为什么说一个试题是多个index,而question却是question?)
基本情况是媒体、试题和分类。媒体可能有多个试题,一个试题可能有多个类别。分类是三级类别加综合属性。通过测试标题、分类等方式搜索和查询媒体。
目前的问题是搜索结果不准确,有的搜索没有结果。ES的数据结构不符合搜索要求。解决方案是重构ES数据结构,采用父子关系,建立媒体和问题两种类型。
在整个过程中使用它。ES的管理和查看非常方便。
从ES的描述可以看出,ES是面向文档的。其实所有的数据都是卡片,比如下面这些:
几个重要的概念:mapping、index、type可以直接参考上图:
_index,可以看作是一个数据库,上图是命名链接,操作搜索时需要指定,就像指定数据库一样。
_type 大约等于 table,比如这里的 media。也可以在上图中的_type列中看到问题值。其实就等于我们的媒体表和问题表
映射,映射,绘制地图...,顾名思义。其实就是表结构和表关系。比如上面点击的卡片,_source里面有id,language等,其实就是mapping。映射还包括关系的定义。例如,这里的媒体是父父级。创建问题结构时,需要指定 _parent 作为媒体。
理解了上面的概念之后,我们就可以创建结构了。就像数据库一样,我们需要一个媒体表来保存媒体信息,媒体ID就是唯一ID。然后是题表,放题信息(这里也包括试题的分类),我们把同一个试题分到不同的类别,算作不同的试题。这里的结构也是为了方便基于多级分类的搜索而设置的。搜索时将突出显示以下内容。
这是初始化创建索引和映射的代码:
$elasticaClient = new \Elastica\Client(array('host'=>'localhost','port'=>9200)); // Load index $elasticaIndex = $elasticaClient->getIndex('links'); // Create the index new // 创建index的参数自行参见官网,就不一一解释了 $elasticaIndex->create( array( 'number_of_shards' => 4, 'number_of_replicas' => 1, 'analysis' => array( 'analyzer' => array( 'indexAnalyzer' => array( 'type' => 'custom', 'tokenizer' => 'standard', 'filter' => array('lowercase', 'mySnowball') ), 'searchAnalyzer' => array( 'type' => 'custom', 'tokenizer' => 'standard', 'filter' => array('standard', 'lowercase', 'mySnowball') ) ), 'filter' => array( 'mySnowball' => array( 'type' => 'snowball', 'language' => 'German' ) ) ) ), true ); //创建media的mapping,作为父级 $mediaType = $elasticaIndex->getType('media'); // Define mapping $mapping = new \Elastica\Type\Mapping(); $mapping->setType($mediaType); $mapping->setParam('index_analyzer', 'indexAnalyzer'); $mapping->setParam('search_analyzer', 'searchAnalyzer'); // Define boost field $mapping->setParam('_boost', array('name' => '_boost', 'null_value' => 1.0)); // Set mapping // 定义media的字段和属性 $mapping->setProperties(array( 'id' => array('type' => 'string', 'include_in_all' => FALSE), 'media_name' => array('type' => 'string', 'include_in_all' => TRUE), 'tstamp' => array('type' => 'date', 'include_in_all' => FALSE), 'language' => array('type' => 'integer', 'include_in_all' => FALSE), '_boost' => array('type' => 'float', 'include_in_all' => FALSE) )); // Send mapping to type // 保存media的mapping $mapping->send(); //创建question的mapping,父级为media $questionType = $elasticaIndex->getType('question'); // Define mapping $mapping = new \Elastica\Type\Mapping(); $mapping->setType($questionType); $mapping->setParam('index_analyzer', 'indexAnalyzer'); $mapping->setParam('search_analyzer', 'searchAnalyzer'); // Define boost field $mapping->setParam('_boost', array('name' => '_boost', 'null_value' => 1.0)); // Set mapping // question的字段和属性 $mapping->setProperties(array( 'id' => array('type' => 'string', 'include_in_all' => FALSE), 'level_one' => array('type' => 'integer', 'include_in_all' => FALSE), 'level_two' => array('type' => 'integer', 'include_in_all' => FALSE), 'level_thr' => array('type' => 'integer', 'include_in_all' => FALSE), 'top_level' => array('type' => 'string', 'include_in_all' => FALSE), 'cat_id' => array('type' => 'integer', 'include_in_all' => FALSE), 'quest_hash' => array('type' => 'string', 'include_in_all' => TRUE), 'content' => array('type' => 'string', 'include_in_all' => TRUE), 'view_num' => array('type' => 'integer', 'include_in_all' => FALSE), 'like_num' => array('type' => 'integer', 'include_in_all' => FALSE), '_boost' => array('type' => 'float', 'include_in_all' => FALSE) )); $mapping->setParent("media");//指定question的父类 // Send mapping to type // 保存question的mapping $mapping->send();
虽然上面是PHP代码,但最终生成的也是一个url请求。
下面我们来谈谈搜索。ES通过query、filter等处理,query有很多种不同的方式,见:,filter也见
这里的搜索是这样的。根据media的media_name做一个query_string搜索,然后对media进行has_child过滤搜索,在has_child搜索中使用boolAnd filter进行过滤。
这是搜索代码:
$query = new \Elastica\Query(); if (!empty($input['key'])) { //针对media的media_name字段设置QueryString查询 $elasticaQueryString = new \Elastica\Query\QueryString(); $elasticaQueryString->setFields(array("media.media_name")); $elasticaQueryString->setQuery($input['key']); // $query->setQuery($elasticaQueryString); }else { $query->setQuery(new MatchAll()); //命中全部纪录 } $language_bool = false; $elasticaFilterAnd = new \Elastica\Filter\BoolAnd(); //language也是针对media,设置BoolAnd查询 if (isset($input['language']) && !empty($input['language'])) { $filterl1 = new \Elastica\Filter\Term(); $filterl1->setTerm('language', intval($input['language'])); $elasticaFilterAnd->addFilter($filterl1); $language_bool = true; } // //对子集进行筛选查询,使用has_child $subFilterAnd = new \Elastica\Filter\BoolAnd(); $bool = false; // 一级分类条件 if (isset($input['level_one']) && !empty($input['level_one'])) { $filterl1 = new \Elastica\Filter\Term(); $filterl1->setTerm('level_one', intval($input['level_one'])); $subFilterAnd->addFilter($filterl1); $bool = true; } // 二级分类条件 if (isset($input['level_two']) && !empty($input['level_two'])) { $filterl1 = new \Elastica\Filter\Term(); $filterl1->setTerm('level_two', intval($input['level_two'])); $subFilterAnd->addFilter($filterl1); $bool = true; } // 三级分类条件 if (isset($input['level_thr']) && !empty($input['level_thr'])) { $filterl1 = new \Elastica\Filter\Term(); $filterl1->setTerm('level_thr', intval($input['level_thr'])); $subFilterAnd->addFilter($filterl1); $bool = true; } // 直接指定分类ID查询 if (isset($input['cat_id']) && !empty($input['cat_id'])) { $filterl1 = new \Elastica\Filter\Term(); $filterl1->setTerm('cat_id', intval($input['cat_id'])); $subFilterAnd->addFilter($filterl1); $bool = true; } // 分类属性查询 if (isset($input['top_level']) && !empty($input['top_level'])) { $filterl1 = new \Elastica\Filter\Term(); $filterl1->setTerm('top_level', $input['top_level']); $subFilterAnd->addFilter($filterl1); $bool = true; } if($bool){ // 声明一个查询,用于放入子查询 $subQuery = new \Elastica\Query(); // 使用filteredquery,融合query和filter $filteredQuery = new \Elastica\Query\Filtered(new MatchAll(),$subFilterAnd); // 添加filterquery到子查询 $subQuery->setQuery($filteredQuery); // 声明hasChildFilter,声明的时候就指定子查询的内容,指定查询的子表(也就是TYPE)为question $filterHasChild = new \Elastica\Filter\HasChild($subQuery,"question"); // 将拥有子类查询增加到父级查询的filter中 $elasticaFilterAnd->addFilter($filterHasChild); } if($bool || $language_bool){ // 将filter增加到父查询汇中 $query->setFilter($elasticaFilterAnd); } // // $query->setFrom($start); // Where to start? $query->setLimit($limit); // How many? // //Search on the index. $elasticaResultSet = $elasticaIndex->search($query);
上面看起来很长的PHP代码实际上只是最后发送json数据的请求。将下面的json数据和上面的代码对比一下,大家就很容易理解了:
{ "query": { "query_string": { "query": "like", "fields": [ "media.media_name" ] } }, "filter": { "and": [ { "term": { "language": 1 } }, { "has_child": { "query": { "filtered": { "query": { "match_all": {} }, "filter": { "and": [ { "term": { "top_level": "111" } } ] } } }, "type": "question" } } ] }, "from": 0, "size": 20 }
总结:ES非常强大,不仅是导入性能或者搜索性能,或者搜索结果,或者结构调整。作为一个能快速重构数据结构,重写搜索的新手,还是比较不错的。唯一的缺点就是中文文档太少了,需要不断的用google查看文档,去官网看文档,看PHP API。
部分参考来自这里:
从: