How can I find the Neo4J Index Key's for Indexes created on nodes using Spring Data? - neo4j

I have created nodes using Spring using the following basic process (see below). I have a POJO for my Concepts and using this object and a Neo4J template to create nodes with indexes. I am still unable to discover what the 'KEY' for the created index is. I know the name of the index is 'CID' but assumed the 'KEY' would be 'conceptId'. However, when I use the following query (see below), no data is returned. It is confirmed that the Index does exist, but I am unable to find out what the proper 'KEY' for said index is so I can utilize it to improve query performance. I am able to query a specified node using WHERE clause searching for a specific value for a property of said node. However, when I try to find the node using the Index 'CID' with Key 'conceptId' no nodes are returned.
// Concept POJO
#NodeEntity
public class Concept {
#GraphId
private Long nodeId;
#Indexed(indexName="CID", fieldName="conceptId")
private Long conceptId;
...
// service where code to create Concept Nodes exists
#Repository
public class ConceptService {
#Autowired
private Neo4jTemplate n4jTemplate;
#Autowired
private ConceptRepository cr;
// Call to create node in a 'service'
public void addConceptNode(Concept concept) {
concept = n4jTemplate.save(concept);
}
...
//Cypher Queries used to retrieve nodes using index
START a=node:CID( conceptId = "66573009")
RETURN a;
// this returns 0 nodes quickly
START a=node:CID( conceptid = "66573009")
RETURN a;
// this returns 0 nodes quickly
START a=node:CID( CID = "66573009")
RETURN a;
// this returns 0 nodes quickly
START a=node:CID( cid = "66573009")
RETURN a;
// this returns 0 nodes quickly
START a=node:CID( CONCEPTID = "66573009")
RETURN a;
// this returns 0 nodes quickly
// Cypher query not using index to retrieve same node
START a=node(*)
WHERE HAS(a.conceptId) AND a.conceptId = 66573009
RETURN a;
// this returns 1 node in 77365ms
//'quickly' = approx.(43-87ms).
There is more to the code than what is shown, but this gives you the basic gist of how I am creating nodes with indexes in a Neo4J DB. There are more properties and more indexes. When using Spring to retrieve the nodes it seems to 'auto' use (I am assuming it is using the index) the index created because it returns the results faster than using the Neo4J data browser.
Any help would be greatly appreciated.
Thanks!

Related

How to make node changes in a TransactionEventHandler that are returned within the same CREATE query

I am trying to implement a plugin for neo4j to add an autoincrement ID using GraphAware library. To this end, I've written the following classes:
public class ModuleBootstrapper implements RuntimeModuleBootstrapper
{
public RuntimeModule bootstrapModule(String moduleId, Map<String, String> config, GraphDatabaseService database)
{
return new MyModule(moduleId, config, database);
}
}
And:
public class MyModule extends BaseTxDrivenModule<Void>
{
int counter = 0;
public Void beforeCommit(ImprovedTransactionData transactionData)
throws DeliberateTransactionRollbackException
{
if (transactionData.mutationsOccurred()) {
for (Node newNode : transactionData.getAllCreatedNodes()) {
newNode.setProperty("id", counter++);
}
}
}
}
And for the testing I can execute:
CREATE (n);
And then:
MATCH (n) RETURN n;
And I can see the effect of my plugin as some id property added to the node. But when I run:
CREATE (n) RETURN n;
The returned node does not have the mentioned id property but again, when I match the node in a separate query, I see the things have worked out just fine. It's just that in the CREATE query, the returned node infos are the ones before my plugin modified them.
The questions are; why is that? Didn't I modify the nodes through my plugin within the transaction? Shouldn't the returned nodes be showing the modifications I've made on them? Is there any way I can make this happen?
While you're still within the transaction, the Cypher result is already computed and there is no clean way to add additional informations to it.
I guess a feature request on the neo4j repository could be cool but in total honesty this would require a serious change into the neo4j core codebase.
BTW, the incremental ID is already implemented in the graphaware-uuid plugin : https://github.com/graphaware/neo4j-uuid#specifying-the-generator-through-configuration

Cypher query to take all relationships from one node, and attach them to another node

My question is.. given two nodes, node A & node B, is there a cypher query that removes all of the relationships from node A and attaches them instead to node B,
so that all nodes that were previously attached to node A, are now attached to node B?
I'm sure there are many use cases for such a query, but my use case is to do with merging multiple social network logins:
Given that I have a member account (member1) using google as a sign in provider
And I have a separate member account (member2) using facebook as a sign in provider
When member1 attempts to connect to the same facebook account that member2 is using as a sign in provider
And member1 requests a merge (merge account)
And member2 confirms the merge
Then the accounts for member1 and member2 will be merged
And one member will be remaining using both google and facebook as a signin provider.
What is the cypher query to do this?
EDIT: there is now way, to specify relationship type dynamically. So, workaround below is limited to relationships with same type and with no properties.
Let's create some data:
CREATE (n1:Node1)
CREATE (n2:Node2)
CREATE (target1:Target)
CREATE (target2:Target)
CREATE (target3:Target)
CREATE (n1)-[:REL]->(target1)
CREATE (n1)-[:REL]->(target2)
CREATE (n1)-[:REL]->(target3);
We have such data right now:
Now we want to "move" relationship from Node1 to Node2
Query:
MATCH (n1:Node1)-[r:REL]->(target)
MATCH (n2:Node2)
CREATE (n2)-[:REL]->(target)
DELETE r
And result is:
What we basically are done is:
Get relationship from one path
Create relationships to other node
Delete relationships from original node
I know this thread is old but I googled the same question and didn't found a solution on StackOverflow. So here it is.
As Michael told me here
You can use APOC functions :
apoc.refactor.to(relations, destination_node)
apoc.refactor.from(relations, destination_node)
And have something like :
MATCH(n2:Resource {name: 'destionation-resource'})
MATCH(n:Resource {name:'source-resource'})<-[r]-()
OPTIONAL MATCH(n)-[r2]->()
CALL apoc.refactor.to(r, n2) YIELD input, output
CALL apoc.refactor.from(r2, n2) YIELD input AS i, output AS o
RETURN *
The code for the alternative solution I used is below. I'm having trouble getting the unit tests to run but once they're fixed I'll post a mini project for this on github.
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.Transaction;
import org.neo4j.server.plugins.Description;
import org.neo4j.server.plugins.Name;
import org.neo4j.server.plugins.Parameter;
import org.neo4j.server.plugins.PluginTarget;
import org.neo4j.server.plugins.ServerPlugin;
import org.neo4j.server.plugins.Source;
/**
* #author John Deverall
* This plugin transfers all relationships from one node to another, including the relationships
* direction, type and properties.
* Can be called from the neo4j browser ui with command
* :POST /db/data/ext/TransferRelationships/graphdb/transfer_relationships {"sourceNode":"/db/data/node/<node_id>", "destinationNode":"/db/data/node/<node_id>"}
*/
public class TransferRelationships extends ServerPlugin {
#Name("transfer_relationships")
#Description("Transfer all relationships from one node to another")
#PluginTarget(GraphDatabaseService.class)
public Long transferNodes(#Source GraphDatabaseService graphDb,
#Description("The source node") #Parameter(name = "sourceNode", optional = false) Node sourceNode,
#Description("The destination node") #Parameter(name = "destinationNode", optional = false) Node destinationNode) {
try (Transaction tx = graphDb.beginTx()) {
// transfer outgoing relationships
Iterable<Relationship> outgoingRelationships = sourceNode.getRelationships(Direction.OUTGOING);
for (Relationship relationship : outgoingRelationships) {
Node otherNode = relationship.getOtherNode(sourceNode);
Relationship newRelationship = destinationNode.createRelationshipTo(otherNode, relationship.getType());
Iterable<String> propertyKeys = relationship.getPropertyKeys();
for (String key : propertyKeys) {
Object property = relationship.getProperty(key);
newRelationship.setProperty(key, property);
}
relationship.delete();
}
// transfer incoming relationships
Iterable<Relationship> incomingRelationships = sourceNode.getRelationships(Direction.INCOMING);
for (Relationship relationship : incomingRelationships) {
Node otherNode = relationship.getOtherNode(sourceNode);
Relationship newRelationship = otherNode.createRelationshipTo(destinationNode, relationship.getType());
Iterable<String> propertyKeys = relationship.getPropertyKeys();
for (String key : propertyKeys) {
Object property = relationship.getProperty(key);
newRelationship.setProperty(key, property);
}
relationship.delete();
}
tx.success();
}
return destinationNode.getId();
}
}

Dynamic Cypher Search Query

I know this may sound nonsense but I will try my best to explain the problem that I am facing with neo4jClient.
I developing a Social TaskManagment Software. and for the server side code and data Storage I choose to have neo4j (neo4jClient) and C# in place.
in Our software Imagine a User : (mhs) Post a Task of "#somebody please help me on #Neo4j #cypher" the task would be decorated with some fancy character including (# # + /) the obove post will be save in neo4j as graph like this :
(post:Post {Text : #somebody please help me on #Neo4j})-[Has_HashTag]-(neo4j:HashTag)
(post:Post)-[Has_HashTag]-(Cypher: HashTag)
(post:Post)-[Has_Author)-[mhs:User]
(post:Post)-[Has_MentionedUser)-[Somebody:User]
Now Imagine User #mhs is trying to search the Post that Have HashTag of #Neo4j and mentioned to #somebody
Here I am building (hand Craft) a Cypher Query Including the 2 seach Paramerts in Cypher with Some Fancy C# code which result the blow Cypher Query:
MATCH (nodes)-[r]-(post:Post),
(post:Post)-[:HAS_MentionedUsers]->(assignee1307989068:User),
(nodes)-[r]-(post:Post)-[:HAS_HashTags]->(Hashtag1482870844:HashTag)
WHERE (assignee1307989068.UserName = "somebody") AND (Hashtag1482870844.Value = "neo4j")
RETURN post AS Post, Collect(nodes) as nodes
ORDER BY post.creationDate
the above cypher will return a post with just all the nodes of the post that is not included in Where clause.
my question is how to include all the Nodes related to the Targeted (post) without including them in Return part of the cypher. Something like return (*).
The Other problem is How can I deserialize the result set into C# without knowing what shape it may have.
The Search method that I am producing the mentioned Cypher is as fallow:
public List<PostNode> Search(string searchterm)
{
List<string> where = new List<string>();
var tokenizedstring = searchterm.Split(' ');
var querystring = new StringBuilder();
var relatedNodes = new List<string>();
var q = new CypherFluentQuery(_graphClient) as ICypherFluentQuery;
foreach (var t in tokenizedstring)
{
_commandService.BuildPostQueystring(t, ref querystring, ref where, ref relatedNodes);
}
if (querystring[querystring.Length - 1] == ',')
querystring = querystring.Remove(querystring.Length - 1, 1);
q = q.Match(querystring.ToString());
int i = 1;
if (where.Count > 0)
q = q.Where(where[0]);
while (i < where.Count)
{
q = q.AndWhere(where[i]);
i++;
}
var rq = q.Return(
(post, nodes) => new PostNode
{
Post = post.As<Node<string>>(),
Nodes = nodes.CollectAs<string>()
})
.OrderBy("post.creationDate");
var results = rq.Results.ToList();
//foreach (var result in results)
//{
// //dynamic p = JsonConvert.DeserializeObject<dynamic>(result.Post.Data);
// //dynamic d = JsonConvert.DeserializeObject<dynamic>(result.Nodes.Data);
//}
return results;
}
}
//Some Helper Class just to cast the result.
public class PostNode
{
public Node<string> Post { get; set; }
public IEnumerable<Node<string>> Nodes { get; set; }
}
But as you may noticed it does not have the nodes that is included in the search term via Where Clause in Cypher Query.
I am really stopped here for a while, as I can not provide any decent solution for this. so your help may save me a lot.
may be i am totally in a wrong direction so please help in any way you can think of.
It appears that a list of UNKNOWN related nodes are being provisioned, so one of this:
Scenario A
It doesn't matter what EXACTLY those related nodes are, I just want them.
Question: What is intention of retrieving those unknown but related nodes? By answering this chances are this query could be improved for good.
Scenario B
These unknown related nodes are actually known! It's just they are not fully known at time of query execution however down the road somewhere in C# code we will have things like this
if (nodes.Any(_ => _ is HashTag) {...}
Question:
This type of behaviour requires to KNOW the type. Even with reflection or C# dynamic stuff that requirement is still there because Neo4jClient has no way of correlating a bag of JSON data coming from Neo4j into any local type. When Neo4jClient receives bulk of data over wire somehow it should know what type would YOU prefer to represent. This is why queries are always like this:
Return((a, p) => new
{
Author = a.As<Author>(), //we expect node content to be represented as Author
Post = p.As<Post>()
})
Neo4jClient does NOT preserve C# types inside your Neo4j database. It would have been nasty to do so. However, idea behind it is that you shouldn't find yourself desperate for it and if you do so then I would recommend looking for problem somewhere else i.e. why would you rely on your client library to describe your domain for you?

neo4j reference node obsolete but yet still returned from getAllNodes

According to Neo4j documentation the "reference node concept is obsolete - indexes are the canonical way of getting hold of entry points in the graph.".
However if I use GlobalGraphOperations.getAllNodes() I'm still returned a node with id 0 which I didn't create and which has all the looks of a reference node.
I'm trying to implement a method getNode(String uuid)
public Node getNode(String uuid)
{
GlobalGraphOperations globalGraphOperations = GlobalGraphOperations.at(graphDb);
for(Node tmpNode : globalGraphOperations.getAllNodes())
{
if(tmpNode.equals(graphDb.getReferenceNode()))
{ continue;}
String tmpNodeUuid = (String)tmpNode.getProperty("uuid");
if (tmpNodeUuid.equals(uuid))
{
return tmpNode;
}
}
return null;
}
why does getAllNodes return a reference node?
how to implement programmatically getNode() without using deprecated function getReferenceNode()?
The reference node concept is indeed deprecated and will be removed with Neo4j version 2.0. In 1.x the concept still exists and the reference node is created when the database is intially created. If you don't need it, you can just delete the reference node. The method you're writing is gonna get slow as the graph grows as the entire graph is traversed. You should create an index for the UUID property and use that to look up nodes in the graph, which is much faster. As well as being the 'canonical way of getting hold of entry points in the graph' :-)

Map cypher query result to domain object

I just started to use Neo4j with Spring Data and I'm not able to recover graph objects and convert them back to domain objects. I must say I have no previous experience in that kind of databases.
In that way, I'm using Spring Data repositories. For standard queries the repository code is auto-generated but I would like to define some custom methods, so I created my custom repository as explained here.
For example, I would like to be able to update a certain property value (currentValue property in this case) from a given edge between two certain nodes (searchByUserName is a previously defined index in my node entity which represents a user). I'm using the query method from the Neo4j template in my custom repository implementation as follows:
public class TwitterUserRepositoryImpl implements TwitterUserRepositoryCustom{
#Autowired
private Neo4jOperations neo4jTemplate;
public void updateRelationshipValueByUserName(
String userAUserName, String userBUserName, double value){
HashedMap params = new HashedMap();
params.put("userAUserName", userAUserName);
params.put("userBUserName", userBUserName);
params.put("value", value);
String query = "START x=node:searchByUserName(userName = {userAUserName}), " +
"y=node:searchByUserName(userName = {userBUserName})" +
" MATCH (x)-[r:FOLLOWS]->(y)" +
" SET r.currentValue = {value}" +
" RETURN r";
Result<Map<String, Object>> relationships = neo4jTemplate.query(query, params);
/* let's try to recover the relationship entity and do some more stuff */
}
The cypher query returns an "edge" between two users, where its relationship type is "FOLLOWS", simulating a Twitter users network. I have no idea how to convert this QueryResult object back to my RelationshipEntity object. Is that possible?
Just use the result-dsl: http://static.springsource.org/spring-data/data-graph/snapshot-site/reference/html/#d5e1118
relationships.to(MyRelationshipEntity.class)
will return you a Result<MyRelationshipEntity> which is an Iterable

Resources