MongoDB 全文搜索解決方案(lucene+IKAnalyzer)

jopen 12年前發布 | 100K 次閱讀 MongoDB 搜索引擎

mongodb 解決 全文搜索是個不小的問題

可以用 正則匹配 但是效率很低 往往到大數據量的搜索的時候就會出現 查詢超時等現象

當然也可以用官方的做法(在mongodb的文檔類型中加字段,存分詞結果,

然后從該字段中匹配) 但是我嘗試了 效率比原先的好像還要低
 

后來我嘗試了 lucene+IKAnalyzer 發現效率有所提升啊

原理:lucene 把大文本的數據 利用分詞器 在新建的索引文件中建立索引

取數據的時候從索引文件中取

取出mongodb 中的數據進行 索引的創建

package sample3;

import java.io.File;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriter.MaxFieldLength;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.wltea.analyzer.lucene.IKAnalyzer;

import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.Mongo;

/**
 * 創建索引
 * @author  zhanghaijun
 *
 */
public class Demo1 {
    public static void main(String[] args) throws Exception {
        //先在數據庫中拿到要創建索引的數據 
        Mongo mongo = new Mongo();
        DB db = mongo.getDB("zhang");
        DBCollection msg = db.getCollection("test3");
        DBCursor cursor = msg.find();
        //是否重新創建索引文件,false:在原有的基礎上追加
        boolean create = true;
        //創建索引
        Directory directory = FSDirectory.open(new File("E:\\lucene\\index"));
        Analyzer analyzer = new IKAnalyzer();//IK中文分詞器 
        IndexWriter indexWriter = new IndexWriter(directory,analyzer,MaxFieldLength.LIMITED);
        boolean exist = cursor.hasNext();
        while(exist){
            //System.out.println(cursor.next().get("text").toString());
            Document doc = new Document();
            Field fieldText = new Field("text",cursor.next().get("text").toString(),Field.Store.YES, 
                      Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS);
            doc.add(fieldText);
            indexWriter.addDocument(doc);
            exist = cursor.hasNext();
        }
        cursor = null;
        //optimize()方法是對索引進行優化
        indexWriter.optimize();     
        //最后關閉索引
        indexWriter.close();
    }
}

數據的查找(直接從索引文件中查找)

package sample3;

import java.io.File;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.FSDirectory;
import org.wltea.analyzer.lucene.IKAnalyzer;
import org.wltea.analyzer.lucene.IKQueryParser;
import org.wltea.analyzer.lucene.IKSimilarity;

/**
 * 查找索引
 */
public class Demo2 {
    public static void main(String[] args) throws Exception {
        // onlysearching, so read-only=true
        long starttime = System.currentTimeMillis();
        IndexReader reader =IndexReader.open(FSDirectory.open(new File("E:\\lucene\\index")),true); 
        IndexSearcher searcher = new IndexSearcher(reader);
        searcher.setSimilarity(new IKSimilarity());   //在索引器中使用IKSimilarity相似度評估器 
        //String[] keys = {"4","testtest"};      //關鍵字數組
        //String[] fields = {"id","title"};  //搜索的字段
        //BooleanClause.Occur[] flags = {BooleanClause.Occur.MUST,BooleanClause.Occur.MUST};    //BooleanClause.Occur[]數組,它表示多個條件之間的關系 
        //使用 IKQueryParser類提供的parseMultiField方法構建多字段多條件查詢
        //Query query = IKQueryParser.parseMultiField(fields,keys, flags);     //IKQueryParser多個字段搜索  
        Query query =IKQueryParser.parse("text","上海人");  //IK搜索單個字段       
        IKAnalyzer analyzer = new IKAnalyzer();
        //Query query =MultiFieldQueryParser.parse(Version.LUCENE_CURRENT, keys, fields, flags,analyzer);   //用MultiFieldQueryParser得到query對象   
        System.out.println("query:"+query.toString()); //查詢條件    
        /*TopScoreDocCollector topCollector = TopScoreDocCollector.create(searcher.maxDoc(), false);
        searcher.search(query,topCollector);

        ScoreDoc[] docs = topCollector.topDocs(3).scoreDocs;
        System.out.println(docs.length);*/

        /**
        *得到TopDocs對象之后,可以獲取它的成員變量totalHits和scoreDocs.這兩個成員變量的訪問權限是public的,所以可以直接訪問
        */
        TopDocs topDocs = searcher.search(query,1000001);
        Integer count = topDocs.totalHits;
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for(int i = 0;i<count;i++){
            ScoreDoc scoreDoc = scoreDocs[i];
            Document document = searcher.doc(scoreDoc.doc);
            document.get("text");
        }
        System.out.println("查找數據量:"+count);
        long endtime = System.currentTimeMillis();
        System.out.println(endtime-starttime);
        reader.close(); //關閉索引   
    }
}

//直接查找的代碼:

package sample3;

import java.net.UnknownHostException;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.MongoException;

public class Demo3 {
    public static void main(String[] args) throws Exception{
        Mongo mongo = new Mongo();
        DB db = mongo.getDB("zhang");
        DBCollection dbc = db.getCollection("test3");
        DBObject basicdb = new BasicDBObject();
        basicdb.put("$regex","上海人");
        basicdb.put("$options","");
        long startTime = System.currentTimeMillis();
        DBCursor cursor = dbc.find(new BasicDBObject("text",basicdb));
        int j =0;
        while(cursor.hasNext()){
            cursor.next().get("text");
            j++;
        }
        System.out.println("查找數據量"+j);
        long endTime = System.currentTimeMillis();
        System.out.println("未優化前:"+(endTime-startTime));
    }
}

測試結果

       從索引中找數據  

優化后的:
query:text:上海人 text:上海
查找數據量:1000001
查找用時:2917

      直接從mongodb 正則匹配的

查找數據量1000000
未優化前:6573

恩 速度還是有不少提高的

還有更好的解決方案嗎 求指導哦

 

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!