从neo4j官方网站http://neo4j.org/下载neo4j-apoc包,或者自己获取源码自行打包也可。
我使用的是java开发工具是Spring Tool Suite,新建一个spring web mvc项目,模板引擎使用velocity(velocity.apache.org),web.xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="attentiondb" version="2.5">
<display-name>attentiondb</display-name>
<!--
Enables clean URLs with JSP views e.g. /welcome instead of
/app/welcome
-->
<filter>
<filter-name>UrlRewriteFilter</filter-name>
<filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>UrlRewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Handles all requests into the application -->
<servlet>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/app-config.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<url-pattern>/attentiondb/*</url-pattern>
</servlet-mapping>
</web-app>
在/WEB-INF/spring/app-config.xml中配置neo4j bean(这里需要注意spring 2.5与3.0的区别,3.0中使用map元素代替util:map元素):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!--
Scans the classpath of this application for @Components to deploy as
beans
-->
<context:component-scan base-package="attentiondb" />
<bean id="graphDbService" class="org.neo4j.kernel.EmbeddedGraphDatabase"
init-method="enableRemoteShell" destroy-method="shutdown">
<constructor-arg index="0" value="d://attentiondb" />
<constructor-arg index="1">
<map>
<entry key="create" value="false"/>
<entry key="neostore.nodestore.db.mapped_memory" value="20M"/>
<entry key="neostore.propertystore.db.mapped_memory" value="90M"/>
<entry key="neostore.nodestore.db.mapped_memory" value="1M"/>
<entry key="neostore.nodestore.db.mapped_memory" value="1M"/>
<entry key="neostore.nodestore.db.mapped_memory" value="130M"/>
<entry key="neostore.nodestore.db.mapped_memory" value="100M"/>
</map>
</constructor-arg>
</bean>
<bean id="indexService" class="org.neo4j.index.lucene.LuceneIndexService"
destroy-method="shutdown">
<constructor-arg index="0" ref="graphDbService" />
</bean>
<bean id="velocityConfig"
class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
<property name="resourceLoaderPath" value="/WEB-INF/views/" />
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
<property name="cache" value="true" />
<property name="prefix" value="" />
<property name="suffix" value=".vm" />
</bean>
<!-- Application Message Bundle -->
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="/WEB-INF/messages/messages" />
<property name="cacheSeconds" value="0" />
</bean>
<!-- Configures Spring MVC -->
<import resource="mvc-config.xml" />
</beans>
原文地址:http://www.openfetion.com/category/neo4j.aspx
在spring中使用neo4j
neo4j处理好友关系
在实际业务中,我们经常遇到这样的场景,比如查找好友的好友。处理此类业务,neo4j体现出了方便快捷强大的功能。
比如我们有九个人:
Id Name
1 梁1
2 梁2
3 梁3
4 梁4
5 梁5
6 梁6
7 梁7
8 梁8
9 梁9
其中:梁1、梁2认识所有的人,梁9认识梁1、梁2、梁8
我们要实现查找梁9的好友的好友(不包括自身和直接好友)。
首先定义好友关系枚举:
public enum FriendRelationTypes implements RelationshipType {
KNOWS
}
创建上述好友关系graph database:
ArrayList<Node> nodes = new ArrayList<Node>(9);
Map<String, String> props = new HashMap<String, String>();
// props.put("create", "false");
GraphDatabaseService graphDb = new EmbeddedGraphDatabase("var/base",
props);
Transaction tx = graphDb.beginTx();
try {
for (int i = 0; i < 9; i++) {
Node node = graphDb.createNode();
node.setProperty("Id", i + 1);
node.setProperty("Name", "梁" + i);
nodes.add(node);
}
Node node1 = nodes.get(0);
Node node2 = nodes.get(1);
Node node9 = nodes.get(8);
for (int i = 2; i < 9; i++) {
Node node = nodes.get(i);
node.createRelationshipTo(node1, FriendRelationTypes.KNOWS);
node.createRelationshipTo(node2, FriendRelationTypes.KNOWS);
}
nodes.get(7).createRelationshipTo(node9, FriendRelationTypes.KNOWS);
tx.success();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
tx.finish();
graphDb.shutdown();
}
显示所有的好友,查找梁9的好友并显示:
Map<String, String> props = new HashMap<String, String>();
props.put("create", "false");
GraphDatabaseService graphDb = new EmbeddedGraphDatabase("var/base",
props);
Transaction tx = graphDb.beginTx();
try {
Iterable<Node> nodes = graphDb.getAllNodes();
for (Node node : nodes) {
if (node.hasProperty("Id")) {
System.out.println("NodeId: " + node.getId() + " Id: "
+ node.getProperty("Id") + " Name:"
+ node.getProperty("Name"));
}
}
Node node = graphDb.getNodeById(9);
Traverser friends = node.traverse(Traverser.Order.BREADTH_FIRST,
StopEvaluator.END_OF_GRAPH, new ReturnableEvaluator() {
public boolean isReturnableNode(TraversalPosition pos) {
System.out.println(pos.depth());
return pos.depth() != 1 && pos.notStartNode();
}
}, FriendRelationTypes.KNOWS, Direction.BOTH);
for (Node friend : friends) {
System.out.println("Id:" + friend.getProperty("Id") + " Name:"
+ friend.getProperty("Name"));
}
tx.success();
} finally {
tx.finish();
graphDb.shutdown();
}
在这里我们可以看到Traverser的简单应用。在neo4j查找中,需要先找到一个根节点,然后再这个根节点上创建Traverser来进行各种查询。
neo4j初体验
neo4j(http://neo4j.org/)一个开源的图数据库graph database。neo4j是基于磁盘文件、内嵌、支持java事务JTS,用于存储图结构数据的数据库引擎。具体的资料可以查看官方网站。
neo4j的存储结构中重点的就是Node(节点)和Relationship(关系)。Node与Node之间通过Relationship连接,Node与Node之间可以具有多种关系,但是两个Node不能相同。
下面通过一个具体的业务来演示一下neo4j的简单使用。
我们需要来存储用户与用户之间都有过什么关系,关系之间有什么样的权重,这样通过用户之间的关系和关系的权重,在此基础上我们可以分析用户之间的关注度。在数据中是这样存储的:
relation关系表,用来枚举用户之间可能存在的关系,及关系的自定义权重
1 发表文章评论 3
2 回复文章评论 1
3 喜欢文章 1
4 文章点名 50
5 留言 10
6 回复留言 3
7 评论留言飞语 5
8 回复留言飞语评论 5
9 收藏留言飞语 3
10 评论图片 3
11 回复图片评论 3
12 图片圈人 20
13 分享 20
14 站内信 2
15 回复站内信 2
graphs用户关系系表,存储两个用户之间都具有什么样的关系。有三个字段:BeginUserId开始用户Id、EndUserId结束用户Id、Reason原因。其中Reason是xml格式数据,记录了用户Id为EndUserId对用户Id为BeginUserId发生过什么行为,产生过什么样的关系,如1490578 18129607 21 <a t="3" e="1" /><a t="13" e="20" />,表明用户18129607对用户1490578曾经回复留言、喜欢文章,e可以次数以表示权重。
以下是如何通过neo4j来创建此业务的graph database,实际graphs表中有600万左右的数据,我们为了演示只取前1000条,并且只是通过neo4j kernel核心包来创建。
创建AttentionRelationTypes枚举,实现接口RelationshipType,这是必须的。节点直接的Relationship是通过RelationshipType的不同名称来定义并区分的。
package easynet.neo4j;
import org.neo4j.graphdb.RelationshipType;
/*
* 关注关系
*/
public enum AttentionRelationTypes implements RelationshipType {
PublishedComments, ReplyToComment, FavoriteArticle, ArticleMention, Message, ReplyToMessage, CommentOnMood, ReplyToMoodComments, FavoriteMood, CommentOnPicture, ReplyToPictureComments, CirclePicture, Share, SiteMessage, ReplyToSiteMessage
}
很重要的一点,由于neo4j是自己维护Node的Id,我们不能把UserId当作Node的Id,而只能作为一个Node的Property。但是因为我们在业务上需要保证作为用户Node的唯一性,所以创建一个用来维护UserId与NodeId的对应关系表:usernode(有两个字段UserId、NodeId)。定义一个类NodeStore用来分装此操作。
package easynet.neo4j;
import java.sql.*;
import net.sourceforge.jtds.jdbcx.*;
public class NodeStore {
private JtdsDataSource dataSource = null;
private String insertQuery = "{call [UserNode_Create](?,?)}";
private String getNodeIdQuery = "{call [UserNode_GetNodeIdByUserId](?)}";
public NodeStore(JtdsDataSource dataSource) {
this.dataSource = dataSource;
}
public void store(int userId, long nodeId) throws SQLException {
Connection con = dataSource.getConnection();
CallableStatement cst = con.prepareCall(insertQuery);
cst.setInt(1, userId);
cst.setLong(2, nodeId);
cst.execute();
cst.close();
}
public long getNodeIdByUserId(int userId) throws SQLException {
int nodeId = 0;
Connection con = dataSource.getConnection();
CallableStatement cst = con.prepareCall(getNodeIdQuery);
cst.setInt(1, userId);
ResultSet rs = cst.executeQuery();
while (rs.next()) {
nodeId = rs.getInt("NodeId");
}
cst.close();
return nodeId;
}
}
下面就是具体通过neo4j创建graph database的代码了:
package easynet.neo4j;
import java.io.*;
import java.sql.*;
import java.util.*;
import org.w3c.dom.*;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.xml.parsers.*;
import net.sourceforge.jtds.jdbcx.*;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.Node;
import org.neo4j.kernel.*;
import org.neo4j.kernel.impl.traversal.*;
public class AttentionRelationShell {
/**
* 处理关注关系
*
* @throws SQLException
*/
public static void main(String[] args) {
JtdsDataSource dataSource = new JtdsDataSource();
dataSource.setServerName("192.168.1.100");
dataSource.setPortNumber(1433);
dataSource.setDatabaseName("yadong_temp");
dataSource.setUser("sa");
dataSource.setPassword("devterry");
NodeStore nodeStore = new NodeStore(dataSource);
long beginTime = System.currentTimeMillis();
System.out.println("开始创建...");
Map<String, String> props = new HashMap<String, String>();
// props.put("create", "false");
GraphDatabaseService graphDb = new EmbeddedGraphDatabase("var/base",
props);
try {
Connection con = dataSource.getConnection();
Statement st = con.createStatement();
ResultSet rs = st
.executeQuery("SELECT [BeginUserId],[EndUserId],[Reason] FROM [graphs]");
while (rs.next()) {
int beginUserId = rs.getInt("BeginUserId");
int endUserId = rs.getInt("EndUserId");
String reason = rs.getString("Reason");
System.out.println("beginUserId:" + beginUserId
+ " endUserId:" + endUserId);
Node beginUserNode = null;
Node endUserNode = null;
Transaction tx = graphDb.beginTx();
try {
long beginNodeId = nodeStore.getNodeIdByUserId(beginUserId);
long endNodeId = nodeStore.getNodeIdByUserId(endUserId);
if (beginNodeId != 0) {
beginUserNode = graphDb.getNodeById(beginNodeId);
} else {
beginUserNode = graphDb.createNode();
beginUserNode.setProperty("UserId", beginUserId);
nodeStore.store(beginUserId, beginUserNode.getId());
}
if (endNodeId != 0) {
endUserNode = graphDb.getNodeById(endNodeId);
} else {
endUserNode = graphDb.createNode();
endUserNode.setProperty("UserId", endUserId);
nodeStore.store(endUserId, endUserNode.getId());
}
try {
DocumentBuilderFactory factory = DocumentBuilderFactory
.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
StringReader sr = new StringReader("<relation>"
+ reason + "</relation>");
InputSource is = new InputSource(sr);
Document doc = builder.parse(is);
NodeList nodes = doc.getElementsByTagName("a");
// 创建用户Node之间的Relationship
for (int i = 0; i < nodes.getLength(); i++) {
org.w3c.dom.Node node = nodes.item(i);
NamedNodeMap attributes = node.getAttributes();
int realtionTypeId = Integer.parseInt(attributes
.getNamedItem("t").getNodeValue());
int weight = Integer.parseInt(attributes
.getNamedItem("e").getNodeValue());
AttentionRelationTypes relationTypes = AttentionRelationTypes.PublishedComments;
switch (realtionTypeId) {
case 1:
relationTypes = AttentionRelationTypes.PublishedComments;
break;
case 2:
relationTypes = AttentionRelationTypes.ReplyToComment;
break;
case 3:
relationTypes = AttentionRelationTypes.FavoriteArticle;
break;
case 4:
relationTypes = AttentionRelationTypes.ArticleMention;
break;
case 5:
relationTypes = AttentionRelationTypes.Message;
break;
case 6:
relationTypes = AttentionRelationTypes.ReplyToMessage;
break;
case 7:
relationTypes = AttentionRelationTypes.CommentOnMood;
break;
case 8:
relationTypes = AttentionRelationTypes.ReplyToMoodComments;
break;
case 9:
relationTypes = AttentionRelationTypes.FavoriteMood;
break;
case 10:
relationTypes = AttentionRelationTypes.CommentOnPicture;
break;
case 11:
relationTypes = AttentionRelationTypes.ReplyToPictureComments;
break;
case 12:
relationTypes = AttentionRelationTypes.CirclePicture;
break;
case 13:
relationTypes = AttentionRelationTypes.Share;
break;
case 14:
relationTypes = AttentionRelationTypes.SiteMessage;
break;
case 15:
relationTypes = AttentionRelationTypes.ReplyToSiteMessage;
break;
}
Relationship relationShip = endUserNode
.createRelationshipTo(beginUserNode,
relationTypes);
relationShip.setProperty("weight", weight);
}
} catch (Exception ex) {
ex.printStackTrace();
}
} finally {
tx.success();
tx.finish();
}
}
} catch (SQLException ex) {
ex.printStackTrace();
} finally {
graphDb.shutdown();
}
System.out.println("完成 共用时:"
+ (System.currentTimeMillis() - beginTime));
}
}