一、中文分詞插件
NEO4J中文全文索引,分詞組件使用IKAnalyzer。爲了支持高版本LUCENE,IKAnalyzer需要做一些調整。
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