【NEO4J数据库】编写自定义过程/函数
1.NEO4J 过程:使用CALL语句调用,可以选择添加参数,大多数过程返回固定列的记录流。例如,调用数据库的内嵌过程,列出数据库的所有标签CALL db.labels2.NEO4J 可以编写自定义过程。步骤为:1)编写类和方法。关键点:a、对方法使用@Procedure 注解 b、方法的返回值是Stream<T>package example;import java
·
1.NEO4J 过程:使用CALL语句调用,可以选择添加参数,大多数过程返回固定列的记录流。例如,调用数据库的内嵌过程,列出数据库的所有标签
CALL db.labels
2.NEO4J 可以编写自定义过程。步骤为:
1)编写类和方法。关键点:a、对方法使用@Procedure 注解 b、方法的返回值是Stream<T>
package example;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexManager;
import org.neo4j.logging.Log;
import org.neo4j.procedure.Context;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.PerformsWrites;
import org.neo4j.procedure.Procedure;
import static org.neo4j.helpers.collection.MapUtil.stringMap;
import static org.neo4j.procedure.Procedure.Mode.SCHEMA;
import static org.neo4j.procedure.Procedure.Mode.WRITE;
/**
* This is an example showing how you could expose Neo4j's full text indexes as
* two procedures - one for updating indexes, and one for querying by label and
* the lucene query language.
*/
public class FullTextIndex
{
// Only static fields and @Context-annotated fields are allowed in
// Procedure classes. This static field is the configuration we use
// to create full-text indexes.
private static final Map<String,String> FULL_TEXT =
stringMap( IndexManager.PROVIDER, "lucene", "type", "fulltext" );
// This field declares that we need a GraphDatabaseService
// as context when any procedure in this class is invoked
@Context
public GraphDatabaseService db;
// This gives us a log instance that outputs messages to the
// standard log, `neo4j.log`
@Context
public Log log;
/**
* This declares the first of two procedures in this class - a
* procedure that performs queries in a manual index.
*
* It returns a Stream of Records, where records are
* specified per procedure. This particular procedure returns
* a stream of {@link SearchHit} records.
*
* The arguments to this procedure are annotated with the
* {@link Name} annotation and define the position, name
* and type of arguments required to invoke this procedure.
* There is a limited set of types you can use for arguments,
* these are as follows:
*
* <ul>
* <li>{@link String}</li>
* <li>{@link Long} or {@code long}</li>
* <li>{@link Double} or {@code double}</li>
* <li>{@link Number}</li>
* <li>{@link Boolean} or {@code boolean}</li>
* <li>{@link org.neo4j.graphdb.Node}</li>
* <li>{@link org.neo4j.graphdb.Relationship}</li>
* <li>{@link org.neo4j.graphdb.Path}</li>
* <li>{@link java.util.Map} with key {@link String} and value of any type in this list, including {@link java.util.Map}</li>
* <li>{@link java.util.List} of elements of any valid field type, including {@link java.util.List}</li>
* <li>{@link Object}, meaning any of the types above</li>
*
* @param label the label name to query by
* @param query the lucene query, for instance `name:Brook*` to
* search by property `name` and find any value starting
* with `Brook`. Please refer to the Lucene Query Parser
* documentation for full available syntax.
* @return the nodes found by the query
*/
@Procedure( name = "example.search", mode = WRITE )
public Stream<SearchHit> search( @Name("label") String label,
@Name("query") String query )
{
String index = indexName( label );
// Avoid creating the index, if it's not there we won't be
// finding anything anyway!
if( !db.index().existsForNodes( index ))
{
// Just to show how you'd do logging
log.debug( "Skipping index query since index does not exist: `%s`", index );
return Stream.empty();
}
// If there is an index, do a lookup and convert the result
// to our output record.
return db.index()
.forNodes( index )
.query( query )
.stream()
.map( SearchHit::new );
}
/**
* This is the second procedure defined in this class, it is used to update the
* index with nodes that should be queryable. You can send the same node multiple
* times, if it already exists in the index the index will be updated to match
* the current state of the node.
*
* This procedure works largely the same as {@link #search(String, String)},
* with three notable differences. One, it is annotated with `mode = SCHEMA`,
* which is <i>required</i> if you want to perform updates to the graph in your
* procedure.
*
* Two, it returns {@code void} rather than a stream. This is simply a short-hand
* for saying our procedure always returns an empty stream of empty records.
*
* Three, it uses a default value for the property list, in this way you can call
* the procedure by simply invoking {@code CALL index(nodeId)}. Default values are
* are provided as the Cypher string representation of the given type, e.g.
* {@code {default: true}}, {@code null}, or {@code -1}.
*
* @param nodeId the id of the node to index
* @param propKeys a list of property keys to index, only the ones the node
* actually contains will be added
*/
@Procedure( name = "example.index", mode = SCHEMA )
public void index( @Name("nodeId") long nodeId,
@Name(value = "properties", defaultValue = "[]") List<String> propKeys )
{
Node node = db.getNodeById( nodeId );
// Load all properties for the node once and in bulk,
// the resulting set will only contain those properties in `propKeys`
// that the node actually contains.
Set<Map.Entry<String,Object>> properties =
node.getProperties( propKeys.toArray( new String[0] ) ).entrySet();
// Index every label (this is just as an example, we could filter which labels to index)
for ( Label label : node.getLabels() )
{
Index<Node> index = db.index().forNodes( indexName( label.name() ), FULL_TEXT );
// In case the node is indexed before, remove all occurrences of it so
// we don't get old or duplicated data
index.remove( node );
// And then index all the properties
for ( Map.Entry<String,Object> property : properties )
{
index.add( node, property.getKey(), property.getValue() );
}
}
}
/**
* This is the output record for our search procedure. All procedures
* that return results return them as a Stream of Records, where the
* records are defined like this one - customized to fit what the procedure
* is returning.
*
* The fields must be one of the following types:
*
* <ul>
* <li>{@link String}</li>
* <li>{@link Long} or {@code long}</li>
* <li>{@link Double} or {@code double}</li>
* <li>{@link Number}</li>
* <li>{@link Boolean} or {@code boolean}</li>
* <li>{@link org.neo4j.graphdb.Node}</li>
* <li>{@link org.neo4j.graphdb.Relationship}</li>
* <li>{@link org.neo4j.graphdb.Path}</li>
* <li>{@link java.util.Map} with key {@link String} and value {@link Object}</li>
* <li>{@link java.util.List} of elements of any valid field type, including {@link java.util.List}</li>
* <li>{@link Object}, meaning any of the valid field types</li>
* </ul>
*/
public static class SearchHit
{
// This records contain a single field named 'nodeId'
public long nodeId;
public SearchHit( Node node )
{
this.nodeId = node.getId();
}
}
private String indexName( String label )
{
return "label-" + label;
}
}
2) 将类和相关的引用包打包为jar包。把jar包放在Neo4j的$Home/plugins下,如果没有任何的设置请放在安装的目录plugins下,如果有单独设置,要设置在配置的plugins下面。
3) 重启NEO4J数据库。输入cypher 语句
CALL dbms.procedures()
查看编写的过程
"example.index" "example.index(nodeId :: INTEGER?, properties = [] :: LIST? OF STRING?) :: VOID" ""
"example.search" "example.search(label :: STRING?, query :: STRING?) :: (nodeId :: INTEGER?)" ""
NEO4J自定义函数和过程的编写类似,但使用的是@UserFunction注解。
注:NEO4J版本v3.3 ,JAVA 1.8

GitCode 天启AI是一款由 GitCode 团队打造的智能助手,基于先进的LLM(大语言模型)与多智能体 Agent 技术构建,致力于为用户提供高效、智能、多模态的创作与开发支持。它不仅支持自然语言对话,还具备处理文件、生成 PPT、撰写分析报告、开发 Web 应用等多项能力,真正做到“一句话,让 Al帮你完成复杂任务”。
更多推荐
所有评论(0)