Elasticesearch和PHP结合使用搜索分词

本示例所用环境
* 机器:普通电脑 ThinkPad E470c
* 系统:Ubuntu 16.04 LTS(在ArchLinux下测试也没有任何问题)
步骤

一. 学习Python,使用Python爬虫框架Scrapy爬取测试数据,之前准备爬取微博数据,学艺不精,爬取了豆瓣书籍目录下的一些信息。
1. 学习Python,感谢廖雪峰老师关于Python教程所作的贡献,教程地址
2. 感谢Scrapy文档,感叹:只要有一颗学习的心,知识真是唾手可得。
3. 感谢不知名网友,通过他的示例,完成了自己的需求,我自己爬取豆瓣书籍信息的项目地址


二. 爬取数据时,是保存在文件里的,用PHP把数据导入MySQL。
1. 把抓取到的书籍信息文件items.json放入第四步的demo里,然后使用demo里的PHP脚本导入MySQL.


三. 安装Elasticsearch,用PHP把数据从MySQL导入Elasticsearch。
1. 感谢阮一峰老师关于Elasticsearch教程所作的贡献,教程地址
这里面讲讲解了Elastic的安装和简单使用,还有分词插件。
2. 通过上述教程,结合Elastic官方出的elasticsearch-php库,完成了下面的demo。


四. 完成demo。
1. 项目地址
2. 图示
elasticsearch搜索显示示例图片


demo简介
  • Html+jquery+bootstrap+PHP+Elasticsearch中文分词搜索显示的示例。

demo目录结构及文件说明:

README.md 此说明


composer.json composer资源文件,此demo所使用的两个PHP库,Eloquent和elasticesearch-php库。Eloquent是Laravel里使用的数据库ORM,方便好用,能独立于Laravel使用。elasticsearch是PHP调用Elasticsearch服务的库。clone此项目后,使用命令:composer update -vvv安装依赖。


items.json 使用Python爬虫获取的豆瓣读书目录下的一些信息,JSON格式。此文件已从版本库里去掉,文件已共享


book.sql 数据库和数据表结构


douban.sql 爬取的数据导入MySQL后,导出的小部分SQL数据。觉得自己学习爬数据麻烦的同学,可以直接把数据导入MySQL。觉得数据太少的同学可以下载上面的json文件自己使用下面的脚本导入数据库,再从数据库导入ElasticSearch。


import_data_to_book_table_form_items_json.php 把items.json文件内容导入数据表脚本,命令:php 文件名直接执行


start.php PHP第三方库和数据库等前置配置



以下四个文件是单独测试Elasticsearch示例

createIndex.php 创建elasticsearch index、type(类似创建MySQL数据库、数据表),命令:php createIndex.php

insert.php 向elasticsearch插入测试数据,命令:php insert.php

search.php 根据条件查询数据,命令:php search.php

delete.php 删除elasticsearch index或者document,命令:php delete.php


douban 实际搜索显示demo目录

createDoubanIndex.php 创建elasticsearch index、type(类似创建MySQL数据库、数据表),命令:php createDoubanIndex.php

insertDataToEs.php 把之前导入MySQL的数据导入ElasticSearch,使用Eloquent ORM查出数据,然后批量插入Elasticsearch,命令:php insertDataToEs.php

search.php 前端请求的后端地址文件

view 前端模版文件目录

index.html 搜索显示页面,使用ajax分页和向后端传递数据

在自己本地测试访问地址:localhost/项目路径/elasticsearch_example/douban/view/index.html

完结

PHP设计模式-工厂模式

  • 工厂模式

    工厂设计模式提供获取某个对象的新实例的一个接口,同时使调用代码避免确定实际实例化基类的步骤。

    简述:假设,我们现在在获取微信端消息的通知,消息有许多类型,比如文本、图片、语音等,这些类型都包含在微信推送的内容里,需求是要把这些内容都保存在本地的数据库,我们来模拟一下,方法大概有两种:

    微信端消息的通知:用户向某服务号发送信息或触发了一些事件(扫码,上报地理位置等),如果此服务号开启了开发者的一些设置,那么微信会把相关信息或事件推送给服务号设置的接收信息地址。

  1. 获取到微信推送的内容后,判断出信息类型,然后实例化不同的对象来保存对应的信息。
    // 模拟获取信息
    $message = file_get_contents("php://input");
    switch ($message->type) {
        case 'link':
            $object = new Link();
            $object->save($message->content);
            break;
        case 'text':
            $object = new Text();
            $object->save($message->content);
            break;
        case 'image':
            $object = new Image();
            $object->save($message->content);
            break;
        case 'voice':
            $object = new Voice();
            $object->save($message->content);
            break;
        case 'video':
            $object = new Video();
            $object->save($message->content);
            break;
        case 'location':
            $object = new Location();
            $object->save($message->content);
            break;
        default:
            # code...
        break;
    }
    
  2. 使用工厂模式,可以有助于减少主代码流中基于条件的复杂性。
    // 模拟获取信息
    $message = file_get_contents("php://input");
    class MessageFactory {
        public static function create($type) {
            $class = ucfirst(strtolower($type));
    
            return new $class;
        }
    }
    
    $objcet = MessageFactory::create($message->type);
    $object->save($message->content);
    

    可以看到,第二种方式明显比第一种更简洁,不需要做过多的判断。另外,如果微信端增加了新的信息类型,我们在接收时也不需要再增加判断语句,只要增加一个处理对应类型的类即可。

    这种模式有点像硬币自动分拣器一样,不需要每个都判断它是五毛的还是一块的硬币,它会自动去到自己的存放点。见下图:
    硬币自动分拣器

PHP设计模式-建造者模式

  • 建造者模式

    该模式定义了处理其它对象的复杂构建的对象设计。

    简述:是不是很晦涩?没错。现在我们管建造者模式叫榨汁机模式,举个栗子:

    想象现在你有一台榨汁机,榨汁机有十个放入橙子的管道(忽略为什么会有十个管道的梗,我想要一杯大大大橙汁不行么,其实这里类比的意思是:你有需要在十个地方构建同一个对象来使用),当你想得到“一杯橙汁”这个对象时,常规做法,你每次需要:

    1. 从十个入口放入橙子;
    2. 从十个口把去皮的橙子放进去;
    3. 放入一些调味品(比如浓缩橙汁);
    4. 把杯子对准出口,用杯子盛橙汁;
    5. 按动开关,榨汁,得到一杯橙汁.
    

    如果使用榨汁机模式呢?

    1. 从十个入口放入橙子;
    2. 按动开关,榨汁,得到一杯橙汁.
    

    把常规做法下的第2,3,4步全部放入了榨汁机内部,你不用在每个入口处给橙子去皮(这是最重要的),就像你在构建一个对象时,不用每次都给每个对象设置某个属性一样。

    那么问题来了,使用榨汁机模式除了节省了一些构建对象(做出一杯橙汁)步骤之外,还有什么好处呢?
    现在假设你买的橙子都有很多坚硬的籽,如果直接把这些橙子用来榨汁喝会非常影响口感,所以我们需要把籽给去掉,常规做法下:

    1. 给十个橙子去皮;
    2. 给十个橙子去籽(即使是自动去籽也需要在十个入口处加自动去籽机);
    ......
    

    榨汁机模式下,在榨汁机内部增加一个去籽机,然后:

    1. 从十个口放入橙子;
    2. 按动开关,榨汁,得到一杯橙汁.
    

    看出问题了吗?构建对象时,在常规做法下,如果有什么修改,那么在每个构建的地方,都需要去修改。但是在建造者模式下,有了修改,只需要去修改这个构建对象一次即可。

    这就是我理解的建造者模式,如果你有更好的理解,欢迎在下面评论区留言!

PHP设计模式-适配器模式

  • 适配器模式

    将某个对象的接口适配为另一个对象所期望的接口。

    简述:B类在使用A类里的方法,现在C类也需要使用到A类,但又有新特性,怎么办?有两种做法:

    1. 改造A类,增加新方法;
    2. 使用适配器模式,增加一个 A' 类继承A类,把新方法放在 A' 类里,这样C类就直接使用 A' 就好了。
    

    个人生活举例:比如,你有一辆车,你想听音乐(据说开车和听歌更配哟),但车的中控系统里只能播放音乐碟片,这就很尴尬了,手机里有无数好歌,却不能享受,就像你很饿,眼前有盘红烧肉,但吃不到嘴一样。(什么?你说为什么不直接用手机放歌?这是我的设定啊喂!)

    现在也有两个做法:

    1. 大刀阔斧改造中控(拆卸都很麻烦),让她既能播放碟片也能使用蓝牙连接手机;
    2. 买一个蓝牙连接器,通过连线(假设存在这样的线,类似于PHP的继承)连接中控,然后 done。
    

    这就是我理解的适配器模式,如果你有更好的理解,欢迎在下面评论区留言!

PHP魔术常量的示例

php的魔术常量有8个

私以为这个顺序是比较方便记忆的,从整体到局部,分别是:
__DIR__       文件所在目录的绝对路径

__FILE__     文件所在绝对路径

__LINE__    文件所在绝对路径

__NAMESPACE__    当前命名空间名称

__CLASS__    当前类名称

__TRAIT__     当前Trait名称

__MEHTOD__   当前方法名称

__FUNCTION__    当前函数名称

有一点是需要注意的就是trait的优先级,从基类继承的成员会被 trait 插入的成员所覆盖。优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。当前类的方法又会覆盖trait的方法。
当有使用到Trait时,且没有被当前类里的方法覆盖时__METHOD__输出的是trait里的方法;

否则,__METHOD__输出的是当前类里的方法。

以下是代码。

<?php
namespace bobo;

trait Lianbo {
    public function test()
    {
        echo 'hello world'.PHP_EOL;

        // /var/www/html/test
        echo __DIR__.PHP_EOL;

        // /var/www/html/test/magic_const.php
        echo __FILE__.PHP_EOL;

        // 16
        echo __LINE__.PHP_EOL;

        // bobo echo 
        __NAMESPACE__.PHP_EOL;

        // bobo\Lianbo 
        echo __TRAIT__.PHP_EOL;
        // bobo\Bobo
        echo __CLASS__.PHP_EOL;

        // bobo\Lianbo::test
        echo __METHOD__.PHP_EOL;

        // test
        echo __FUNCTION__.PHP_EOL;
    }
}

class Bobo {
    use Lianbo;

    //public function test() {
        //echo __DIR__.PHP_EOL;
        //echo __FILE__.PHP_EOL;
        //echo __LINE__.PHP_EOL;
        //echo __NAMESPACE__.PHP_EOL;
        //echo __CLASS__.PHP_EOL;
        //echo __TRAIT__.PHP_EOL;
        //echo __METHOD__.PHP_EOL;
        //echo __FUNCTION__.PHP_EOL;
    //}
}

$obj = new Bobo;
$obj->test();

图片第一张是class Bobo里test()方法注释时的结果,第二张是注释打开时的结果。