自定義中文全文索引

一、中文分詞插件

NEO4J中文全文索引,分詞組件使用IKAnalyzer。爲了支持高版本LUCENE,IKAnalyzer需要做一些調整。

IKAnalyzer-3.0 舊版本實現參考

ELASTICSEARCH-IKAnlyzer 高版本實現參考

1、分詞組件的調整

調整之後的分詞組件 casia.isiteam.zdr.wltea

// 調整之後的實現
public final class IKAnalyzer extends Analyzer {

    // 默認細粒度切分 true-智能切分 false-細粒度切分
    private Configuration configuration = new Configuration(false);

    /**
     * IK分詞器Lucene  Analyzer接口實現類
     * <p>
     * 默認細粒度切分算法
     */
    public IKAnalyzer() {
    }

    /**
     * IK分詞器Lucene Analyzer接口實現類
     *
     * @param configuration IK配置
     */
    public IKAnalyzer(Configuration configuration) {
        super();
        this.configuration = configuration;
    }

    /**
     * 重載Analyzer接口,構造分詞組件
     */
    @Override
    protected TokenStreamComponents createComponents(String fieldName) {
        Tokenizer _IKTokenizer = new IKTokenizer(configuration);
        return new TokenStreamComponents(_IKTokenizer);
    }

}

2、分詞測試

自定義分詞函數

RETURN zdr.index.iKAnalyzer('復聯終章快上映了好激動,據說知識圖譜與人工智能技術應用到了那部電影!吖啶基氨基甲烷磺酰甲氧基苯胺是一種藥嘛?',true) AS words

在這裏插入圖片描述

 /**
     * @param text:待分詞文本
     * @param useSmart:true 用智能分詞,false 細粒度分詞
     * @return
     * @Description: TODO(支持中英文本分詞)
     */
    @UserFunction(name = "zdr.index.iKAnalyzer")
    @Description("Fulltext index iKAnalyzer - RETURN zdr.index.iKAnalyzer({text},true) AS words")
    public List<String> iKAnalyzer(@Name("text") String text, @Name("useSmart") boolean useSmart) {

        PropertyConfigurator.configureAndWatch("dic" + File.separator + "log4j.properties");
        Configuration cfg = new Configuration(useSmart);

        StringReader input = new StringReader(text.trim());
        IKSegmenter ikSegmenter = new IKSegmenter(input, cfg);

        List<String> results = new ArrayList<>();
        try {
            for (Lexeme lexeme = ikSegmenter.next(); lexeme != null; lexeme = ikSegmenter.next()) {
                results.add(lexeme.getLexemeText());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return results;
    }

二、樣例數據準備

# 構造樣例數據
MERGE (a:Loc {name:'A'}) SET a.description='復聯終章快上映了好激動,據說知識圖譜與人工智能技術應用到了那部電影!吖啶基氨基甲烷磺酰甲氧基苯胺是一種藥嘛?'
MERGE (b:Loc {name:'B'}) SET b.description='復聯終章快上映了好激動,據說知識圖譜與人工智能技術應用到了那部電影!吖啶基氨基甲烷磺酰甲氧基苯胺是一種藥嘛?'
MERGE (c:Loc {name:'C'}) SET c.description='復聯終章快上映了好激動,據說知識圖譜與人工智能技術應用到了那部電影!吖啶基氨基甲烷磺酰甲氧基苯胺是一種藥嘛?'
MERGE (d:Loc {name:'D'}) SET d.description='復聯終章快上映了好激動,據說知識圖譜與人工智能技術應用到了那部電影!吖啶基氨基甲烷磺酰甲氧基苯胺是一種藥嘛?'
MERGE (e:Loc {name:'E'}) SET e.description='復聯終章快上映了好激動,據說知識圖譜與人工智能技術應用到了那部電影!吖啶基氨基甲烷磺酰甲氧基苯胺是一種藥嘛?'
MERGE (f:Loc {name:'F'}) SET f.description='復聯終章快上映了好激動,據說知識圖譜與人工智能技術應用到了那部電影!吖啶基氨基甲烷磺酰甲氧基苯胺是一種藥嘛?'
MERGE (a)-[:ROAD {cost:50}]->(b)
MERGE (a)-[:ROAD {cost:50}]->(c)
MERGE (a)-[:ROAD {cost:100}]->(d)
MERGE (b)-[:ROAD {cost:40}]->(d)
MERGE (c)-[:ROAD {cost:40}]->(d)
MERGE (c)-[:ROAD {cost:80}]->(e)
MERGE (d)-[:ROAD {cost:30}]->(e)
MERGE (d)-[:ROAD {cost:80}]->(f)
MERGE (e)-[:ROAD {cost:40}]->(f);

三、通過中文全文分詞組件創建節點索引

自定義創建索引過程

CALL zdr.index.addChineseFulltextIndex('IKAnalyzer', 'Loc', ['description']) YIELD message RETURN message

在這裏插入圖片描述

 @Procedure(value = "zdr.index.addChineseFulltextIndex", mode = Mode.WRITE)
    @Description("CALL zdr.index.addChineseFulltextIndex(String indexName, String labelName, List<String> propKeys) YIELD message RETURN message," +
            "爲一個標籤下的所有節點的指定屬性添加索引")
    public Stream<NodeIndexMessage> addChineseFulltextIndex(@Name("indexName") String indexName,
                                                            @Name("labelName") String labelName, @Name("properties") List<String> propKeys) {
        Label label = Label.label(labelName);

        List<NodeIndexMessage> output = new ArrayList<>();

//        // 按照標籤找到該標籤下的所有節點
        ResourceIterator<Node> nodes = db.findNodes(label);
        System.out.println("nodes:" + nodes.toString());

        int nodesSize = 0;
        int propertiesSize = 0;
        while (nodes.hasNext()) {
            nodesSize++;
            Node node = nodes.next();
            System.out.println("current nodes:" + node.toString());

            // 每個節點上需要添加索引的屬性
            Set<Map.Entry<String, Object>> properties = node.getProperties(propKeys.toArray(new String[0])).entrySet();
            System.out.println("current node properties" + properties);

            // 查詢該節點是否已有索引,有的話刪除
            Index<Node> index = db.index().forNodes(indexName, FULL_INDEX_CONFIG);
            System.out.println("current node index" + index);
            index.remove(node);

            // 爲了該節點的每個需要添加索引的屬性添加全文索引
            for (Map.Entry<String, Object> property : properties) {
                propertiesSize++;
                index.add(node, property.getKey(), property.getValue());
            }
        }

        String message = "IndexName:" + indexName + ",LabelName:" + labelName + ",NodesSize:" + nodesSize + ",PropertiesSize:" + propertiesSize;
        NodeIndexMessage indexMessage = new NodeIndexMessage(message);
        output.add(indexMessage);
        return output.stream();
    }

四、中文分詞索引查詢

自定義查詢索引過程

CALL zdr.index.chineseFulltextIndexSearch('IKAnalyzer', 'description:吖啶基氨基甲烷磺酰甲氧基苯胺', 100) YIELD node RETURN node

在這裏插入圖片描述

CALL zdr.index.chineseFulltextIndexSearch('IKAnalyzer', 'description:復聯* AND year:1999', 100) YIELD node,weight RETURN node.name,node.year,node.description,weight

在這裏插入圖片描述

  @Procedure(value = "zdr.index.chineseFulltextIndexSearch", mode = Mode.WRITE)
    @Description("CALL zdr.index.chineseFulltextIndexSearch(String indexName, String query, long limit) YIELD node RETURN node," +
            "執行LUCENE全文檢索,返回前{limit個結果}")
    public Stream<ChineseHit> chineseFulltextIndexSearch(@Name("indexName") String indexName,
                                                         @Name("query") String query, @Name("limit") long limit) {
        if (!db.index().existsForNodes(indexName)) {
            log.debug("如果索引不存在則跳過本次查詢:`%s`", indexName);
            return Stream.empty();
        }
        return db.index()
                .forNodes(indexName, FULL_INDEX_CONFIG)
                .query(new QueryContext(query).sortByScore().top((int) limit))
                .stream()
                .map(ChineseHit::new);  // provider
    }

【跨標籤類型檢索】使用addChineseFulltextIndex給標籤下節點屬性添加的索引,默認可以使用chineseFulltextIndexSearch合併檢索出來

// 增加一個非Loc標籤的節點,然後使用檢索
CREATE (n:LocProvince {name:'P'})  SET n.description='復聯終章快上映了好激動,據說知識圖譜與人工智能技術應用到了那部電影!' RETURN n
// 節點增加索引(索引名與已有相同)
CALL zdr.index.addChineseFulltextIndex('IKAnalyzer', 'LocProvince', ['description','year']) YIELD message RETURN message
// 通過屬性檢索節點
CALL zdr.index.chineseFulltextIndexSearch('IKAnalyzer', 'description:復聯', 100) YIELD node,weight RETURN node

在這裏插入圖片描述

五、總結

上述NEO4J中文全文索引解決方法,索引不會自動更新,修改節點屬性以及新增節點時都需要重新建立索引。
NEO4J默認索引實現參考:neo4j-lucene-index
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章