• Neo4j中實現自定義中文全文索引


    資料庫檢索效率時,一般首要優化途徑是從索引入手,然後根據需求再考慮更復雜的負載均衡、讀寫分離和分散式水平/垂直分庫/表等手段;索引通過資訊冗餘來提高檢索效率,其以空間換時間並會降低資料寫入的效率,因此對索引欄位的選擇非常重要。

    Neo4j可對指定Label的Node Create Index,當新增/更新符合條件的Node屬性時,Index會自動更新。Neo4j Index預設採用Lucene實現(可定製,如Spatial Index自定義實現的RTree索引),但預設新建的索引只支援精確匹配(get),模糊查詢(query)的話需要以全文索引,控制Lucene後臺的分詞行為。

    Neo4j全文索引預設的分詞器是針對西方語種的,如預設的exact查詢採用的是lucene KeywordAnalyzer(關鍵詞分詞器),fulltext查詢採用的是 white-space tokenizer(空格分詞器),大小寫什麼的對中文沒啥意義;所以針對中文分詞需要掛一箇中文分詞器,如IK Analyzer,Ansj,至於類似樑廠長家的基於深度學習的分詞系統pullword,那就更厲害啦。

    本文以常用的IK Analyzer分詞器為例,介紹如何在Neo4j中對欄位新建全文索引實現模糊查詢。

    IKAnalyzer分詞器

    IKAnalyzer是一個開源的,基於java語言開發的輕量級的中文分詞工具包。

    IKAnalyzer3.0特性

    • 採用了特有的“正向迭代最細粒度切分演算法“,支援細粒度和最大詞長兩種切分模式;具有83萬字/秒(1600KB/S)的高速處理能力。

    • 採用了多子處理器分析模式,支援:英文字母、數字、中文詞彙等分詞處理,相容韓文、日文字元優化的詞典儲存,更小的記憶體佔用。支援使用者詞典擴充套件定義

    • 針對Lucene全文檢索優化的查詢分析器IKQueryParser(作者吐血推薦);引入簡單搜尋表示式,採用歧義分析演算法優化查詢關鍵字的搜尋排列組合,能極大的提高Lucene檢索的命中率。
      IK Analyser目前還沒有maven庫,還得自己手動下載install到本地庫,下次空了自己在github做一個maven私有庫,上傳這些maven central庫裡面沒有的工具包。

    IKAnalyzer自定義使用者詞典

    詞典檔案

    自定義詞典字尾名為.dic的詞典檔案,必須使用無BOM的UTF-8編碼儲存的檔案。

    詞典配置

    詞典和IKAnalyzer.cfg.xml配置檔案的路徑問題,IKAnalyzer.cfg.xml必須在src根目錄下。詞典可以任意放,但是在IKAnalyzer.cfg.xml裡要配置對。如下這種配置,ext.dic和stopword.dic應當在同一目錄下。

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
    <properties>  
    <comment>IK Analyzer 擴充套件配置</comment>
    <!--使用者可以在這裡配置自己的擴充套件字典 -->
    <entry key="ext_dict">/ext.dic;</entry>
    <!--使用者可以在這裡配置自己的擴充套件停止詞字典-->
    <entry key="ext_stopwords">/stopword.dic</entry>
    </properties>

    Neo4j全文索引構建

    指定IKAnalyzer作為luncene分詞的analyzer,並對所有Node的指定屬性新建全文索引

      @Override
    public void createAddressNodeFullTextIndex () {
    try (Transaction tx = graphDBService.beginTx()) {
    IndexManager index = graphDBService.index();
    Index<Node> addressNodeFullTextIndex =
    index.forNodes( "addressNodeFullTextIndex", MapUtil.stringMap(IndexManager.PROVIDER, "lucene", "analyzer", IKAnalyzer.class.getName()));
    ResourceIterator<Node> nodes = graphDBService.findNodes(DynamicLabel.label( "AddressNode"));
    while (nodes.hasNext()) {
    Node node = nodes.next();
    //對text欄位新建全文索引
    Object text = node.getProperty( "text", null);
    addressNodeFullTextIndex.add(node, "text", text);
    }
    tx.success();
    }
    }

    Neo4j全文索引測試

    對關鍵詞(如’有限公司’),多關鍵詞模糊查詢(如’蘇州 教育 公司’)預設都能檢索,且檢索結果按關聯度已排好序。

    package uadb.tr.neodao.test;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.neo4j.graphdb.GraphDatabaseService;
    import org.neo4j.graphdb.Node;
    import org.neo4j.graphdb.Transaction;
    import org.neo4j.graphdb.index.Index;
    import org.neo4j.graphdb.index.IndexHits;
    import org.neo4j.graphdb.index.IndexManager;
    import org.neo4j.helpers.collection.MapUtil;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.wltea.analyzer.lucene.IKAnalyzer;
    import com.lt.uadb.tr.entity.adtree.AddressNode;
    import com.lt.util.serialize.JsonUtil;
    /**
    * AddressNodeNeoDaoTest
    *
    * @author geosmart
    */
    @RunWith(SpringJUnit4ClassRunner. class)
    @ContextConfiguration(locations = { "classpath:app.neo4j.cfg.xml" })
    public class AddressNodeNeoDaoTest {
    @Autowired
    GraphDatabaseService graphDBService;
    @Test
    public void test_selectAddressNodeByFullTextIndex() {
    try (Transaction tx = graphDBService.beginTx()) {
    IndexManager index = graphDBService.index();
    Index<Node> addressNodeFullTextIndex = index.forNodes("addressNodeFullTextIndex" ,
    MapUtil. stringMap(IndexManager.PROVIDER, "lucene", "analyzer" , IKAnalyzer.class.getName()));
    IndexHits<Node> foundNodes = addressNodeFullTextIndex.query("text" , "蘇州 教育 公司" );
    for (Node node : foundNodes) {
    AddressNode entity = JsonUtil.ConvertMap2POJO(node.getAllProperties(), AddressNode. class, false, true);
    System. out.println(entity.getAll地址實全稱());
    }
    tx.success();
    }
    }
    }

    CyperQL中使用自定義全文索引查詢

    正則查詢

    profile  
    match (a:AddressNode{ruleabbr:'TOW',text:'唯亭鎮'})<-[r:BELONGTO]-(b:AddressNode{ruleabbr:'STR'})
    where b.text=~ '金陵.*'
    return a,b

    全文索引查詢

    profile
    START b=node:addressNodeFullTextIndex("text:金陵*")
    match (a:AddressNode{ruleabbr:'TOW',text:'唯亭鎮'})<-[r:BELONGTO]-(b:AddressNode)
    where b.ruleabbr='STR'
    return a,b

    LegacyIndex中建立聯合exact和fulltext索引

    對label為AddressNode的節點,根據節點屬性ruleabbr的分類addressnode_fulltext_index(省->市->區縣->鄉鎮街道->街路巷/物業小區)/addressnode_exact_index(門牌號->樓幢號->單元號->層號->戶室號),對屬性text分別建不同型別的索引

    profile
    START a=node:addressnode_fulltext_index("text:商業街"),b=node:addressnode_exact_index("text:二期19")
    match (a:AddressNode{ruleabbr:'STR'})-[r:BELONGTO]-(b:AddressNode{ruleabbr:'TAB'})
    return a,b limit 10

    相關文章

    原文地址:https://codertw.com/%e8%b3%87%e6%96%99%e5%ba%ab/10006/
  • 相关阅读:
    第24章、OnLongClickListener长按事件(从零开始学Android)
    第21章、OnItemSelectedListener事件(从零开始学Android)
    GIS开源软件大全
    How to configure SRTM elevations in WorldWind WMS
    [转]SRTM、ASTER GDEM等全球数字高程数据(DEM)下载方式简介
    SRTM数据介绍与说明
    [转]基于四叉树(QuadTree)的LOD地形实现
    [转]World Wind学习总结一
    [转]World Wind Java开发之四——搭建本地WMS服务器
    [转]World Wind Java开发之五——读取本地shp文件
  • 原文地址:https://www.cnblogs.com/jpfss/p/11412649.html
Copyright © 2020-2023  润新知