How to use parameters correctly in Transactional Cypher HTTP - neo4j

I'm a bit stuck. I'm trying to use parameters in my http request in order to reduce the overhead but I just can't figure out why this won't work:
{
"statements" : [ {
"statement" : "MATCH (n:Person) WHERE n.name = {name} SET n.dogs={dogs} RETURN n",
"parameters" : [{
"name" : "Andres",
"dogs":5
},{
"name" : "Michael",
"dogs":3
},{
"name" : "Someone",
"dogs":2
}
]
}]
}
I've tried just opening a transaction with a STATEMENT and feeding the separate 'rows' in as PARAMETERS on subsequent transactions before I /COMMIT, but no joy.
I know that multiple nodes can be created in a similar from the examples in the manual.
What am I missing?

I've since modified an answer from this post, which seems to work by using a FOREACH statement to allow for multplie 'sets' of parameters.
{
"statements" : [
{
"parameters": {
"props": [
{
"userid": "177032492760",
"username": "John"
},
{
"userid": "177032492760",
"username": "Mike"
},
{
"userid": "100007496328",
"username": "Wilber"
}
]
},
"statement": "FOREACH (p in {props} | MERGE (user:People {id:p.userid}) ON CREATE SET user.name = p.username) "
}
]
}

You can also use UNWIND for this case :
The statement :
UNWIND props as prop
MERGE (user:People {id: {prop}.id}) // and the rest of your query
The parameters :
{"props":[ {"id": 1234, "name": "John"},{"id": 4567, "name": "Chris"}]}
This is what is used on Graphgen to load the generated graphs in a local database from the webapp. http://graphgen.neoxygen.io

Related

How to create dynamic node relation in neo4j for dynamic data?

I was able to create author nodes directly from the json file . But the challenge is on what basis or how we have to link the data. Linking "Author" to "organization". since the data is dynamic we cannot generalize it. I have tried with using csv file but, it fails the conditions when dynamic data is coming. For example one json record contain 2 organization and 3 authors, next record will be different. Different json record have different author and organization to link. organization/1 represent organization1 and organization/2 represents organization 2. Any help or hint will be great. Thank you. Please find the json file below.
"Author": [
{
"seq": "3",
"type": "abc",
"identifier": [
{
"idtype:auid": "10000000"
}
],
"familyName": "xyz",
"indexedName": "MI",
"givenName": "T",
"preferredName": {
"familyName": "xyz1",
"givenName": "a",
"initials": "T.",
"indexedName": "bT."
},
"emailAddressList": [],
"degrees": [],
"#id": "https:abc/2009127993/author/person/3",
"hasAffiliation": [
"https:abc/author/organization/1"
],
"organization": [
[
{
"identifier": [
{
"#type": "idtype:uuid",
"#subtype": "idsubtype:affiliationInstanceId",
"#value": "aff2"
},
{
"#type": "idtype:OrgDB",
"#subtype": "idsubtype:afid",
"#value": "12345"
},
{
"#type": "idtype:OrgDB",
"#subtype": "idsubtype:dptid"
}
],
"organizations": [],
"addressParts": [],
"sourceText": "",
"text": " Medical University School of Medicine",
"#id": "https:abc/author/organization/1"
}
],
[
{
"identifier": [
{
"#type": "idtype:uuid",
"#subtype": "idsubtype:affiliationInstanceId",
"#value": "aff1"
},
{
"#type": "idtype:OrgDB",
"#subtype": "idsubtype:afid",
"#value": "7890"
},
{
"#type": "idtype:OrgDB",
"#subtype": "idsubtype:dptid"
}
],
"organizations": [],
"addressParts": [],
"sourceText": "",
"text": "K University",
"#id": "https:efg/author/organization/2"
}
]
Hi I see that Organisation is part of the Author data, so you have to model it like wise. So for instance (Author)-[:AFFILIATED_WITH]->(Organisation)
When you use apoc.load.json which supports a stream of author objects you can load the data.
I did some checks on your JSON structure with this cypher query:
call apoc.load.json("file:///Users/keesv/work/check.json") yield value
unwind value as record
WITH record.Author as author
WITH author.identifier[0].`idtype:auid` as authorId,author, author.organization[0] as organizations
return authorId, author, organizations
To get this working you will need to create include apoc in the plugins directory, and add the following two lines in the apoc.conf file (create one if it is not there) in the 'conf' directory.
apoc.import.file.enabled=true
apoc.import.file.use_neo4j_config=false
I also see a nested array for the organisations in the output why is that and what is the meaning of that?
And finally I see also in the JSON that an organisation can have a reference to other organisations.
explanation
In my query I use UNWIND to unwind the base Author array. This means you get for every author a 'record' to work with.
With a MERGE or CREATE statement you can now create an Author Node with the correct properties. With the FOREACH construct you can walk over all the Organization entry and create/merge an Organization node and create the relation between the Author and the Organization.
here an 'psuedo' example
call apoc.load.json("file:///Users/keesv/work/check.json") yield value
unwind value as record
WITH record.Author as author
WITH author.identifier[0].`idtype:auid` as authorId,author, author.organization[0] as organizations
// creating the Author node
MERGE (a:Author { id: authorId })
SET a.familyName = author.familyName
...
// walk over the organizations
// determine
FOREACH (org in organizations |
MERGE (o:Organization { id: ... })
SET o.name = org.text
...
MERGE (a)-[:AFFILIATED_WITH]->(o)
// if needed you can also do a nested FOREACH here to process the Org Org relationship
)
Here is the JSON file I used I had to change something at the start and the end
[
{
"Author":{
"seq":"3",
"type":"abc",
"identifier":[
{
"idtype:auid":"10000000"
}
],
"familyName":"xyz",
"indexedName":"MI",
"givenName":"T",
"preferredName":{
"familyName":"xyz1",
"givenName":"a",
"initials":"T.",
"indexedName":"bT."
},
"emailAddressList":[
],
"degrees":[
],
"#id":"https:abc/2009127993/author/person/3",
"hasAffiliation":[
"https:abc/author/organization/1"
],
"organization":[
[
{
"identifier":[
{
"#type":"idtype:uuid",
"#subtype":"idsubtype:affiliationInstanceId",
"#value":"aff2"
},
{
"#type":"idtype:OrgDB",
"#subtype":"idsubtype:afid",
"#value":"12345"
},
{
"#type":"idtype:OrgDB",
"#subtype":"idsubtype:dptid"
}
],
"organizations":[
],
"addressParts":[
],
"sourceText":"",
"text":" Medical University School of Medicine",
"#id":"https:abc/author/organization/1"
}
],
[
{
"identifier":[
{
"#type":"idtype:uuid",
"#subtype":"idsubtype:affiliationInstanceId",
"#value":"aff1"
},
{
"#type":"idtype:OrgDB",
"#subtype":"idsubtype:afid",
"#value":"7890"
},
{
"#type":"idtype:OrgDB",
"#subtype":"idsubtype:dptid"
}
],
"organizations":[
],
"addressParts":[
],
"sourceText":"",
"text":"K University",
"#id":"https:efg/author/organization/2"
}
]
]
}
}
]
IMPORTANT create unique constraints for Author.id and Organization.id!!
In this way you can process any json file with an unknown number of author elements and an unknown number of affiliated organisations

Openlayers bbox strategy

I have bbox strategy for one source of data. Code looks like this:
bbox: function newBboxFeatureSource(url, typename) {
return new ol.source.Vector({
loader: function (extent) {
let u = `${url}&TYPENAME=${typename}&bbox=${extent.join(",")}`;
$.ajax(u).then((response) => {
this.addFeatures(
geoJsonFormat.readFeatures(response)
);
});
},
strategy: ol.loadingstrategy.bbox
});
},
I works fine but... When I pan/move the map then this loader is calling again and add another features which fit to new box. But there is a lot of duplicates then because some of new features are just the same as old.
So I wanted first clear all features using this.clear() before add new features but when I add this command then loader is running all the time and I have "infinitive loop". Do you know why? How can I disable loading new features after calling this.clear()?
edit:
my response with features looks like this:
{ "type": "FeatureCollection", "crs": { "type": "name", "properties":
{ "name": "urn:ogc:def:crs:EPSG::3857" } },
"features": [ { "type": "Feature", "properties": { "ogc_fid": "2",
"name": "AL" }, "geometry": { "type": "MultiPolygon" , "coordinates":
[ [ [ ... ] ] ] } }, { "type": "Feature", "properties": { "ogc_fid":
"3", "name": "B" }, "geometry": { "type": "MultiPolygon" ,
"coordinates": [ [ [ ...] ] ] } } ..... and so on
I've removed coordinates because there was too many of them.
My features are generated by mapserver and are configured in .map file which looks like this:
LAYER
NAME "postcode_area_boundaries"
METADATA
"wfs_title" "Postcode area boundaries"
"wfs_srs" "EPSG:3857"
"wfs_enable_request" "*"
"wfs_getfeature_formatlist" "json"
"wfs_geomtype" "multipolygon"
"wfs_typename" "postcode_area_boundaries"
"wms_context_fid" "id"
"wfs_featureid" "id"
"gml_featureid" "id"
"gml_include_items" "id,postarea,wkb_geometry"
"gml_postarea_alias" "name"
"ows_featureid" "id"
"tinyows_table" "postcode_area_boundaries"
"tinyows_retrievable" "1"
"tinyows_include_items" "id,postarea,wkb_geometry"
END
TYPE POLYGON
STATUS ON
CONNECTIONTYPE POSTGIS
CONNECTION "..."
DATA "wkb_geometry FROM postcode_area_boundaries USING UNIQUE id"
DUMP TRUE
END
To summarize the discussion and answer the initial question:
The features sent by the server need an attribute called id, which must be unique and the same for the feature on every request.
{type: "Feature", id: "some-wfs.1234", properties: { "ogc_fid": 2, ...
See this GitHub Issue for the original comment of ahocevar.
In GeoServer this can be achieved if you set an identifier in your layer.
I guess there is something similar to set in MapServer.
Following up on #bennos comment: you can expose the fid in Mapserver using the FORMATOPTION USE_FEATUREID=true as described in: https://mapserver.org/output/ogr_output.html
USE_FEATUREID=true/false
Starting from MapServer v7.0.2. Defaults to false. Include feature ids in the generated output, if the ows_featureid metadata key is set at the layer level. The featureid column to use should be an integer column. Useful if you need to include an “id” attribute to your geojson output. Use with caution as some OGR output drivers may behave strangely when fed with random FIDs.
OUTPUTFORMAT
NAME "application/json"
DRIVER "OGR/GEOJSON"
MIMETYPE "application/json"
FORMATOPTION "FORM=SIMPLE"
FORMATOPTION "FILENAME=ol-query.json"
FORMATOPTION "STORAGE=memory"
FORMATOPTION "USE_FEATUREID=true"
END

Neo4j Cypher: Importing nested collection by getting Error: Unable to deserialize request: Unexpected character code 99

I have nested collection and I add "Data" then foreach "Data" I add its own "Tags". I found unwind for "Data" with foreach for "Tags".
I wanna add person which I import its info from outside of collection manually.
I execute below Cypher query by statements:
I have imitated from:
Cypher Import Statement
AND
Cypher Unwind
I checked my json via enter link description here And it is validated.
{ "statements": [ { "statement": " WITH { "categories": [ {"dataid" : "11" , "dataname" : "data1" , "datalanguage" : "en" , "datatype" : "type1" ,"content" : "content1" , "tags" : [{"myid" : "11" , "tagid" : 10 , "tagname" : "tag1" }] } , {"dataid" : "22" , "dataname" : "data2" , "datalanguage" : "en" , "datatype" : "type2" ,"content" : "content2" , "tags" : [{"myid" : "22" , "tagid" : 20 , "tagname" : "tag2" }] } ] } AS document UNWIND document.categories AS category MERGE (dt:Data {name: category.dataname}) ON CREATE SET dt.id = category.dataid , dt.type = category.datatype , dt.language = category.datalanguage , dt.content = category.datacontent MERGE (p:Person { name : 'Mahsa' , lastname : 'Mahsa' } ) ON CREATE SET p.id =1 MERGE (p)-[r:owner]->(dt) FOREACH (mytag IN category.tags | MERGE (t:Tag {name: mytag.tagname}) ON CREATE SET t.id = mytag.tagid MERGE (dt)-[r2:tagged { Freq : 12 ]->(t) )" } ] }
But it returns as result: (I checked many times for "Unexpected character" but I could not find )
{"results":[],"errors":[{"code":"**Neo.ClientError.Request.InvalidFormat**","message":"**Unable to deserialize request: Unexpected character ('c' (code 99)): was expecting comma to separate OBJECT entries**\n at [Source: HttpInputOverHTTP#132e16b; line: 1, column: 48]"}]}
I made my nested collection as:
string dataCollection2 = "{ \"categories\": [ {\"dataid\" : \"11\" , \"dataname\" : \"data1\" , \"datalanguage\" : \"en\" , \"datatype\" : \"type1\" ," +
"\"content\" : \"content1\" , \"tags\" : [{\"myid\" : \"11\" , \"tagid\" : 10 , \"tagname\" : \"tag1\" }] }" +
" , {\"dataid\" : \"22\" , \"dataname\" : \"data2\" , \"datalanguage\" : \"en\" , \"datatype\" : \"type2\" ," +
"\"content\" : \"content2\" , \"tags\" : [{\"myid\" : \"22\" , \"tagid\" : 20 , \"tagname\" : \"tag2\" }] } ] }";
var obj1 = JValue.Parse(#"'" + dataCollection2 + "'");
I imported my json to http://jsonlint,com and I got it is validated:
{
"categories": [
{
"dataid": 11,
"dataname": "data1",
"datalanguage": "en",
"datatype": "type1",
"content": "content1",
"tags": [
{
"myid": 11,
"tagid": 10,
"tagname": "tag1"
}
]
},
{
"dataid": 22,
"dataname": "data2",
"datalanguage": "en",
"datatype": "type2",
"content": "content2",
"tags": [
{
"myid": 22,
"tagid": 20,
"tagname": "tag2"
}
]
}
]}
The fundamental problem with the string you are passing to the REST API is that you are not passing a legal Cypher query. Cypher property "maps", which look a bit like JSON, are NOT JSON.
In your case, the important difference is that property names must NOT be delimited by double-quotes. Only string property values can be delimited by double-quotes.
So, categories, dataId, dataName, etc., must not be surrounded by double-quotes.
You also have a typo near the end of the query. [r2:tagged { Freq : 12 ] should be [r2:tagged { Freq : 12} ].
For Adding nested collection to neo4j:
Firstly: For having right input to neo4j as parameter I must remove "{
"categories": [ ] }" from the first and the end of json.
Secondly: I should put this json as parameter to statement
Thirdly: I have to use two "UNWIND and WITH" (instead of using foreach) for separating each row from collection either for parent or for child.
"Foreach" DOES similar to "UNWIND and WITH".
My final collection and query are:
Collection:
{"id" : 1, "name" : "Data1", "language" : "en", "tags": {"id" : 11, "name": "tag 11" } } , {"id" : 2, "name": "Data2" , "tags": [ {"id" : 33, "name": "tag 33"} , {"id" : 44, "name": "tag44"} ] }
Query:
{ "statements": [ { "statement": " UNWIND { datas } AS data MERGE (p:Person { name : 'God' , lastname : 'God' } ) ON CREATE SET p.id =2 MERGE (d:Data {name: data.name}) ON CREATE SET d.id = data.id , d.language = data.language MERGE (p)-[r:owner]->(d) WITH d, data.tags AS mytags UNWIND mytags AS mytag MERGE (t:Tag {name: mytag.name}) ON CREATE SET t.id = mytag.id MERGE (d)-[r2:tagged { Freq : 12 } ]->(t) " , "parameters": { "datas" : [{"id" : 1, "name" : "Data1", "language" : "en", "tags": {"id" : 11, "name": "tag 11" } } , {"id" : 2, "name": "Data2" , "tags": [ {"id" : 33, "name": "tag 33"} , {"id" : 44, "name": "tag44"} ] }] } } ] }

Neo4j cypher with parameters returns success but doesn't create anything?

If I send this:
{
"query" : "MATCH (p) WHERE p.id={id} CREATE (c {props}) CREATE UNIQUE p-[r:CHILD]->c",
"params" : {
"id" : ["{0000-0000-0000-0000}","{0000-0000-0000-0000}","{0000-0000-0000-0004}"],
"props" : [ {
"id" : "{0000-0000-0000-0004}",
"type": 48,
"title" : "TestNode"
},{
"id" : "{0000-0000-0000-0005}",
"type": 49,
"title" : "TestNode"
},{
"id" : "{0000-0000-0000-0006}",
"type": 49,
"title" : "TestNode"
}]
}
}
Via the restful cypher api, I get back "success" but nothing was created. If I send through this:
{
"query" : "MATCH (p) WHERE p.id={id} CREATE (c {props}) CREATE UNIQUE p-[r:CHILD]->c",
"params" : {
"id" : "{0000-0000-0000-0000}",
"props" : [ {
"id" : "{0000-0000-0000-0001}",
"type": 48,
"title" : "TestNode"
},{
"id" : "{0000-0000-0000-0002}",
"type": 49,
"title" : "TestNode"
} ]
}
}
It creates two children of 0000-0000-0000-0000 as expected. So something about the way I'm specifying two arrays isn't working.
I was hoping to be able to create large tree structures by essentially specifying parent ID/child to create parameters. The other option is that I use the latter style cypher and the transactional endpoint... but I'm just not sure what I'm doing wrong in the first one. Any advice much appreciated.
You probably need to do:
MATCH (p) WHERE p.id IN {id}
CREATE (c {props})
CREATE UNIQUE (p)-[r:CHILD]->(c)
The = operator is an exact comparison.

How to specify optional parameter in CREATE query using the Neo4J REST API

I'm exploring the Neo4J REST API, and have stumbled into a problem.
I do a POST to http://localhost:7474/db/data/cypher with the following body:
{
"query": "CREATE (n {name: {name}, description: {description}})",
"params": {
"name": "Test"
}
}
It returns a 400 Bad Request with the following body:
{
"message": "Expected a parameter named description",
"exception": "ParameterNotFoundException",
"fullname": "org.neo4j.cypher.ParameterNotFoundException",
"stacktrace": [
...
]
}
How do I make the description parameter optional?
You need to provide every parameter:
{
"query": "CREATE (n {name: {name}, description: {description}})",
"params": {
"name": "Test",
"description": "sometext"
}
}
Check if description is provided and otherwise set it to empty string.
I think it would be better to built your query on the fly.
If discription is not provided. Built this query:
"CREATE (n {name: {name}})"

Resources