- 浏览: 75461 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
hay:
大骗子
zend studio教程 -
jiewuzhe02:
请问开源CMS digitaluscms 安装后不能登录后台 ...
zend frawework 开源代码列子 -
天梯梦:
怎么一个都打不开啊
zend studio教程 -
freedomstyle:
调试的时候,变量中文出现乱码
zend studio for eclipse 中文乱码的问题 -
freedomstyle:
是的,我也碰到这样的问题。
zend studio for eclipse 中文乱码的问题
写给每个朋友来访的朋友!
Friday, December 12, 2008
想学习symfony,上网才知道学习资料这么难找。所以建了这个网站,将学习过程中遇到的问题,和能够提供的资料都发上来。希望对其他正在学习朋友有帮助,也希望得到朋友们的帮助。因为在网上没有发现比较好symfony中论坛,所以在建此网站的同时也建了一个。希望它能成为大家交流的一个平台。Filed Under: Technology 3 Comments
Popular Articles
Jobeet 第17天:搜索引擎
转载注明出处:http://symfony.lag.cn
Life@fter http://www.lag.cn
英文原版:http://www.symfony-project.org/jobeet/1_2/Propel/en/17
回顾
两天前,我们添加了feed,让用户可以订阅最新发布的招聘信息。今天,我们继续提高用户体验,实现Jobeet最后一个主要功能:搜索引擎。
The Technology
工作之前,我们先了解一点symfony的历史。我们始终提倡代码测试、重构这些好习惯,在开发symfony框架时也试着将它们应用其中。 “不要重新发明轮子”是我们座右铭。事实上,在4年前开始开发symfony时,我们就结合Mojavi 和 Propel这两个开源软件,而没有再去重新开发。正因如此,每当我们要解决一些新问题时,我们并不忙于开始编写代码,而是先去寻找是否有一个已经存在、并且好用库。
今天,我们要添加的搜索程序也是使用现有的库——
Zend Lucene,这是Zend框架中的一个库,是著名的Java Lucene项目的一个端口。
在Zend Lucene文档中,对这个库的描述:
… 一个PHP5写成的通用文本搜索引擎。使用文件系统存储索引,不需要数据库支持,所以它可以用在几乎所有PHP网站。Zend_Search_Lucene支持下面功能:
- 分等排列搜索结果 – 最好的结果显示在最前面
- 多种请求方式:短语请求,布尔请求,通配符请求,模糊请求,范围请求等等
- 在不同字段中搜索(如,标题,作者,内容)
我们这里不再多介绍Zend Lucene库的使用,而是重点讲如何在symfony中使用它;更广泛的来说,是如何在symfony中使用第三方软件。如果你想了解更多关于Zend Lucene的信息,请参考
Zend Lucene 文档。
在昨天安装Zend Framework的邮件库的时候,已经已经顺便安装了Zend Lucene库。
索引
当用户输入关键字时,Jobeet搜索引擎会返回与之相匹配的所有工作。我们必须给所有工作建立索引,搜索引擎才能正常,索引文件存储在data/目录下。
Zend Lucene提供2种方法检索索引,无论索引是否存在,方法都会访问索引文件。所以,我们必须在JobeetJobPeer中创建一个helper方法,当索引存在时则返回索引,如果不存在则创建一个新的索引文件:
// lib/model/JobeetJobPeer.php static public function getLuceneIndex() { ProjectConfiguration::registerZend(); if (file_exists($index = self::getLuceneIndexFile())) { return Zend_Search_Lucene::open($index); } else { return Zend_Search_Lucene::create($index); } } static public function getLuceneIndexFile() { return sfConfig::get('sf_data_dir').'/job.'.sfConfig::get('sf_environment').'.index'; }
The save()
method
每次创建、更新或删除工作时,索引文件也必须更新。
为了保证每次保存工作信息到数据时,都更新索引文件,我们需要编辑JobeetJob的save()方法:
// lib/model/JobeetJob.php public function save(PropelPDO $con = null) { // ... $ret = parent::save($con); $this->updateLuceneIndex(); return $ret; }
创建updateLuceneIndex()方法,它做实际工作:
// lib/model/JobeetJob.php public function updateLuceneIndex() { $index = JobeetJobPeer::getLuceneIndex(); // remove an existing entry if ($hit = $index->find('pk:'.$this->getId())) { $index->delete($hit->id); } // don't index expired and non-activated jobs if ($this->isExpired() || !$this->getIsActivated()) { return; } $doc = new Zend_Search_Lucene_Document(); // store job primary key URL to identify it in the search results $doc->addField(Zend_Search_Lucene_Field::UnIndexed('pk', $this->getId())); // index job fields $doc->addField(Zend_Search_Lucene_Field::UnStored('position', $this->getPosition(), 'utf-8')); $doc->addField(Zend_Search_Lucene_Field::UnStored('company', $this->getCompany(), 'utf-8')); $doc->addField(Zend_Search_Lucene_Field::UnStored('location', $this->getLocation(), 'utf-8')); $doc->addField(Zend_Search_Lucene_Field::UnStored('description', $this->getDescription(), 'utf-8')); // add job to the index $index->addDocument($doc); $index->commit(); }
因为Zend Lucene不能更新索引中已存在的记录,所以当我们需要更新一条已存在的记录时,必须先移除这条记录。
建立工作索引本身很简单:存储主键(pk)的作用是,做为搜索结果中工作URL的参数,用户可以通过URL访问相应的工作页面。而索引文件中存储的主字段(position, company, location, description)是用来匹配搜索内容的。
Propel异常处理
还有些问题,比如说一个工作存储到了数据库中,在写入索引文件时却失败了,或者存储一个工作到数据库失败了,但这个记录却写入的索引文件中,怎么办?Propel和Zend Lucene都会抛出异常。在一些情况下,我们可能已经将工作存入数据库,但没有生成相应的索引。为了阻止这种情况发生,我们可以将两种情况放入异常处理中,当发生错误时进行事务回滚:
// lib/model/JobeetJob.php public function save(PropelPDO $con = null) { // ... if (is_null($con)) { $con = Propel::getConnection(JobeetJobPeer::DATABASE_NAME, Propel::CONNECTION_WRITE); } $con->beginTransaction(); try { $ret = parent::save($con); $this->updateLuceneIndex(); $con->commit(); return $ret; } catch (Exception $e) { $con->rollBack(); throw $e; } }
delete()
我们同样需要覆盖delete()方法,当从数据库删除一条记录时,从索引文件中移除相应的记录:
// lib/model/JobeetJob.php public function delete(PropelPDO $con = null) { $index = JobeetJobPeer::getLuceneIndex(); if ($hit = $index->find('pk:'.$this->getId())) { $index->delete($hit->id); } return parent::delete($con); }
Mass delete
当我们使用propel:data-load导入初始化数据时,symfony通过调用JobeetJobPeer::doDeleteAll()方法移除所有存在的工作记录。修改该方法,在删除所有记录的同时删除全部索引文件:
// lib/model/JobeetJobPeer.php public static function doDeleteAll($con = null) { if (file_exists($index = self::getLuceneIndexFile())) { sfToolkit::clearDirectory($index); rmdir($index); } return parent::doDeleteAll($con); }
Searching
好了,万事俱备,现在导入数据,生成索引文件:
$ php symfony propel:data-load --env=dev
命令中使用的—env选项指定索引运行在dev环境中,默认环境是cli。
对于Unix用户:因为索引可能从命令行,也可能web进行修改,你必须同时保证两种情况下,索引目录都可写。
You might have some warnings about the
ZipArchive
class if you don’t have
thezip
extension compiled in your PHP. It’s a known bug of theZend_Loader
class.
在前台实现搜索非常容易。首先,创建路由:
job_search: url: /search param: { module: job, action: search }
然后,动作:
// apps/frontend/modules/job/actions/actions.class.php class jobActions extends sfActions { public function executeSearch(sfWebRequest $request) { if (!$query = $request->getParameter('query')) { return $this->forward('job', 'index'); } $this->jobs = JobeetJobPeer::getForLuceneQuery($query); } // ... }
模板也非常简单:
// apps/frontend/modules/job/templates/searchSuccess.php <?php use_stylesheet('jobs.css') ?> <div id="jobs"> <?php include_partial('job/list', array('jobs' => $jobs)) ?> </div>
搜索直接使用的方法getForLuceneQuery():
// lib/model/JobeetJobPeer.php static public function getForLuceneQuery($query) { $hits = self::getLuceneIndex()->find($query); $pks = array(); foreach ($hits as $hit) { $pks[] = $hit->pk; } $criteria = new Criteria(); $criteria->add(self::ID, $pks, Criteria::IN); $criteria->setLimit(20); return self::doSelect(self::addActiveJobsCriteria($criteria)); }
我们从Lucene索引中获得全部结果中过滤掉未激活的工作,将结果限制为20条记录。
更新layout:
// apps/frontend/templates/layout.php <h2>Ask for a job</h2> <form action="<?php echo url_for('@job_search') ?>" method="get"> <input type="text" name="query" value="<?php echo $sf_request->getParameter('query') ?>" id="search_keywords" /> <input type="submit" value="search" /> <div class="help"> Enter some keywords (city, country, position, ...) </div> </form>
Zend Lucene提供了丰富的查询机制,支持布尔、通配符、模糊搜索等方式。请参考
Zend Lucene手册。
Unit Tests
我们需要对搜索引擎做哪些测试呢?显然我不会测试Zend Lucene框架本身,但它集成在JobeetJob类中。
添加下面的测试到JobeetJobTest.php文件尾部,不要忘记更新测试的数目:
// test/unit/model/JobeetJobTest.php $t->comment('->getForLuceneQuery()'); $job = create_job(array('position' => 'foobar', 'is_activated' => false)); $job->save(); $jobs = JobeetJobPeer::getForLuceneQuery('position:foobar'); $t->is(count($jobs), 0, '::getForLuceneQuery() does not return non activated jobs'); $job = create_job(array('position' => 'foobar', 'is_activated' => true)); $job->save(); $jobs = JobeetJobPeer::getForLuceneQuery('position:foobar'); $t->is(count($jobs), 1, '::getForLuceneQuery() returns jobs matching the criteria'); $t->is($jobs[0]->getId(), $job->getId(), '::getForLuceneQuery() returns jobs matching the criteria'); $job->delete(); $jobs = JobeetJobPeer::getForLuceneQuery('position:foobar'); $t->is(count($jobs), 0, '::getForLuceneQuery() does not return delete jobs');
我们测试一个未激活或已删除的工作,是否没有出现在搜索结果中;也测试了匹配条件的工作是否显示在结果中。
Tasks
最后我们需要创建一个任务清理过期的索引(如,当工作过期),同时地对索引进行优化。因为我们已经有了cleanup任务,我们只需要将上面的功能加进去便可以了:
// lib/task/JobeetCleanupTask.class.php protected function execute($arguments = array(), $options = array()) { $databaseManager = new sfDatabaseManager($this->configuration); // cleanup Lucene index $index = JobeetJobPeer::getLuceneIndex(); $criteria = new Criteria(); $criteria->add(JobeetJobPeer::EXPIRES_AT, time(), Criteria::LESS_THAN); $jobs = JobeetJobPeer::doSelect($criteria); foreach ($jobs as $job) { if ($hit = $index->find('pk:'.$job->getId())) { $hit->delete(); } } $index->optimize(); $this->logSection('lucene', 'Cleaned up and optimized the job index'); // Remove stale jobs $nb = JobeetJobPeer::cleanup($options['days']); $this->logSection('propel', sprintf('Removed %d stale jobs', $nb)); }
上面的工作从索引中移除所有过期的招聘信息,并进行优化,感谢Zend Lucene内建的optimize()优化方法。
See you Tomorrow
今天我们花不到一个小时时间完成了一个完整的搜索引擎。每当你想添加一个新功能时,看一下是不是其它什么地方已经解决了这个问题。首先,看一看是不
symfony framework已经有这样的功能。同时不要忘记查看一下
symfony plugins. And don’t forget
to check the Zend Framework libraries
和ezComponent。
明天我们将使用非侵入式JavaScript代码,提高搜索引擎的反应能力。根据用户输入内容,实时更新搜索结果。当然也会适时的讲解如何在symfony中使用AJAX。
Categories: Jobeet 中文版
发表评论
-
Zend Framework 教程大全(英文版)
2009-02-22 23:05 2532Zend Framework教程大全 07月 3rd, 200 ... -
Zend Framework 1.7.5发布增加了不少功能
2009-02-19 21:43 1405Welcome to Zend Framework 1.7 ... -
lucene的简单实例<一>
2009-02-17 15:15 1074说明一下,这一篇文章的用到的lucene,是用2.0版本的,主 ... -
走进全文搜索
2009-02-17 14:07 1007走进全文搜索 http://www.phpx.com/hap ... -
Zend Search Lucene实现全文搜索收藏
2009-02-17 10:24 1871Zend Search Lucene实现全文搜索收藏 新一篇 ... -
DBSight-Zend Framework中lucene的最佳替代方案
2009-02-17 10:22 2356DBSight-Zend Framework中lucene的最 ... -
理解ZEND_DB_PROFILE相关的知识
2009-02-14 00:46 978MYSQL的性能跟踪mysql> help profil ... -
zend framwork quickstart 列子SQLLITE介绍
2009-02-13 01:54 1215SQLLITE 1、SQLite简介SQLite第一个Al ... -
ZF HEADLINK相关的HTML link标签 rel 属性
2009-02-12 21:04 2262rel 属性 -- rel属性,描述了当前页面与href所指定 ... -
php 目录结构学习
2009-02-11 21:59 0d:/docroot/ index.phpapplica ... -
zend framework中的helper们
2009-02-10 15:21 22112008-11-06 00:28 写 ... -
使用zend Framework的lucene进行全文检索——中文分词
2009-02-09 16:05 1965[2007/06/16 21:52 | 分类: PHP高级技 ... -
Zend Framework实例教程2
2009-02-09 10:00 2320最后,admin.php模板可以用来批准新闻条目: &l ... -
Zend Framework实例教程
2009-02-09 09:17 2718作者:张佳(译) 来源:PHPEye开源社区 20 ... -
Zend Framework 留言本实战
2009-02-08 20:27 5817一、环境搭建和ZF安装 *[注] ... -
xampp下跑zendframe框架apache报pdo错
2009-02-08 16:47 2075今天在xampp下跑zendframe框架 链接数据库,遇到这 ... -
zend frawework 开源代码列子
2009-02-08 13:01 16381.zend frawework QUICK START ... -
国外主流PHP框架比较
2009-02-06 10:13 2303国外主流PHP框架比较 作者:heiyelure ...
相关推荐
**Symfony 4.2 Jobeet教程** Symfony是一款流行的PHP框架,被广泛用于构建高效、可维护的企业级Web应用程序。Jobeet是一个经典的教程项目,它通过创建一个在线招聘平台来教授Symfony框架的基础知识和最佳实践。在这...
这里的版本号4.2表示使用的是Symfony的第四个主要版本中的第二个次要版本。 描述中的"Symfony 4.0 Jobeet项目"可能是一个小误写,因为标题中提到的是4.2,但通常这意味着项目是基于Symfony 4系列的,可能从4.0开始...
有一天心血来潮,就Google了一些资源,于是就发现了这一些列不错的教程。教程中的内容和《Ruby on Rails Tutorial》差不多,都是以一个真是的案例来给大家讲解框架的使用,其中有教你怎样使用Sym
所有代码都是由我完成的,我没有在其他实现上作弊,因此代码可能会因您在其他 Jobeet2 存储库中找到的内容而异。 尽管如此,我还是尝试通过遵守 100% 的 Symfony2 编码标准来生成我能做到的最干净的代码。 代码将...
在Jobeet中,每个功能,如搜索职位、发布职位等,都有相应的控制器来处理。 3. **模型(Model)**:模型层负责处理数据,通常与数据库交互。Jobeet的模型类定义了对数据库操作的方法,如添加、更新、删除职位。 4....
根据提供的文件信息,我们可以得知这是一份关于Symfony2 Jobeet项目第三天的内容,主要讨论的是数据模型。以下是相关知识点的详细说明: ### Symfony2 Jobeet项目第三天内容概述 Symfony2 Jobeet是基于Symfony2框架...
同时,Jobeet的前端部分展示给求职者的职位信息清晰直观,便于求职者根据自己的需求进行搜索和筛选。 总结来说,Symfony2 Jobeet项目不仅展示了Symfony2框架如何用于构建复杂的Web应用程序,还提供了一个实际的案例...
通过逐步创建一个博客引擎——Jobeet项目,读者将深入理解Symfony的工作原理,并掌握其在Web开发中的应用。 在项目开始前,我们强调了安全、验证、错误处理和测试的重要性,这些都是任何商业级应用程序不可或缺的...
【标题】"jobeetGit" 提示我们这个项目可能与 Jobeet 相关,Jobeet 是一个常见的 PHP 教程项目,用于教授 Web 开发者如何使用 PHP 和相关技术来创建一个在线招聘网站。"Git" 表明项目使用了 Git 进行版本控制,意味...