I've recently had to change from using Cypher to Gremlin and I'm trying to convert a query that allowed a user to 'delete' a node and all of the subgraph nodes that would be affected by this. It wasn't actually removing nodes but just adding a 'DELETED' label to the affected nodes.
I can get a subgraph in Gremlin using:
g.V(nodeId).repeat(__.inE('memberOf').subgraph('subGraph').outV()).cap('subGraph')
but this doesn't take into account any nodes in the subgraph that might have a route back past the originally 'deleted' node and therefore shouldn't be orphaned.
If you take the graph above; B is the node being deleted. It's subgraph would include D, E, G and H. However, since E still has a route back to A through C, we don't want to 'delete' it. D, G and H will be left without a route back to A and should therefore also be deleted.
My Cypher query worked like this (using Neo4jClient.Cypher in C#):
// Find the node to be deleted i.e. B
.Match("(b {Id: 'B'})")
// Set a DELETED label to B
.Set("b:DELETED")
.With("b")
// Find the central node i.e A
.Match("(a {Id: 'A'})")
// Find the subgraph of B ignoring previously deleted nodes
.Call("apoc.path.subgraphAll(b, { relationshipFilter: '<memberOf', labelFilter: '-DELETED'})")
.Yield("nodes AS subgraph1")
// Get each node in subgraph1 as sg1n
.Unwind("subgraph1", "sg1n")
// Check if each sg1n node has a route back to A ignoring DELETED routes
.Call("apoc.path.expandConfig(sg1n, { optional: true, relationshipFilter: 'memberOf>', labelFilter: '-DELETED', blacklistNodes:[b],terminatorNodes:[a]})")
.Yield("path")
// If there is a path then store the nodes as n
.Unwind("CASE WHEN path IS NULL THEN [null] ELSE nodes(path) END", "n")
// Remove the nodes in n from the original subgraph (This should leave the nodes without a route back)
.With("apoc.coll.subtract(subgraph1, collect(n)) AS subgraph2")
// Set the DELETED label on the remaining nodes
.ForEach("(n IN(subgraph2) | SET n:DELETED)")
Is there any way I can get similar functionality in Gremlin?
UPDATE
Thanks to sel-fish's help in this question and in this one, I now have this working using:
g.V(itemId) // Find the item to delete.
.union( // Start a union to return
g.V(itemId), // both the item
g.V(itemId) // and its descendants.
.repeat(__.inE('memberOf').outV().store('x')) // Find all of its descendants.
.cap('x').unfold() // Unfold them.
.where(repeat(out('memberOf') // Check each descendant
.where(hasId(neq(itemId))).simplePath()) // to see if it has a path back that doesn't go through the original vertex
.until(hasId(centralId))) // that ends at the central vertex .
.aggregate('exception') // Aggregate these together.
.cap('x').unfold() // Get all the descendants again.
.where(without('exception'))) // Remove the exceptions.
.property('deleted', true) // Set the deleted property.
.valueMap(true) // Return the results.
First, save the vertices in subgraph as candidates:
candidates = g.V().has('Id', 'B').repeat(__.inE('memberOf').subgraph('subGraph').outV()).cap('subGraph').next().traversal().V().toList()
Then, filter the candidates, remains those which doesn't get a path towards Vertex('A') which not including Vertex('B'):
g.V(candidates).where(repeat(out('memberOf').where(has('Id', neq('B'))).simplePath()).until(has('Id','A'))).has('Id', neq('B')).aggregate('expection').V(candidates).where(without('expection'))
Related
I am using DiffBuilder to compare two XML files.
For the given element Product, I want to check that the element is present but I want to ignore its value.
Is it possible using XmlUnit ?
My code below will work regardless of the presence of the Product element, which doesn't work for what I need
var differenceBuilder = DiffBuilder
.Compare(Input.FromDocument(controlXmlFile))
.WithTest(Input.FromDocument(testXmlDile))
.WithNodeFilter(n => n.Name != "Product");
In this case you don't want to throw away the node itself but the differences between the nodes. You wouldn't use a NodeFilter but rather a DifferenceEvaluator for this.
A very simplified version could be something like
.WithDifferenceEvaluator((comparison, outcome) =>
comparison.ControlDetails.Target.Name == "Product" ? ComparisonResult.EQUAL : outcome
)
but you'd need to take into account that ControlDetails could be null (and look at TestDetails instead) for example. And if your Product node has child elements you may want to either filter them out with NodeFilter or check whether any parent of the current comparison target is named Product.
I need to implement a binary tree view attached in the diagram. The process is going like this. First, start in position#1. Then need to print P#2.After p#3.As binary tree method need print left first then right. This is for pyramid system. Please check the example below how this should be. Each node represents the customer(By ID) and his position.
Ex: Let's assume out customerID is number 01 and he might have 5 positions in the tree representing his ID #1, Like in the diagram. And customer number2 have 3 positions in a tree. So we have a lot of customers and some have more than 20 positions in each node.
I need a solution to implement this view. Please note this is not relevant for binary search tree. Thank you!
Please check the image in here
It looks like you are trying to implement a breadth first search but with an unusual order for returning the results whereby you take one item from each child in a row and interleave them.
So you will need to proceed row-by-row: build a list of nodes at that level, and then to get the next level take all of the left children of each and then all of the right children of each of each to form the next level. This will disentangle your curious tree and you will visit the nodes in this order:
Position #1
LEFTS = { P#1 }, RIGHTS = { P#2 }
LEFTS = { P#3 P#4}, RIGHTS = { P#5 P#6 }
LEFTS = { P#7 P#8 P#9 P#10 }, RIGHTS = { P#11 P#12 P#13 P#14 }
I've been going through the Neo4J and Neo4J C# client..
The neo4jclient wiki helped me to with node crud operations.. however the wiki ends there abruptly..
I poked around the test methods in source code and managed to understand about relationships and searched online to understand how indexing works.
So far, here's what I have, roughly:
//create indexing on user and car
client.CreateIndex("User", new IndexConfiguration() { Provider = IndexProvider.lucene, Type = IndexType.fulltext }, IndexFor.Node);
client.CreateIndex("Car", new IndexConfiguration() { Provider = IndexProvider.lucene, Type = IndexType.fulltext }, IndexFor.Node);
//create user
client.Create(new User() { Name = "Dovakiin", Job = "Dragon Slayer" });
client.Create(new User() { Name = "Ulfric stormcloak", Job = "Imperial Slayer" });
//create Car
client.Create(new Car() { Name = "Paarthurnax", Modal = 212 });
//User owns car relationship
client.CreateRelationship(userRef, new Owns_CarRelationship(CarRef));
This is where I am stuck now.. When I try to look for the user by name, my cipher query is returning zero results:
start u=node:User(Name="Dovakiin") return u;
and I don't quite understand why it returns zero nodes when clearly
start n=node(*) return n;
shows all nodes.
Am I missing something else while indexing? Or is this not index related at all? Do I not need to add each node to the index?
All I am trying to do, is select the node with a given property: Name = "Dovakiin" in this case.. How do I select this please?
Just to expand on ulkas' answer, if you want to enable auto indexing and found the documentation a little confusing (like I did the first time I read it), this is how you set it up.
Let's say you want to automatically index some node properties; say, "name" and "job". Open up the /conf/neo4j.properties file and you should see something like this:
# Autoindexing
# Enable auto-indexing for nodes, default is false
#node_auto_indexing=true
# The node property keys to be auto-indexed, if enabled
#node_keys_indexable=name,age
You then have to edit the file to the following:
# Autoindexing
# Enable auto-indexing for nodes, default is false
node_auto_indexing=true
# The node property keys to be auto-indexed, if enabled
node_keys_indexable=name,job
Once this is done, in order for auto indexing to take effect, you'll have to restart neo4j. Also, as a side note, any currently existing nodes won't be auto indexed, which means you'll have to recreate them. If you don't want to start from scratch, here's some documentation on how to update them: http://docs.neo4j.org/chunked/milestone/auto-indexing.html#auto-indexing-update-removal (I've never tried it).
Then you can start finding nodes like this:
start n=node:node_auto_index(name="Dovakiin"), or
start n=node:node_auto_index(job="Dragon Slayer")
Or, like this with the C# client:
Node<User> myNode = client.QueryIndex<User>("node_auto_index", IndexFor.Node, "name:Dovakiin").First();, or
Node<User> myNode = client.QueryIndex<User>("node_auto_index", IndexFor.Node, "job:Dragon Slayer").First();
You can do the same thing with with relationships as well, as soon as you set it up in the /conf/neo4j.properties file. You do it exactly the same way as with nodes.
you must manually add the nodes to the index, something like
client.indexRef1.addToIndex(nodeRef, 'name', 'Dovakiin')
client.indexRef2.addToIndex(nodeRef, 'job', 'Dragon Slayer')
there is also an automatic indexing feature in neo4j in case you want the nodes to be automatically added to the index.
I need to get info from an XML-file in C#.
Here is some fragments from the XML.
<Surface id="su-62" surfaceType="InteriorWall">
<Name>S-3-7-I-W-62</Name>
<AdjacentSpaceId spaceIdRef="sp-3-TUIN">
</AdjacentSpaceId>
<AdjacentSpaceId spaceIdRef="sp-7-huizen">
</AdjacentSpaceId>
<CADObjectId>Basic Wall: _omgevingsmuur [184610]</CADObjectId>
</Surface>
...
<Surface id="su-63" surfaceType="ExteriorWall">
<Name>N-4-E-W-63</Name>
<AdjacentSpaceId spaceIdRef="sp-4-onthaal">
</AdjacentSpaceId>
<Opening id="su-63-op-1" openingType="NonSlidingDoor">
</Opening>
<CADObjectId>Basic Wall: _detentiemuur [193431]</CADObjectId>
</Surface>
...
<Surface id="su-282" surfaceType="Shade">
<Name>X-S-282</Name>
<CADObjectId>Basic Roof: Generic - 400mm [190976]</CADObjectId>
</Surface>
As you see there are some surfaces that don't have all the info others have.
I have to know which surfaces are adjacent to which space and if there is an opening or not.
(The ultimate goal is to make an 2d array where you can see which SPACE is adjacent to which SPACE and another array to see if the have a joined opening.)
Assuming you have .NET 3.5 or greater, than this is a good use for LINQ2XML.
You can write a query that will grab the associated areas an identify which space are adjacent to each other.
// Load your XML File into an XDocument object
var xDoc = XDocument.Load(xmlPath);
// this is your query, in the end result with my a Dictionary with the Surface
// Id attribute as the key and the AdjacentSpaceId as the value
var result = (from x in xDoc.Descendants("AdjacentSpaceId")
select new
{
SurfaceId = (String)x.Parent.Attribute("id"),
SurfaceName = (String)x.Parent.Element("Name"),
AdjacentSpace = (String)x
}).GroupBy(sp => sp.SurfaceId)
.ToDictionary(grp => grp.Key,
grp => grp.Select(value => value.AdjacentSpace));
If you are unfamiliar with any of the LINQ varients, I will attempt to explain the query in a bit more detail. The local field containing your query results is just called results. The var type is another .NET 3.5 addition that tells the compiler to identify the type by the right side of the expression. In this case, result will be a Dictionary<String, IEnumerable<String>>.
In the first line of the query:
from x in xDoc.Descendants("AdjacentSpaceId")
You are basically telling LINQ to iterate through all nodes in the XML called AdjacentSpaceId. It doesn't matter where these nodes are located or what the parent nodes are called. The Descendants() method in LINQ can be very powerful for this reason as it means you don't need to know the exact XML Path to a particular node to select it, you just need the name (see my note at the end)
The next lines of the query are defining exactly what you want to return from the query:
select new
{
SurfaceId = (String)x.Parent.Attribute("id"),
SurfaceName = (String)x.Parent.Element("Name"),
AdjacentSpace = (String)x
})
All LINQ queries using this syntax must have a select statement (there is also a method syntax that can be used instead of or in addition too that doesn't need a select necessarily, but this syntax is easier to learn in my opinion).
In the select clause, you are basically defining a new Anonymous Type that has 3 properties, SurfaceId, SurfaceName, and AdjacentSpace. I'll explain one of the assignments and it should let you understand all of them:
SurfaceId = (String)x.Parent.Attribute("id")
In this line, x refers to the initial line of the LINQ query where you were iterating all of your AdjacentSpaceId nodes, so x is of type XElement.
The Parent property of x is going to select the parent node, in this case Surface. (NOTE: If your root node in your XML happens to be called AdjacentSpaceId, then this line is going to throw an exception since the parent of a root node will be null, but seems to be a safe assumption that it won't be a problem in this case). The Parent property on XElement is also going to be another XElement.
The next part is the Attribute method of XElement, and you are selecting the first attribute of node called id.
So effectively you are selecting the id attribute of all parent nodes to each AdjacentSpaceId nodes.
And lastly, I am casting this to a String. There is a Value property that can be used instead, but my personal preference is to cast to a String because if the Attribute("id") method fails (which could happen if there is no attribute called "id"), calling the Value property will throw an exception, whereas casting to a string will just return null in these cases.
The other parts of this select clause are virtually the same.
The rest of this query are effectively separate queries. I just chained them together, but they could easily be broken out and put on their own.
The next piece:
GroupBy(sp => sp.SurfaceId)
is grouping the result of your LINQ query by the SurfaceId property. This is a key step because it sounds like you want to know where spaces are adjacent to each surface and this will effectively group all of the adjacent spaces together by the surface.
If you aren't familiar, sp => sp.SurfaceId is a Lambda Expression for creating anonymous functions or delegates quickly and easily.
And the final piece will take your grouped results and convert them into something more usable, usually a Dictionary<>, or in this case a Dictionary<String, IEnumerable<String>>
ToDictionary(grp => grp.Key,
grp => grp.Select(value => value.AdjacentSpace));
Just one side note about the Descendants() extension method for Linq-to-Xml objects, while it can be very useful as I mentioned above, it can also be very dangerous. When you have multiple nodes in the same XML with the same name but with different purposes or in different parents of the tree, Descendants() will return ALL objects. If you only want to return some of the nodes matching a specific name, you need to filter it down first by using the Element() or Elements() extension methods first to select the proper parent node, then you can safely call Descendants().
TreeNode[] nodes = this.treeview.Nodes.Find(node.Text, true);
if (nodes.Length > 0)
{
int i = nodes[0].Index;
if (nodes.Length > 0)
this.treeview.Nodes.Remove(nodes[0]);
this.treeview.Nodes.Insert(i, nodes[0]);
}
i tried this code,
but the node nodes[0] is not inserting into the particular index.
instead it is adding at the last.
but yes i use treeviewsorter.
Any idea how to insert node without using insert
or using insert effectively with treeviewsorter??
If you have set the TreeViewNodeSorter property to a custom comparer, your TreeView nodes will automatically be sorted using that comparer.
Thus, you can't insert a node in a different position, because the position is decided using the comparer.
But, in your specific case you're removing a node and inserting it back in its original position, and you say it doesn't work when actually it should do.
This, (I'm guessing) could be due to several reasons:
Your comparer implementation is wrong, or peraphs it uses a property that depends on the sorting itself (like Node.Index)
The node you get with Find() (supposing is just one...), belongs to a level lower than root, but you try to remove it from root nodes and add to that level...
Other reasons, we need more code...