I have the following model snippet:
nodeOrigin: types.maybe(types.reference(nodeState)),
node: types.maybe(nodeState),
And I start editing a node by the following function (the original node i saved so it can be used in a undo() function):
startEditing(node) {
self.nodeOrigin = node;
self.node = clone(node);
}
And in my render method the editing node is used like this:
<form className="form">
<TextField margin='dense' value={getStore().node["name"]} />
</form>
But when I change the name and print the content of both the node and the original node, they have both the changed name. NodeOriginal should contain the original name. What am I doing wrong?
types.reference is tied to types.identifier. The thing you probably misunderstand here, is that types.reference references the types.identifier property of the given node and not the node itself.
When you clone you do not alter an id of the original node. types.reference is resolved on the fly from identifier cache by the given id, so it will always reference the node with the given id.
Moreover, given that id cannot be altered after initialization and that it should be unique across the whole node tree, I'd conclude that nodes having types.identifier property are not meant to be cloned with clone utility.
Alternatively, you could take a snapshot of the node you want to clone, manually update types.identifier property and create a new node from that. Something like:
const cloneWithNewId = (node, id) =>
getType(node).create(Object.assign({}, getSnapshot(node), { id }));
Related
I am currently working on a POC by using ThingsBoard PE.
Our raw data contains [Asset] [Attributes].
Data flow:
[IoT cloud] --https webhook carry raw data--> [ThingsBoard PE HTTP INTEGRATION] --uplink--> [ThingsBoard PE Data Converter]
My question is: is it possible to apply [Rule Chain] after [ThingsBoard PE Data Converter]? Therefore, the device can auto create relationship with [Asset] by the [Attribute], instead of [AssetName].
Example data after data converter process:
{
"deviceName": "ABC",
"deviceType": "temperature",
"attributes": {
"asset_id": 6 // <-- the id is used in asset attribute
},
"telemetry": {
"temperature": 39.43
}
}
Answering your two, separate questions:
is it possible to apply [Rule Chain] after [ThingsBoard PE Data Converter]?
Yes it is possible. Once your data is successfully integrated and you are receiving it, you can access it using the [Input] Rule Node (the default green one that is always there when you create a Rule) and route it to any other node you need.
Therefore, the device can auto create relationship with [Asset] by the [Attribute], instead of [AssetName].
So, you want the relationship to take your custom attribute and use that as the pattern that identifies the Asset you want to create the relationship from.
The PE edition already has the Create Relation Node. However, seems that as it is one is not able to do what you seek (has no option to specify custom Asset id).
However, two options you got are:
Create a Custom Rule Node that does what you want. For that try checking the Rule Node Development page from Thingsboard. You can use the Create Relation Node as base and work from there. This can be a longer solution than doing...
Enrich your incoming message's metadata, adding your desired attribute. The Create Relation Node allows you to use variables on your message's metadata in your Name and Type patterns, as seen from this screenshot I took from that node:
This allows us a workaround to what you want to do: Add a Script Transformation Node that adds attributes.asset_id to the metadata, for example as metadata.asset_id, so you can then use it as ${asset_id} on your Name and Type patterns.
For example, your Transform() method of such Script Transformation Node should look something like this:
function Transform(msg, metadata, msgType){
//assumming your desired id is msg.attributes.asset_id, add it to the metadata
metadata.asset_id = msg.attributes.asset_id;
//return the message, in your case to the Create Relation Node
return {msg: msg, metadata:metadata, msgType:msgType};
}
Finally, your Rule should be connected like this:
[Input] -> [Script Node] -> [Create Relation Node] -> [...whatever else you like]
I want to delete few particular job work spaces present in a slave node. For this I would like to fetch all items present in that particular slave, and then delete the workspace of the item that matches the particular job.
For this, I know
Jenkins.instance.items gives me a list of all items present in that Jenkins instance (in my case it would be the master node).
whereas
Jenkins.items gives me list of all items present in Jenkins.
I need something like,
node.items which will give me list of all items present in that particular node,
where node will be one of Jenkins.instance.nodes
My code currently, looks like
for (node in Jenkins.instance.nodes) {
println("node: " + node.getDisplayName())
if (node.getDisplayName() == 'abc' || node.getDisplayName() == 'def') {
performCleanup(node, node.items)
}
}
def performCleanup(def node, def items) {
for (item in items) {
workspacePath = node.getWorkspaceFor(item)
if (workspacePath == null) {
println(".... could not get workspace path")
continue
}
pathAsString = workspacePath.getRemote()
if (workspacePath.exists()) {
workspacePath.deleteRecursive()
println(".... deleted from location " + pathAsString)
} else {
println(".... nothing to delete at " + pathAsString)
}
}
}
I suppose that jenkins nodes do not possess items as such - all the "items", which in your case I believe to be hudson.model.TopLevelItem, exist on master instance, thus nodes do not hold information what they do have. More than that, after looking into https://javadoc.jenkins.io/hudson/model/Node.html#getWorkspaceFor-hudson.model.TopLevelItem- source code, it looks like this method will not help you as well - as if no work space will be found, it will create it, thus instead of deleting you will spam dozens more.
In your case there is another no-brainer available:
If you have particular jobs you want to clear from the particular nodes - just iterate through each node root children - https://javadoc.jenkins.io/hudson/model/Node.html#getRootPath-- and check if folder with job name exists - then clean. That is it, all workspaces should be under root, thus just search by directory name.
I have a question about the query based on the predefined constraints in PopotoJs. In this example, the graph can be filtered based on the constraints defined in the search boxes. The sample file in this example visualizations folder, constraint is only defined for "Person" node. It is specified in the sample html file like the following:
"Person": {
"returnAttributes": ["name", "born"],
"constraintAttribute": "name",
// Return a predefined constraint that can be edited in the page.
"getPredefinedConstraints": function (node) {
return personPredefinedConstraints;
},
....
In my graph I would like to apply that query function for more than one node. For example I have 2 nodes: Contact (has "name" attribute) and Delivery (has "address" attribute)
I succeeded it by defining two functions for each nodes. However, I also had to put two search box forms with different input id (like constraint1 and constraint2). And I had to make the queries in the associated search boxes.
Is there a way to make queries which are defined for multiple nodes in one search box? For example searching Contact-name and/or Delivery-adress in the same search box?
Thanks
First I’d like to specify that the predefined constraints feature is still experimental (but fully functional) and doesn’t have any documentation yet.
It is intended to be used in configuration to filter data displayed in nodes and in the example the use of search boxes is just to show dynamically how it works.
A common use of this feature would be to add the list of predefined constraint you want in the configuration for every node types.
Let's take an example:
With the following configuration example the graph will be filtered to show only Person nodes having "born" attribute and only Movie nodes with title in the provided list:
"Person": {
"getPredefinedConstraints": function (node) {
return ["has($identifier.born)"];
},
...
}
"Movie": {
"getPredefinedConstraints": function (node) {
return ["$identifier.title IN [\"The Matrix\", \"The Matrix Reloaded\", \"The Matrix Revolutions\"]"];
},
...
}
The $identifier variable is then replaced during query generation with the corresponding node identifier. In this case the generated query would look like this:
MATCH (person:`Person`) WHERE has(person.born) RETURN person
In your case if I understood your question correctly you are trying to use this feature to implement a search box to filter the data. I'm still working on that feature but it won't be available soon :(
This is a workaround but maybe it could work in your use case, you could keep the search box value in a variable:
var value = d3.select("#constraint")[0][0].value;
inputValue = value;
Then use it in the predefined constraint of all the nodes type you want.
In this example Person will be filtered based on the name attribute and Movie on title:
"Person": {
"getPredefinedConstraints": function (node) {
if (inputValue) {
return ["$identifier.name =~ '(?i).*" + inputValue + ".*'"];
} else {
return [];
}
},
...
}
"Movie": {
"getPredefinedConstraints": function (node) {
if (inputValue) {
return ["$identifier.title =~ '(?i).*" + inputValue + ".*'"];
} else {
return [];
}
},
...
}
Everything is in the HTML page of this example so you can view the full source directly on the page.
#Popoto, thanks for the descriptive reply. I tried your suggestion and it worked pretty much well. With the actual codes, when I make a query it was showing only the queried node and make the other node amount zero. I wanted to make a query which queries only the related node while the number of other nodes are still same.
I tried a temporary solution for my problem. What I did is:
Export the all the node data to JSON file, search my query constraint in the exported JSONs, if the file is existing in JSON, then run the query in the related node; and if not, do nothing.
With that way, of course I needed to define many functions with different variable names (as much as the node amount). Anyhow, it is not a propoer way, bu it worked for now.
I created the following nodes and relationship in neo4j
CREATE (United_States:Citizenship { type : “Naturalized”})
CREATE (United_States:Citizenship { type : “Native_Born”})
CREATE (uid:Person { unique_id: 'A23AF39D-BEED-4FFC-B080-1362920FA7A8', id_type: '128bit_UUID' })
MATCH (uid:Person),(Native_Born:Citizenship) WHERE uid:Person="A23AF39D-BEED-4FFC-B080-1362920FA7A8" CREATE (uid) <- [ r:PersonUniqueIdentifier ] -> (Native_Born)
CREATE (fn:Person { first_name:'Willie', id_type:'128bit_UUID'})
CREATE (ln:Person { last_name:'Armstrong', id_type:'128bit_UUID'}))
CREATE CONSTRAINT ON (uid:Person) ASSERT Person.unique_id IS UNIQUE
CREATE INDEX ON :Person(unique_id)
I do not see the 'PersonUniqueIdentifier' Relation between the Citizenship node and id:Person node on the graph.
Screen shot of graph
Firstly, I would make a habit of doing the indexes/constraints first. There's not a lot of data here, but if you add an index after adding the data, it will need to go through all your nodes first. Also, creating a constraint also adds an index for you, so no need for that line. It seems like you're mixing up variables here, so refactoring a bit:
CREATE CONSTRAINT ON (person:Person) ASSERT person.unique_id IS UNIQUE
Also your Citizenship CREATEs are using the same variable name. I don't know if that would necessarily cause a problem, but it's simpler to do this anyway:
CREATE (:Citizenship { type : “Naturalized”}), (:Citizenship { type : “Native_Born”})
This statement looks fine to me (though, again, you could lose the variable if you wanted to):
CREATE (person:Person { unique_id: 'A23AF39D-BEED-4FFC-B080-1362920FA7A8', id_type: '128bit_UUID' })
Here there are a few problems. Here's how I would refactor it:
MATCH (person:Person),(citizenship:Citizenship)
WHERE
person.unique_id="A23AF39D-BEED-4FFC-B080-1362920FA7A8",
citizenship.type = 'Native_Born'
CREATE (person)-[:HAS_CITIZENSHIP]->(citizenship)
I'm not really sure what you want to do here. It seems like you want to create one person, so I would do this:
CREATE (:Person { first_name:'Willie', id_type: '128bit_UUID', last_name:'Armstrong'})
I have a Neo4j database containing information on Congressmen. The problem I'm having is if there is a vacant position. When this happens I am using the same key:value in the "Congressmen" index. I tried the code below because in the py2neo documentation it states that the add function is idempotent
#Check if we have any vacancies and if so if they match the one that we currently want to add
query="start n=node:Congressmen('website:N/A') return n"
result= cypher.execute(graph_db, query.encode('utf-8', errors='ignore'))
#Match what we already have
if str(result[0]) != "[]":
#create is idempotent so will only create a new node if properties are different
rep, = graph_db.create({"name" : userName, "website" : web, "district" : int(district), "state" : child[2].text, "party" : child[4].text, "office" : child[5].text, "phone" : child[6].text, "house" : "House of Representatives"})
cong = graph_db.get_or_create_index(neo4j.Node, "Congressmen")
# add the node to the index
cong.add("website", web, rep)
When I checked the interface after running the code 3 times I have duplicate nodes.
Is it possible to prevent the nodes from duplicating and still be able to index them using the same key/value?
The Index.add method is certainly idempotent: the same entity can only be added once to a particular entry point. The GraphDatabaseService.create method is not however. Each time you run the create method, a new node is created and each run of add appends that new node to the index. You probably want to use the Index.add_if_none, Index.create_if_none or Index.get_or_create method instead.