How to Iterate column with multiple values using cypher? - java

The code I tried was
firstNode = graphDb.createNode();//creating nodes like this
firstNode.setProperty( "person", "Andy " );
Label myLabel = DynamicLabel.label("person");
firstNode.addLabel(myLabel); ...
relationship = firstNode.createRelationshipTo( secondNode, RelTypes.emails );// creating relationships like this
relationship.setProperty( "relationship", "email " );....
Transaction tx1 = graphDb.beginTx();
try{
ExecutionEngine engine = new ExecutionEngine(graphDb);
ExecutionResult result = engine.execute("MATCH (sender:person)-[:emails]-(receiver) RETURN sender, count(receiver)as count, collect(receiver) as receivers ORDER BY count DESC ");..
and the result I obtained was:
sender | count | receivers
Node[2]{person:"Chris"} | 3 | [Node[4]{person:"Elsa "},Node[0]{person:"Andy "},Node[1]{person:"Bobby"}]
Node[4]{person:"Elsa "} | 3 | [Node[5]{person:"Frank"},Node[2]{person:"Chris"},Node[3]{person:"David"}]
Node[1]{person:"Bobby"} | 3 | [Node[2]{person:"Chris"},Node[3]{person:"David"},Node[0]{person:"Andy "}]
Node[5]{person:"Frank"} | 2 | [Node[3]{person:"David"},Node[4]{person:"Elsa "}
I want to iterate the receivers. so I tried the following :
for (Map<String,Object> row : result) {
Node x = (Node)row.get("receivers");
System.out.println(x);
for (String prop : x.getPropertyKeys()) {
System.out.println(prop +": "+x.getProperty(prop));
}
But it throws Exception in thread "main" java.lang.ClassCastException: scala.collection.convert.Wrappers$SeqWrapper cannot be cast to org.neo4j.graphdb.Node.
How can I do this?

The problem is that in your cypher query you collect the receiver.person property into an array called receivers. Receivers isn't a Node, its an array of properties. You can't cast that to strings. If you were looking to get the actual receiver nodes then you need to change your query statement to:
MATCH (sender:person)-[:emails]-(receiver) RETURN sender, count(receiver)as count, receiver as receivers ORDER BY count DESC
Alternatively, if you want to use the array of properties then you can do something like the code below:
Object receivers = row.get("receivers")
if(receivers instanceof String[]) {
for(String receiver in receivers) {
.. do something
}
} else {
// do something with receiver as a single string value
}
Clearly, you will need to change to type of receivers from String to the appropriate type if it isn't a String.

It is a simple thing.
try{
ExecutionEngine engine = new ExecutionEngine(graphDb);
ExecutionResult result = engine.execute("MATCH (sender:person)-[:emails]-(receiver) RETURN sender, count(receiver)as count, collect(receiver.person) as receivers ORDER BY count DESC ");
//ExecutionResult result = engine.execute("MATCH (sender:person)-[:emails]->(receiver) WITH sender, collect(receiver.person) as receivers, RETURN {sender: sender.person, receivers: receivers) ORDER BY size(receivers) DESC");
//System.out.println(result.dumpToString());
LinkedList list_prop = new LinkedList();
for (Map<String,Object> row : result) {
Node x = (Node)row.get("sender");
Object y = row.get("receivers");
System.out.println(y);
for (String prop_x : x.getPropertyKeys()) {
System.out.println(prop_x +": "+x.getProperty(prop_x));
}
}
tx1.success();
}
finally {
tx1.close();
}
In the match query, I used "collect(receiver.person) as receivers" instead of "collect(receiver) as receivers". It worked.

Related

get path after querying neo4j java

I'm trying to do a query to fin all possible paths that correspond to the pattern "(Order) - [ORDERS] -> (Product) - [PART_OF] -> (Category)" and would like to get the whole path (i.e. all 3 nodes and 2 relationships as their appropriate classes).
The method i used below only let me have 1 column of data (number of orders: 2155). If I tried it once more (the 2nd for loop), the number of row i'd get is 0(number of products: 0). Is there a way to save all the results as nodes and relationships or do I have to query the command 5 times over?
Please help!
String query = "MATCH (o:Order)-[:ORDERS]->(p:Product)-[:PART_OF]->(cate:Category) return o,p,cate";
try( Transaction tx = db.beginTx();
Result result = db.execute(query) ){
Iterator<Node> o_column = result.columnAs( "o" );
int i = 0;
for ( Node node : Iterators.asIterable( o_column ) )
{
i++;
}
System.out.println("number of orders: " + i);
i = 0;
Iterator<Node> p_column = result.columnAs( "p" );
for ( Node node : Iterators.asIterable( p_column ) )
{
i++;
}
System.out.println("number of products: " + i);
tx.success();
}
I've found a way to work around this in the code below, where i'd changes the return value to the node ID using id() then uses GraphDatabaseService.getNodeByID(long):
String query = "MATCH (o:Order)-[:ORDERS]->(p:Product)-[:PART_OF]->(cate:Category) return id(o), id(p), id(cate)";
int nodeID = Integer.parseInt(column.getValue().toString());
Node node = db.getNodeById(nodeID);
If you do this :
MATCH path=(o:Order)-[:ORDERS]->(p:Product)-[:PART_OF]->(cate:Category) return path
You can process path in your loop and unpack that. Takes a bit of exploring but all the information is in there.
Hope that helps.
Regards,
Tom

Extracting relationships from a neo4J StatementResult

How do I extract relationships from a StatementResult?
For the moment I have something like this:
while (results.hasNext()) {
Record record = results.next();
try {
if (record.get(0).hasType(TYPE_SYSTEM.NODE())){
Node node = record.get(0).asNode();
//System.out.println(node.get("name") + ": " + node.get("guid"));
// Add block
if (node.hasLabel(configuration.getBlock())) {
Block block = Block.fromRecord(node);
blocks.addBlock(block);
} else
// Add property
if (node.hasLabel(configuration.getProp())) {
Property property = Property.fromRecord(node);
String guid = property.getGuid();
Block block = blocks.getBlock(guid);
block.addProperty(property);
} else
// Add output
if (node.hasLabel(configuration.getOutput())) {
Output output = Output.fromRecord(node);
String guid = output.getGuid();
Block block = blocks.getBlock(guid);
block.addOutput(output);
} else
// Add input
if (node.hasLabel(configuration.getInput())) {
Input input = Input.fromRecord(node);
inputs.add(input);
String guid = input.getGuid();
}
}
My original query was something like this:
MATCH (start:Block{name:'block_3'})
CALL apoc.path.subgraphNodes(start, {relationshipFilter:'PART_OF|hasOutPort>|connectsTo>|<hasInPort'}) YIELD node as block
WITH
block,
[(block)-[:PART_OF]->(prop) | prop] as properties,
[(block)-[:hasOutPort]->(output) | output] as outputs,
[(block)-[:hasInPort]->(input) | input] as inputs
RETURN block, properties, outputs, inputs
I need all the "connectsTo" relationships
Hope that makes sense.
First you need to specify those relations' aliases and return them like nodes. Simplified example:
MATCH (a:Block)-[r:PART_OF]->(b:Block) RETURN a, r, b
This way, your Record instances will contain data (Value instances) for a, r and b. And in your extraction logic you do the following to get the relationship data:
Relationship r = record.get("r").asRelationship();

Collecting the result of cypher query into a hash map java?

This is a followup of Finding connected nodes question.
The code is
firstNode = graphDb.createNode();//creating nodes
firstNode.setProperty( "person", "Andy " );
Label myLabel = DynamicLabel.label("person");
firstNode.addLabel(myLabel); ...
relationship = firstNode.createRelationshipTo( secondNode, RelTypes.emails );// creating relationships
relationship.setProperty( "relationship", "email " );....
ExecutionEngine engine = new ExecutionEngine(graphDb);
ExecutionResult result = engine.execute("MATCH (sender:person)-[:emails]- (receiver)RETURN sender, count(receiver)as count, collect(receiver) as receivers ORDER BY count DESC ");
System.out.println(result.dumpToString());
The result I got was:
sender | count | receivers
Node[2]{person:"Chris"} | 3 | [Node[4]{person:"Elsa "},Node[0]{person:"Andy "},Node[1]{person:"Bobby"}]
Node[4]{person:"Elsa "} | 3 | [Node[5]{person:"Frank"},Node[2]{person:"Chris"},Node[3]{person:"David"}]
Node[1]{person:"Bobby"} | 3 | [Node[2]{person:"Chris"},Node[3]{person:"David"},Node[0]{person:"Andy "}]
Node[5]{person:"Frank"} | 2 | [Node[3]{person:"David"},Node[4]{person:"Elsa "}
How to collect the sender as key and receivers as values?
For ex : {Frank =[David, Elsa], Chris =[Elsa, Andy, Nobby]..
Any idea?
Initially I tried iterating something like this
for (Map<String,Object> row : result) {
Node x = (Node)row.get("receivers");
System.out.println(x);
for (String prop : x.getPropertyKeys()) {
System.out.println(prop +": "+x.getProperty(prop));
}
This throws classcast exception. It works for column "sender" and not for "receivers".
I am very new to cypher. I don't know how to transform the result into a hash map. How is this possible ?
You can rewrite the cypher to return a map instead... (split for readability)
MATCH (sender:person)-[:emails]->(receiver)
WITH sender, collect(receiver.person) as receivers
RETURN {sender: sender.person, receivers: receivers)
ORDER BY size(receivers) DESC
The result can be treated as a list of maps, each map represents the key-value mapping of its record in the result.
Code to achieve this:
private List<Map<String, Object>> buildResultsList(Result result) {
List<Map<String, Object>> results = new LinkedList<>();
while (result.hasNext()) {
Record record = result.next();
results.add(record.asMap());
}
return results;
}

Get number of objects referenced from ArrayList with size 1 grouped by class

I've got a heap dump from the application and found out that there's a huge number of ArrayLists with only 1 object in it. I know how to get the list of such arraylists and also show the class of the contained element:
SELECT list.elementData[0] FROM java.util.ArrayList list WHERE (list.size = 1)
The result looks like this:
java.lang.String [id=0x7f8e44970]
java.lang.String [id=0x7f8e44980]
java.lang.String [id=0x7f8e44572]
java.io.File [id=0x7f8e43572]
...
What I would like is to get something like this:
Class | count
=================================
java.lang.String | 100
java.io.File | 74
...
but I'm not able to aggregate the results or do anything else on those. I've found here how to pass the values to outer select, but I can't figure out how to use anything else beside the * in the first select.
SELECT * from OBJECTS
(SELECT OBJECTS list.elementData[0] FROM java.util.ArrayList list WHERE (list.size = 1))
There is no group by in VisualVM's OQL But you can use the build-in functions to make a JavaScript snippet and run it in the "OQL Console":
var c = {};
/* Filter to show only the first occurence (max count) */
filter(
/* Sort by occurences desc */
sort(
/* Count class instances */
map(
heap.objects("java.util.ArrayList"),
function(list) {
var clazz = 'null';
if (list.size = 1 && list.elementData[0] != null) {
clazz = classof(list.elementData[0]).name;
}
c[clazz] = (c[clazz] ? c[clazz] + 1 : 1);
return { cnt:c[clazz], type:clazz };
}
), 'rhs.cnt - lhs.cnt'
),
function (item) {
if (c[item.type]) {
c[item.type] = false;
return true;
} else {
return false;
}
}
);
The output is an array of object like:
{
cnt = 3854.0,
type = null
}
{
cnt = 501.0,
type = org.apache.tomcat.util.digester.CallMethodRule
}
{
cnt = 256.0,
type = java.lang.ref.WeakReference
}
{
cnt = 176.0,
type = sun.reflect.generics.tree.SimpleClassTypeSignature
}
Finally you can call map function again to format the output to something else like per exmpale as csv:
map(
filter(...),
'it.type + ", " + it.cnt'
);
output:
null, 3854
org.apache.tomcat.util.digester.CallMethodRule, 501
java.lang.ref.WeakReference, 256
sun.reflect.generics.tree.SimpleClassTypeSignature, 176
org.apache.tomcat.util.digester.CallParamRule, 144
com.sun.tools.javac.file.ZipFileIndex$Entry, 141
org.apache.tomcat.util.digester.ObjectCreateRule, 78

Getting the metadata of columns from SOQL Query

I have a simple SOQL query in java for extracting Salesforce standard object as follows -
String soqlQuery = "SELECT FirstName, LastName FROM Contact";
QueryResult qr = connection.query(soqlQuery);
I want to get the datatype of the object fields.
I have written a small function below which will provide the list of Phone fields and its label present in a Custom or Standard Object of your Salesforce ORG. I hope this might help you in writing the business logic for your code.
public list<String> getFieldsForSelectedObject(){
selectedPhoneNumber = ''; //to reset home number field
list<String> fieldsName = new list<String>();
selectedObject = 'Object Name' // This should have the object name for which we want to get the fields type
schemaMap = Schema.getGlobalDescribe(); //Populating the schema map
try{
if(selectedObject != null || selectedObject != '' || selectedObject != '--Select Object--'){
Map<String, Schema.SObjectField> fieldMap = schemaMap.get(selectedObject).getDescribe().fields.getMap();
for(Schema.SObjectField sfield : fieldMap.Values()){
schema.describefieldresult dfield = sfield.getDescribe();
schema.Displaytype disfield= dfield.getType();
system.debug('#######' + dfield );
if(dfield.getType() == Schema.displayType.Phone){// Over here I am trying to findout all the PHONE Type fields in the object(Both Custom/Standard)
fieldsName.add('Name:'+dfield.getName() +' Label:'+ dfield.getLabel ());
}
}
}
}catch(Exception ex){
apexPages.addMessage(new ApexPages.message(ApexPages.severity.ERROR,'There is no Phone or Fax Field Exist for selected Object!'));
}
return fieldsName;
}
Sample OUTPUT List of String::
Name: Home_Phone__c Label: Home Phone
Name: Office_Phone__c Label: Office Phone
Say that we have the below soql.
select FirstName,LastName from Contact limit 2
The query result in the QueryResult object looks like below.
{
[2]XmlObject
{
name={urn:partner.soap.sforce.com}records, value=null, children=
[
XmlObject{name={urn:sobject.partner.soap.sforce.com}type, value=Contact, children=[]},
XmlObject{name={urn:sobject.partner.soap.sforce.com}Id, value=null, children=[]},
XmlObject{name={urn:sobject.partner.soap.sforce.com}FirstName, value=Bill, children=[]},
XmlObject{name={urn:sobject.partner.soap.sforce.com}LastName, value=Gates, children=[]}
]
},
XmlObject
{
name={urn:partner.soap.sforce.com}records, value=null, children=
[
XmlObject{name={urn:sobject.partner.soap.sforce.com}type, value=Contact, children=[]},
XmlObject{name={urn:sobject.partner.soap.sforce.com}Id, value=null, children=[]},
XmlObject{name={urn:sobject.partner.soap.sforce.com}FirstName, value=Alan, children=[]},
XmlObject{name={urn:sobject.partner.soap.sforce.com}LastName, value=Donald, children=[]}
]
},
}
In order to parse the QueryResult and to take column names, I have implemented the below method that will return the column names in comma separated String. I have mentioned the logic inside the code.
public String getColumnNames(QueryResult soqlResponse)
{
String columns = ""
try
{
// We are looping inorder to pick the 1st record from the QueryResult
for (SObject record : soqlResponse.getRecords())
{
Iterator<XmlObject> xmlList = record.getChildren();
int counterXml = 0;
while(xmlList.hasNext())
{
XmlObject xObj = xmlList.next();
// Since the 1st 2 nodes contains metadata of some other information, we are starting from the 3rd node only
if(counterXml > 1)
{
columns += xObj.getName().getLocalPart() + ",";
}
counterXml++;
}
// Since we can get the column names from the 1st record, we are breaking the loop after the data of 1st record is read
break;
}
// We are removing the last comma in the string
columns = columns.substring(0, columns.length() - 1);
}
catch(Exception ex)
{
}
return columns;
}

Categories

Resources