What I have done:
I have created nodes with multiple properties like name,Age,Location,Gender etc.
I want to retrieve nodes which have matching property values and create a relationship between them.(Eg nodes which have same age or same location).
I have done this as follows:
void query()
{
ExecutionResult result;
Transaction tx=null;
ExecutionEngine engine = new ExecutionEngine( graphDb );
try
{
String name="Female";
tx=graphDb.beginTx();
result=engine.execute("start n=node(*) where has(n.City) with n.City as city, collect(n) as nodelist, count(*) as count where count > 1 return city, nodelist, count");
System.out.println(result.dumpToString());
tx.success();
}
catch(Exception e)
{
tx.failure();
}
finally
{
tx.finish();
}
}
The nodelist gives me the nodes with same properties .
I want to create a relationship between these nodes.
How can I point to the nodes in the nodelist?
Also,please suggest other alternative ways of doing the same
To get hold of the nodes in nodelist:
Iterator<Map<String,Object>> it=result.iterator ();
if(it.hasNext()) {
Map<String,Object> row=it.next();
List<Node> nodelist=(List<Node>) row.get("nodelist");
}
You haven't specified what kinds of relationships you want to create - take a look at Create or Merge and if applicable, Foreach - maybe you can write one Cypher query to do it all.
Related
When traversing a tree structure recursively in order to calculate weights and volumes for an entire bill of materials, I run into a ConcurrentModificationException. My approach in pseudocode:
Query initialization: add root node to list of nodes and check if it has any childs.
Progress documentation: Flag the node as visited.
Query childs: Checking for child nodes and if present add to allNodes with a level up flag.
Recursive traversal: Recursively traverse list until no more child elements are found.
I have tried to use iterators to allow myself to expand that array of nodes but ran into the same problem. Slowly running out of ideas here I am grateful for any hint.
NOTE: please forgive me to paste my problem not stating all the context for better readability. Let me know if you need more info.
// Initialization
List<Node> allNodes = new ArrayList<>();
allNodes.add(new Node(input, input, 0, false) // (1)
int counter = 0;
// Method call
getAllNodes(allNodes);
// Query parent for child elements
public void getAllNodes(List<Node> nodes){
for (Node node : nodes) {
if (!node.isVisited()) { // (2)
node.setVisited(true);
String parentId = node.getId();
Product product = QueryUtil.getFirstByIdNo(ctx, parentId, Product.class);
if (isComposite(product)) {
Iterable<Product.Row> rows = product.table().getRows(); // (3)
for (Product.Row row : rows) {
allNodes.add(new Node(parentId, row.getProductListElem().getIdno(), ++counter, false));
--counter;
}
++counter;
// Recursive query of all node elements
getAllNodes(allNodes); // (4)
}
}
}
}
//Node Bean with getters, setters, constructor, toString
#Data
class Node {
String parent;
String id;
int level;
boolean visited;
}
You are getting the error because you are trying to modify the list that you are iterating(reading) over. And this is not allowed in JAVA. [for more explanation check here]
To avoid this you can either get an iterator for your list and then loop over it, but that does not seem like a very good option for the code you have posted above. Thus, I'll recommend using List<Node> allNodes = new CopyOnWriteArrayList<>(); instead of List<Node> allNodes = new ArrayList<>().
I have a database with two columns named
colNode,colLeaf
When I read data from the database and display it on the JTree
while (resultSet.next()) {
DefaultMutableTreeNode url = new DefaultMutableTreeNode(resultSet.getString("colNode"));
mutableTreeNode.add(url);
url.add(new DefaultMutableTreeNode(resultSet.getString("colLeaf")));
}
I would like if the value already exists on JTree colNode then colLeaf will be appended to the.I want to get the results in Figure 2
Please help me
Current output:
Desired output:
I assume you mean, that you want to group all the colLeaf with the same colNode?
I'd start by doing one of two things, either use the SQL to group and/or sort your results by colNode, this way you could monitor the current "group" and when it changes, create a new TreeNode
Something like...
DefaultMutableTreeNode rootNode = ...;
DefaultMutableTreeNode groupNode = null;
String currentGroup = null;
while (resultSet.next()) {
String group = resultSet.getString("colNode");
if (currentGroup == null || !currentGroup.equals(group)) {
currentGroup = group;
group = new DefaultMutableTreeNode(group);
rootNode.add(groupNode);
}
group.add(new DefaultMutableTreeNode(resultSet.getString("colLeaf")));
}
for example.
Or, use somekind of Map, to map all the colNode values under the same key (colLeaf)
Map<String, List<String>> mapGroups = new HashMap<>(25);
while (resultSet.next()) {
String group = resultSet.getString("colNode");
List<String> members = mapGroups.get(group);
if (members == null) {
members = new ArrayList<>(25);
mapGroups.put(group, members);
}
members.add(resultSet.getString("colLeaf"));
}
You would then use the mapGroups to generate your TreeNodes based on the key/value groups.
for example.
Personally, I think the first option is more efficient, but might require a more complex query, the second option is slower and requires more overhead, but models the data in manner which is similar to that which you are trying to achieve
I'm using neo4j through java and I was wondering if there's a way to save some metadata with that node. I wanted to be able to have a node from the graph include more information, for instance to have each node have a dictionary associated with it.
edit - A dictionary was just an example, I want to be able to associate also class instances which contain as one of the fields a dictionary for example.
Unfortunately, there is no such functionality in Neo4j.
Neo4j is simple property graph.
But you can "emulate" such behaviour by using conventions in your application.
Special properties
You can specify in your application that all properties that starts with __ are metadata.
Then storing metadata is trivial:
try (Transaction tx = db.beginTx()) {
Node node = db.createNode();
node.setProperty("__version", "1.0.0");
node.setProperty("__author", "Dmitry");
tx.success();
}
JSON metadata
Other way - store JSON string in __metadata property and specify all your metadata as JSON.
Example:
ObjectMapper mapper = new ObjectMapper();
// create node and set metadata
try (Transaction tx = db.beginTx()) {
Map<String, Object> metadata = new HashMap<>();
metadata.put("version", "1.0.0");
metadata.put("author", "Dmitry");
Node node = db.createNode();
node.setProperty("__metadata", mapper.writeValueAsString(metadata));
tx.success();
}
// find node and get metadata
try (Transaction tx = db.beginTx()) {
Node node = db.findNode(...);
Map<String, Object> metadata = map = mapper.readValue(
node.getProperty("__metadata"), HashMap.class);
tx.success();
}
Note: if you go with this option, then you should create some sort of wrapper/helper for Node to minimize code duplication.
Note2: ObjectMapper should be created only once per application.
In addition to the other answer you can easily create a separate node representing your class and holding class-level meta information.
Either connect all the nodes representing instances to the class node using a relationship (this might cause lock contention if a lot of instances are added concurrently) or use a naming convention:
Node for instances: (:Person{name:'Danny'})
Metanode for person: (:Meta{clazz:'Person', metaProp1: value1, ...})
So the label if the instance node is linked to the clazz property of the meta node.
I have following table People
I want to get relational records and make the JSON Array like following:
{ "Person":[
{"ID":1,
"FirstName":"James",
"LastName":"Donovan",
"Child":[
{"ID":6, "FirstName":"Nikolai", "LastName":"Donovan"}
]
},
{"ID":2,
"FirstName":"Jeffrey",
"LastName":"Williams",
"Child":[
{"ID":4, "FirstName":"Carol", "LastName":"Williams"},
{"ID":5, "FirstName":"Sarah", "LastName":"Williams"}
]
},
.... and so on
]
}
What I use following approach;
short summary of below code: I perform db operation twice in loop and put node into JSON Object is match certain conditions.
preparedStatement = con.prepareStatement("SELECT * FROM PEOPLE");
rs = preparedStatement.executeQuery(); // ResultSet Object
if (rs != null && rs.next()) {
Roles = new JSONObject();
parentNode = new JSONObject();
childNode = new JSONObject();
while(rs.next()) {
parentNode.put("ID", rs.getInt("Id"));
parentNode.put("FirstName", rs.getString("firstname"));
parentNode.put("LastName", rs.getString("lastname"));
if(rs.getInt("parent") > 0) { // Equal 0 Mean it has not child
// Perform again database operation and get record related to parent id
preparedStatement = con.prepareStatement("SELECT * FROM PEOPLE WHERE parent=?");
preparedStatement.setString(1, rs.getInt("id");
resultSet2 = preparedStatement.executeQuery();
while(resultSet2.next()) {
childNode.put("ID", rs.getInt("Id"));
childNode.put("FirstName", rs.getString("firstname"));
childNode.put("LastName", rs.getString("lastname"));
parentNode.put("Child":childRole); // put child into parent node
}
}
}
}
What I want?
Performing db select query in loop is too much expensive for server side.
Is there any better solution which generate desired JSON of relational data and save me from additional SELECT operations!
I am thankful for your attention!
If you are using mysql 5.7 you can you JSON_ARRAY function to create json array. or JSON_OBJECT https://dev.mysql.com/doc/refman/5.7/en/json-attribute-functions.html
maybe use two queries.
select blah where parent = 0.
select blah where parent != 0 order by parent, id
loop through the results of the second result set and assign them to the parent in the first result set.
use gson (or jackson or whatever json library you prefer) to convert from the object list to the final jason.
brief codelike stuff
select the parents (parent = 0).
for each parent row, add the row to a Map<String, Entity> where the key is the parent id.
select the children (parent != 0).
for each child row, get the parent row from the map using the parent id then add them to the parent entity.
use the same class for parent and child rows, initialize the child collection to null and create it on the first add of a child. This way, the child array will be included for parents that have children and not included for parents with no children and for children.
You could issue a single select
SELECT p.ID, p.FirstName, p.LastName, c.ID, c.FirstName, c.LastName
FROM PEOPLE p LEFT OUTER JOIN PEOPLE c ON c.parent = p.ID
ORDER BY p.ID
and then loop over the result.
Whenever the parent ID changes (and for the first result row) you build a new current parentNode. Then you read the child data (which may be empty in case the parent has no children) and put it into the child json array of the current parentNode.
EntityManager em = EMF.get().createEntityManager();
EntityTransaction tx = null;
List<Profile> list = null;
Query q = null;
try{
tx = em.getTransaction();
tx.begin();
q = em.createNamedQuery("Profile.getRandomProfile");
q.setParameter("random", Math.random());
q.setMaxResults(8);
list = (List<Profile>) q.getResultList();
if (list != null){
Collections.shuffle(list);
}
tx.commit();
} catch(NoResultException ex){
System.out.println("ERROR CATCHED: " +ex.getMessage());
if(tx != null && tx.isActive())
tx.rollback();
} catch(Exception e){
e.printStackTrace();
}
finally{
em.close();
}
Shuffling the list have error:
java.lang.UnsupportedOperationException: Query result sets are not modifiable
How to overcome the problem?
Copy the results to a secondary list and shuffle it instead of the query result list.
ArrayList copyList = new ArrayList();
Collections.copy(copyList,list);
Collections.shuffle(copyList);
In the line
list = (List<Profile>) q.getResultList();
after it, you should create a new list based on the result one, like this:
List<Profile> anotherList= new ArrayList<Profile>(listaOrdenes);
This way you have a "new" list and this one you can modify it.
maybe like this ?
List<Profile> profiles = null;
List<Profile> results = (List<Profile>) q.getResultList();
if(results != null) {
profiles = new ArrayList<Profile>();
profiles.addAll(results);
Collections.shuffle(profiles);
}
Just like the other two people said, you should copy the results to your own list because they come back in read-only mode. The other possibility is that the List implementation that comes back, doesn't support the operations that shuffle invokes. You can also try to see what the type of list is to verify, but I doubt this is the case.
There are 2 options:
1)Create new List (2)Use ORDER BY clause in query.
Collections.sort(...) will sort the list that you give it. So, it will modify the list. However, the list that you are trying to sort is unmodifiable. When Collections.sort(...) calls one of the methods of the list to add or remove an element, it will throw an exception.
One solution is to create a new, modifiable list from the original list, and then sort that list.
// Create a new ArrayList that contains all elements from the list 'identities'
List<Identity> data = new ArrayList<Identity>(identities);
// Sort the new list
Collections.sort(data);
But, since you're presumably getting the list from the database using a JPA query, it would be a better idea to change the database query to include an "order by" clause, to let the database do the sorting. You wouldn't need to do the sorting in your Java code then.