Blog chia sẻ và thảo luận về IT|Programming Language|Search Engine Optimization|Data Base|Linux| ...

datnguyen

Zend Search Lucene (ex1)

+ No comment yet

With Zend_Search_Lucene you can build a search function on your web page. The difference between searching via MySQL like most websites do is that you have to create an index and keep it up to date.

Create index folder

Create a folder where you store your search index and set write rights. In this tutorial we use the path /application/data/search of our Zend application.


Create controller

Create the controller for our functionality.
<?php

class SearchController extends Zend_Controller_Action
{
    protected $_indexPath = '../application/data/search';
    
    public function buildindexAction()
    {
        
    }
    
    public function searchAction()
    {
        
    }
}

Set the path to the search storage in a protected variable. We implement a buildindexAction where the index will be build and a searchAction where the index will be searched. 

Build index

You can build a Zend_Search_Lucene index with a lot of document types like websites, word documents etc. The most websites use a SQL database to store their data. In Zend you normally use models so that our controller has not to know where the data comes from. I assume you have a working Zend project and can get users or any data to index.

<?php

class SearchController extends Zend_Controller_Action
{
    protected $_indexPath = '../application/data/search';
    
    public function buildindexAction()
    {
        Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num());

        /**
         * Create index
         */
        $index = Zend_Search_Lucene::create($this->_indexPath);

        /**
         * Get all users
         */
        $mapper = new User_Mapper();
        $users = $mapper->fetchAll();

        /**
         * Create a document for each user and add it to the index
         */
        foreach ($users as $user) {
            $doc = new Zend_Search_Lucene_Document();
            
            /**
             * Fill document with data
             */
            $doc->addField(Zend_Search_Lucene_Field::keyword('userId', $user->getId(), 'UTF-8'));
            $doc->addField(Zend_Search_Lucene_Field::keyword('username', $user->getUsername(), 'UTF-8'));
            $doc->addField(Zend_Search_Lucene_Field::keyword('birthday', $user->getBirthday(), 'UTF-8'));
            $doc->addField(Zend_Search_Lucene_Field::keyword('city', $user->getData()->getCity(), 'UTF-8'));
            $doc->addField(Zend_Search_Lucene_Field::keyword('zodiac', $user->getZodiac(), 'UTF-8'));
            $doc->addField(Zend_Search_Lucene_Field::keyword('age', $user->getAge()), 'UTF-8');
            
            /**
             * Add document
             */
            $index->addDocument($doc);
        }

        $index->optimize();
    }
    
    public function searchAction()
    {
        
    }
}

In the buildindexAction first we set our index to utf8. Then we create it with our path. After creating we load our data we want to index. We go through every record and create a Zend_Search_Lucene_Document for it. This document we can fill with different data. I only used keyword as field type. There are a few other field types you can also use that have different behaviour. You can read more about field types in the Zend documentation. Finally we add the document to the index. After adding every record to the index we optimize the index.

Now you can call http://yourdomain.com/search/buildindex to create the index or configure a cron job that creates the index every day. You could also put the functionality in a class and build the index only if something changes in the user data.

Search index


Zend Search Lucene has it's own query language. You can read detailled information about the query language in thedocumentation. You should build your query to your needs. In this tutorial we use the POST variables to build the query.
<?php

class SearchController extends Zend_Controller_Action
{
    protected $_indexPath = '../application/data/search';
    
    public function buildindexAction()
    {
        Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num());

        /**
         * Create index
         */
        $index = Zend_Search_Lucene::create($this->_indexPath);

        /**
         * Get all users
         */
        $mapper = new User_Mapper();
        $users = $mapper->fetchAll();

        /**
         * Create a document for each user and add it to the index
         */
        foreach ($users as $user) {
            $doc = new Zend_Search_Lucene_Document();
            
            /**
             * Fill document with data
             */
            $doc->addField(Zend_Search_Lucene_Field::keyword('userId', $user->getId(), 'UTF-8'));
            $doc->addField(Zend_Search_Lucene_Field::keyword('username', $user->getUsername(), 'UTF-8'));
            $doc->addField(Zend_Search_Lucene_Field::keyword('birthday', $user->getBirthday(), 'UTF-8'));
            $doc->addField(Zend_Search_Lucene_Field::keyword('city', $user->getData()->getCity(), 'UTF-8'));
            $doc->addField(Zend_Search_Lucene_Field::keyword('zodiac', $user->getZodiac(), 'UTF-8'));
            $doc->addField(Zend_Search_Lucene_Field::keyword('age', $user->getAge()), 'UTF-8');
            
            /**
             * Add document
             */
            $index->addDocument($doc);
        }

        $index->optimize();
    }
    
    public function searchAction()
    {
        if ($request->isPost()) {
            $post = $request->getPost();
            
            /**
             * Open index
             */
            $index = Zend_Search_Lucene::open($this->_indexPath);

            Zend_Search_Lucene_Search_Query_Wildcard::setMinPrefixLength(0);

            $query = 'username:"' . $post['username'] . '" OR city:"' . $post['city'] . '"';

            $this->view->result = $index->find($query);
        } else {
            $this->view->form = new Search_Form();
        }
    }
}
In your view script you can print the results in a foreach loop.
<?php foreach($this->result as $_user): ?>
    <?php echo $_user->username ?><br/>
<?php endforeach; ?>

Một vài nhận xét của những người  :

1. Theo tôi được biết, trên mạng họ xài Solr Lucene, 1 dạng mã nguồn mở với chức năng tìm kiếm thông minh, bạn có thể search google với từ khóa này
Còn về vấn đề Zend Search Lucene, tôi cũng đã dùng, nếu cài trên hosting, set timeout chỉ 30s, nên bạn sẽ không index được, cách tốt nhất là đưa về localhost index thôi (chỉnh thời gian trong php.ini cho phù hợp)
Về cách sử dụng khá đơn giản, lúc đầu mình cũng không biết cách dùng, nhưng bây giờ mình đã sử dụng được cả trên hosting linux, bạn có thể pm cho mình qua nick haitruonginfotech, mình sẽ hướng dẫn cho bạn ... thân.


2. Công nghệ mà tôi chọn cho project của mình là PHP, và tất nhiên phiên bản Lucene đầu tiên mà tôi thử nghiệm là phiên bản trên PHP. PHP Lucene là một phần của Zend Framework, gói Zend_Search_Lucene, đây là một bản port thuần PHP. Tôi hơi lo về performance của nó, bởi ai cũng biết PHP không được sinh ra để giải quyết các công việc nặng về logic. Nhưng mọi thứ không làm tôi thất vọng. Tôi thử index bộ manual của Zend Framework, nặng 40 MB, khoảng trên 3000 file HTML, tốn khoảng 5 phút. Chưa kể là trong lúc thử nghiệm, tôi còn nghe nhạc bằng Windows Media Player, viết tài liệu bằng Open Office Writer. Phần search cũng rất khá, mất trung bình 0.5 giây cho việc tìm kiếm từ khóa “Zend” trong đống dữ liệu trên, trả về hơn 1000 kết quả. Tuy nhiên một khuyết điểm rất lớn của Zend_Search_Lucene khiến tôi không thể sử dụng được nó là không hỗ trợ đầy đủ UTF-8. Dữ liệu bằng tiếng Việt bị hỏng khi index và không trả lại kết quả gì khi search, chưa kể còn sinh ra cả loạt lỗi Notice từ iconv(). Ngoài ra, PHP Lucene còn chưa hỗ trợ một số tính năng có ở bản Java Lucene như sort chẳng hạn.

3.  Tôi sử dụng Zend Search Lucene để tạo index, tuy nhiên không hiểu tại sao khi addDocument đến mục thứ 10.004 thì nó bị đơ, đứng im tại hàm addDocument, có phải là tràn 1 cái gì đó không? Mong các bác giúp đỡ cách khắc phục. Thông tin thêm: tổng lượng file cần index là 468MB (16.000 file), hiện giờ với 10.003 document thì chạy optimize() bị báo thiếu bộ nhớ (đã set memory_limit trong php.ini lên 128MB). Do tình hình server cùi bắp nên ko thể set thêm lên nữa.

4. Tôi khuyên bạn không nên sử dụng Zend_Search_Lucene ở thời điểm này, nó chưa ổn định, chưa hỗ trợ Unicode tốt và còn thiếu rất nhiều chức năng ! Với điều kiện bạn có nhiều server riêng phục vụ cho index/search thì có thể tham khảo Solr, một search server dựa trên Java Lucene. Nó chạy trên một server riêng với Tomcat và giao tiếp với phần còn lại của ứng dụng bằng REST. Tuy nhiên nó chỉ hỗ trợ một index / 1 cá thể Solr, nếu muốn nó linh hoạt hơn thì bạn có thể sửa code nó.
 
 

Nguồn : http://www.tutorial-portal.com/tutorial/show/id/19.
Download file Pdf : http://www.tutorial-portal.com/tutorial/pdf/id/19
Tham khảo http://forum.gocit.vn/threads/mysql-full-text-search.323/, http://kynangiphone.blogspot.com/2012/07/php-zend-search-lucene-tieng-viet.html
http://www.phpriot.com/articles/zend-search-lucene

Đăng nhận xét