1. 前两天做了一个项目, 其中有一个需求是根据用户输入的关键词查询邮编. 最开始设计的数据库结构是省市区分为三个字段, province, city, area, 但是在写代码实现的过程中发现, 用户只输入省或者市或区县, 通过mysql的like模糊查询没问题. 但是如果用户输入的是"广东省深圳", "广东省南山区", "深圳市南山"等等, 显然只使用like无法实现, 我打算使用正则先将用户输入的关键词进行分割, 拆分成三个关键词, 然后再根据拆分后的每个关键词进行模糊查询, 我尝试使用`or like`, 想着使用一条SQL语句进行多个条件的查询, 但是没有成功, 下面的就是根据三个关键词进行查询的部分代码(正则的部分没有贴出), 当你输入"深圳市南山区" 的时候, 查询不到数据. 只有输入"广东省深圳市南山区" 才有数据. 因为正则的分隔是优先分隔省出来, 也就是你输入的是深圳市南山区, 他会把深圳市当做省来查询, 为什么这样呢, 因为要考虑到直辖市, 北京市就是省级的, 所以不好处理正则分隔. 还有内蒙古自治区, 也是省级的.

     if(!empty($pro)){
    $where = ['or like', 'province', $pro[0]];
    }
    if(!empty($city)){
    //$where = ['and', ['like', 'city', $city[0]]];
    $where = ['or like', 'city', $city[0]];
    }
    if(!empty($area)){
    //$where = ['and', ['like', 'area', $area]];
    $where = ['or like', 'area', $area];
    }
    $result = Postcode::find()->select('province, city, area, post_code')->where($where)->asArray()->all();
  2. 上面的数据库结构似乎不太行, 我决定改变数据库的结构, 把省市区三个字段合并为一个字段, 使用逗号进行分隔区分. 这样做好处是 不管用户输入的是什么, 我都可以在一个字段里进行模糊查询. 这样的话, 数据库的字段主要就是两个, place(省市区合并后的字符串). postcode(邮编), 其他自行添加. 事实证明这种方案确实可行, 最后成功了. 接着说, 数据库的省市区之间是有逗号的, 所以在查询的时候,也需要加上逗号, 否则本来能查到数据的也查不到了.
  3. 对用户输入的关键词进行正则分割,  正则分割这里, 最开始我才用的策略是进行一次性分割, 如下
    //一次性分隔, 根本无法确认第几个元素有值, 不可行
    $pattern = '/(.*市)(.*市).*?|(.*省)(.*市).*?|(.*区)(.*盟).*?|(.*区)(.*市).*?|(.*省)(.*州).*?|(.*省)(.*区).*?|(.*省)(.*县).*?|(.*区)(.*区).*?|(.*区)(.*州).*?/';
    preg_match_all($pattern, $q, $matches);
    //但是有个问题就是匹配到的数据在matches数组的第几个元素里, 这是无法确定的. 除非把matches数组遍历, 判断哪个元素有值.

    我觉得这个不太可行, 我才用第二个策略, 先把关键词按照省来分隔, 再将匹配到的省从关键词截掉, 在剩下的关键词里面匹配市, 最后剩下的就是区县, 正则代码如下

     $pattPro = '/.*省|.*市|.*区/U';    //必须加上U, 在这里使用?是不能防止贪婪匹配的, e.g: 北京市北京市, 正则表达式顺序市在前省在后就会先匹配市
    preg_match($pattPro, $q, $pro); //e.g: 辽宁省沈阳市,匹配"省"的结果就是辽宁省沈阳市,而不是辽宁省和沈阳市 /* 把匹配到的省份从关键词中去除, 方便之后市区的匹配, 并且注意是中文, 所以需要mb_string扩展 */
    $lenPro = mb_strlen($pro[0]); //由于mb_strpos获取一个字符串首次出现的位置是按照第一个字符的位置, 而不是最后一个字符的位置, 所以先获取字符串的最后一个字符
    /* 特别注意: 获取字符串长度的时候,没有使用mb_开头的函数, 截取的开始位置不同的 */
    $q = mb_substr($q, $lenPro); //从最后一个字符出现的位置+1开始截取就是省后面的城市名了, $pattCity = '/.*市|.*盟|.*州|.*区|.*县/U';
    preg_match($pattCity, $q, $city);
    $lenCity = mb_strlen($city[0]);
    $area = mb_substr($q, $lenCity);

    正则

    分隔完再添加逗号, 去数据库查询.

     $info = $pro[0].','.$city[0].','.$area;
    $info = trim($info, ','); //去掉逗号, 那么无论省市区哪个为空, 最终字符串info里左右都只会剩下一个",", 这样总是可以在数据库中查询到
    $where = ['or like', 'place', $info];
    $count = post_code::find()->where($where)->count();
    $p = new Pagination(['totalCount'=>$count, 'pageSize'=>$pagesize]);
    $result = post_code::find()->select('place, postcode')->where($where)->offset(($page-1)*$pagesize)->limit($p->limit)->asArray()->all();

    查询数据库

    经过测试, 发现了一个问题, 就是用户输入"江西" 不加"省", 那么正则匹配后会在江西后面加上逗号, 这显然查不到数据的, 因为数据库里存的是"江西省," , 还有输入"江西省吉安", 也查不到,

  4. 为了解决这些问题, 又想到了一种新的方案, 先试想一下, 用户输入习惯, 用户一般肯定是比较喜欢输入"江西", 而不是"江西省", 输入"内蒙古" 而不是"内蒙古自治区", 所以能够发现我只要在用户输入的时候, 先判断一下关键词的字数, 只要是<=3, 那么就不需要进行正则匹配, 直接拿到数据库进行查询, 只有在关键词字数大于 3 的时候, 才进行正则等一系列的操作, 发现这个解决了之前的问题. 不管用户输入"广东省深圳", 还是"深圳", 都可以查到.

  5. 最后附上完整代码, (注: 这里因为数据比较多, 所以使用了分页查询, 以及对数组进行分页等)

     /* 获取邮编号码 */
    public function actionPostcode($page=1, $pagesize=20, $q=null)
    {
    if($q==null){
    $count = post_code::find()->count();
    $p = new Pagination(['totalCount'=>$count, 'pageSize'=>$pagesize]);
    $result = post_code::find()->select('place, postcode')->offset(($page-1)*$pagesize)->limit($p->limit)->asArray()->all();
    if(!$result){
    return [
    'code' => 1,
    'msg' => '没有数据',
    ];
    }
    $data = [];
    foreach($result as $k=>$v){
    $data[$k]['province'] = explode(',', $v['place'])[0];
    $data[$k]['city'] = explode(',', $v['place'])[1];
    $data[$k]['area'] = explode(',', $v['place'])[2];
    $data[$k]['postcode'] = $v['postcode'];
    }
    return [
    'code' => 0,
    'msg' => 'success',
    'data' => $data,
    ];
    }
    /* 使用一个折中方案处理用户输入"石家庄"也能搜索的情况, 就是判断用户输入的字数, 如果<=3, 则直接去数据库查询, 只有字数超过3才进行分隔 */
    if($q != null && !is_numeric($q) && mb_strlen($q)<=3){
    $where = ['like', 'place', $q];
    $count = post_code::find()->where($where)->count();
    $p = new Pagination(['totalCount'=>$count, 'pageSize'=>$pagesize]);
    $result = post_code::find()->select('place, postcode')->where($where)->offset(($page-1)*$pagesize)->limit($p->limit)->asArray()->all();
    if(!$result){
    return [
    'code' => 1,
    'msg' => '没有数据',
    ];
    }
    $data = [];
    foreach($result as $k=>$v){
    $data[$k]['province'] = explode(',', $v['place'])[0];
    $data[$k]['city'] = explode(',', $v['place'])[1];
    $data[$k]['area'] = explode(',', $v['place'])[2];
    $data[$k]['postcode'] = $v['postcode'];
    }
    return [
    'code' => 0,
    'msg' => 'success',
    'data' => $data,
    ];
    } /* 按照省市区关键词查询, 并且关键词字数大于3 */
    if($q != null && !is_numeric($q) && mb_strlen($q)>3){
    $pattPro = '/.*省|.*市|.*区/U'; //必须加上U, 在这里使用?是不能防止贪婪匹配的, e.g: 北京市北京市, 正则表达式顺序市在前省在后就会先匹配市
    preg_match($pattPro, $q, $pro); //e.g: 辽宁省沈阳市,匹配"省"的结果就是辽宁省沈阳市,而不是辽宁省和沈阳市 /* 把匹配到的省份从关键词中去除, 方便之后市区的匹配, 并且注意是中文, 所以需要mb_string扩展 */
    $lenPro = mb_strlen($pro[0]); //由于mb_strpos获取一个字符串首次出现的位置是按照第一个字符的位置, 而不是最后一个字符的位置, 所以先获取字符串的最后一个字符
    /* 特别注意: 获取字符串长度的时候,没有使用mb_开头的函数, 截取的开始位置不同的 */
    $q = mb_substr($q, $lenPro); //从最后一个字符出现的位置+1开始截取就是省后面的城市名了, $pattCity = '/.*市|.*盟|.*州|.*区|.*县/U';
    preg_match($pattCity, $q, $city);
    $lenCity = mb_strlen($city[0]);
    $area = mb_substr($q, $lenCity); /* 将关键词使用逗号拼接起来, 再去数据库模糊查询 */
    // if(!empty($pro) && empty($city) && empty($area)){
    // $info = $pro[0];
    // }elseif (!empty($pro) && !empty($city) && empty($area)){
    // $info = $pro[0].','.$city[0];
    // }else{
    // $info = $pro[0].','.$city[0].','.$area;
    // } /* 因为县,区既可以理解为市级也可以理解为区级, 所以正则会默认按照市级进行分割 */
    /* 只有一种情况需要单独考虑, 就是关键词是省区, 中间跨过市的情况下, 直接查询数据库是查询不到结果的 */
    $status = false; //如果用户输入"江西省吉安", 分隔后city为空, area不为空,如果是这种情况, 将status置为true
    if(empty($city[0]) && !empty($area)){
    $info = [$pro[0], $area]; //参考or like 的使用方法
    $status = true;
    $where = ['or like', 'place', $info];
    $res = post_code::find()->select('place, postcode')->where($where)->asArray()->all();
    }else{
    $info = $pro[0].','.$city[0].','.$area;
    $info = trim($info, ','); //去掉逗号, 那么无论省市区哪个为空, 最终字符串info里左右都只会剩下一个",", 这样总是可以在数据库中查询到
    $where = ['or like', 'place', $info];
    $count = post_code::find()->where($where)->count();
    $p = new Pagination(['totalCount'=>$count, 'pageSize'=>$pagesize]);
    $result = post_code::find()->select('place, postcode')->where($where)->offset(($page-1)*$pagesize)->limit($p->limit)->asArray()->all();
    } if($status){
    /* 在返回的结果中进行二次过滤, 因为江西省吉安的这种情况将返回的是江西省的数据, 而不是北京, 所以可以进行二次过滤 */
    $new_res = []; //用来接收二次过滤后的数据,
    foreach($res as $k=>$v){
    if(strstr($v['place'], $area) !== false){ //也可以使用正则匹配数组, 这里使用 strstr模拟模糊匹配
    /* 匹配成功 TODO */
    array_push($new_res, $v);
    }
    }
    /* 做分页处理 */
    $pnum = ceil(count($new_res) / $pagesize);
    if($page > $pnum){
    $page = $pnum;
    }
    $result = array_slice($new_res, ($page-1)*$pagesize, $pagesize);
    }
    if(!$result){
    return [
    'code' => 1,
    'msg' => '没有数据',
    ];
    }
    /* 处理数据结构, 分隔省市区为三个字段 */
    $data = [];
    foreach($result as $k=>$v){
    $data[$k]['province'] = explode(',', $v['place'])[0];
    $data[$k]['city'] = explode(',', $v['place'])[1];
    $data[$k]['area'] = explode(',', $v['place'])[2];
    $data[$k]['postcode'] = $v['postcode'];
    }
    return [
    'code' => 0,
    'msg' => 'success',
    'data' => $data,
    ];
    }
    /* 按照邮编查询 */
    if($q != null && is_numeric($q)){
    $where = ['like', 'postcode', $q];
    $count = post_code::find()->where($where)->count();
    $p = new Pagination(['totalCount'=>$count, 'pageSize'=>$pagesize]);
    $result = post_code::find()->select('place, postcode')->where($where)->offset(($page-1)*$pagesize)->limit($p->limit)->asArray()->all(); //防止用户输入类似0返回所有数据的情况
    if(!$result){
    return [
    'code' => 1,
    'msg' => '没有数据',
    ];
    }
    $data = [];
    foreach($result as $k=>$v){
    $data[$k]['province'] = explode(',', $v['place'])[0];
    $data[$k]['city'] = explode(',', $v['place'])[1];
    $data[$k]['area'] = explode(',', $v['place'])[2];
    $data[$k]['postcode'] = $v['postcode'];
    }
    return [
    'code' => 0,
    'msg' => 'success',
    'data' => $data,
    ];
    }
    }

    完整代码

    对数组进行模糊匹配可以使用正则preg_grep, 还可以使用strstr函数模拟实现

  6. 这里对数组分页参考了https://www.cnblogs.com/vip-deng-vip/p/8005434.html

PHP实现省市区关键词搜索邮编的更多相关文章

  1. Kaldi的关键词搜索(Keyword Search,KWS)

    本文简单地介绍了KWS的原理--为Lattice中每个词生成索引并进行搜索:介绍了如何处理OOV--替补(Proxy,词典内对OOV的替补)关键词技术:介绍了KWS的语料库格式:介绍了KWS在Kald ...

  2. Thinkphp+Ajax带关键词搜索列表无刷新分页实例

    Thinkphp+Ajax带关键词搜索列表无刷新分页实例,两个查询条件,分页和搜索关键字,懂的朋友还可以添加其他分页参数. 搜索#keyword和加载内容区域#ajax_lists <input ...

  3. 百度关键词搜索工具 v1.1|url采集工具 v1.1

    功能介绍:关键词搜索工具 批量关键词自动搜索采集 自动去除垃圾二级泛解析域名 可设置是否保存域名或者url 持续更新中

  4. 【python网络编程】新浪爬虫:关键词搜索爬取微博数据

    上学期参加了一个大数据比赛,需要抓取大量数据,于是我从新浪微博下手,本来准备使用新浪的API的,无奈新浪并没有开放关键字搜索的API,所以只能用爬虫来获取了.幸运的是,新浪提供了一个高级搜索功能,为我 ...

  5. java 抓取百度根据关键词搜索域名

    package baidusearch; import com.sun.glass.ui.SystemClipboard; import java.util.*; import java.util.H ...

  6. 网站被k到可以使用关键词搜索到首页优化总结

    从今年二月份,刚过完年回到公司,大约一周多过后,网站就被不知名黑客攻击,然后又因为网站标题关键词堆砌导致网站被降权,从此首页不在有我的网站的踪迹,有的只是其他页面的信息,因为刚开始接触SEO,对这一块 ...

  7. 获取百度地图POI数据三(模拟关键词搜索)

    上一篇博文中讲到如何获取用于搜索的关键词,并且已经准备好了一百五十万的关键词   这其中有门牌号码,餐馆酒店名称,公司名称,道路名称等.有了这些数据,我们就可以通过代码,模拟我们在百度地图的搜索框中搜 ...

  8. WordPress如何屏蔽恶意关键词搜索

    我们在用WordPress建站比较方便,但如果网站有一定的权重后,一些不怀好意的人就会过来制作恶意内容,比如故意搜索邪恶的关键词.垃圾评论等,那我们如何屏蔽恶意搜索关键词呢?不会很难,会写点代码的朋友 ...

  9. JavaScript在表格中模拟搜索多关键词搜索和筛选

    模拟搜索需要实现以下功能: 1.用户的模糊搜索不区分大小写,需要小写字母匹配同样可以匹配到该字母的大写单词. 2.多关键词模糊搜索,假设用户关键词以空格分隔,在关键词不完整的情况下仍然可以匹配到包含该 ...

随机推荐

  1. php实现算法

    二分法查找(已排序) @params  $arr 查找的数组  $start 开始查找的下标  $end 结束查找的下标  $value 查找的值 function bin_search($arr,$ ...

  2. vue的基本语法

    在学习vue之前,我们应了解一下什么是vue.js? 什么是Vue.js? Vue.js是目前最后一个前端框架,React是最流行的一个前端框架(react除了开发网站,还可以开发手机App,Vue语 ...

  3. 【思维】Kenken Race

    题目描述 There are N squares arranged in a row, numbered 1,2,...,N from left to right. You are given a s ...

  4. 怎样查看或修改元素节点的id属性

    使用 el.id; el表示获取到的元素节点, 如下所示: // HTML 代码 // <div id="app" class="c1">hello ...

  5. 12-MySQL DBA笔记-MySQL复制

    第12章 MySQL复制 本章将为读者讲述MySQL的复制技术,首先,介绍最基础的主从复制,它是其他所有复制技术的基础,接着再为读者讲述各种复制架构的搭建,最后,列举了一些常见的复制问题及处理方式.复 ...

  6. 7-MySQL DBA笔记-研发规范

    第7章 研发规范 本章将为读者解读一份研发规范.为了更好地协同工作和确保所开发的应用尽可能的稳定.高效,建立一套数据库相关的研发规范是很有必要的,虽然研发规范的确立和推广是一项很耗时的工作,但所取得的 ...

  7. WebStorm使用码云插件问题

    由于项目需求,需要在WebStorm中使用码云插件,在下载安装的过程中出现一系列的问题,现总结出现的问题和解决方法. 先说一下码云是什么?码云有什么作用? 码云的主要功能: 码云除了提供最基础的 Gi ...

  8. chartjs显示数值标签插件:chartjs-plugin-datalabels

    Getting Started #Installation #npm   npm install chartjs-plugin-datalabels --save This plugin can al ...

  9. B树,B+树的原理及区别

    如图所示,区别有以下两点: 1. B+树中只有叶子节点会带有指向记录的指针(ROWID),而B树则所有节点都带有,在内部节点出现的索引项不会再出现在叶子节点中. 2. B+树中所有叶子节点都是通过指针 ...

  10. php实现拼图滑块验证的思考及部分实现

    实现拼图滑块验证,我觉得其中比较关键的一点就是裁剪图片,最起码需要裁剪出下面两张图的样子 底图 滑块图 一张底图和一张滑块图,其中底图实现起来比较简单可以使用添加水印的方式直接将一张拼图形状的半透明图 ...