I am using the cypher rest api manually for this.
I would like to return the data in a way that is easy to parse.
Here's an example of some data with the same sort of relationships I'm dealing with:
(me:Person:Normal),
(dad:Person:Geezer),
(brother:Person:Punk),
(niece:Person:Tolerable),
(daughter:Person:Awesome),
(candy:Rule),
(tv:Rule),
(dad)-[:HAS_CHILD {Num:1}]->(brother),
(dad)-[:HAS_CHILD {Num:2}]->(me),
(me)-[:HAS_CHILD {Num:1}]->(daughter),
(brother)-[:HAS_CHILD {Num:1}]->(niece),
(me)-[:ALLOWS]->(candy),
(me)-[:ALLOWS]->(tv)
I want to get all of the HAS_CHILD relationships and if any of those child nodes have :ALLOWS relationships I want the ids of those too.
so if I were to do something like...
START n=node({idofdad}) MATCH n-[r?:HAS_CHILD]->h-[?:ALLOWS]->allowed
WITH n, r, h, collect(ID(allowed)) as allowedIds
WITH n, r,
CASE
WHEN h IS NOT NULL THEN [ID(h), LABELS(h), r.Num?, allowedIds]
ELSE NULL
END as has
RETURN
LABELS(n) as labels,
ID(n) as id,
n as node,
COLLECT(has) as children;
The :HAS_CHILD may not exist, so I have to do this wierd case thing.
The data that comes back is 'ok' but the JSON mapper that I have (Newtonsoft) doesn't make it easy to map an array to an object (meaning I know that array index[0] is ID of the (children) collections.
The results of the above look like this:
{
"columns": ["labels", "id", "node", "children"],
"data": [
["Person", "Geezer"],
6,
{},
[
[7, ["Person", "Normal"], 2, [2, 1]],
[5, ["Person", "Punk"], 1, []]
]
]
}
Since this is more/less a document and it'd be easier to map the 'children' column I'd like to get something that looks like this:
{
"columns": ["labels", "id", "node", "children"],
"data": [
["Person", "Geezer"],
6,
{},
[
[
"id": 7,
"labels": ["Person", "Normal"],
"ChildNumber": 2,
"AllowedIds": [2, 1]
},
{
"id": 5,
"labels": ["Person", "Punk"],
"ChildNumber": 1,
"AllowedIds": []
}
]
]
}
I would expect the query to look something like:
START n=node({idofdad}) MATCH n-[r?:HAS_CHILD]->h-[?:ALLOWS]->allowed
WITH n, r, h, collect(ID(allowed)) as allowedIds
WITH n, r,
CASE
WHEN h IS NOT NULL THEN
{ id: ID(h), labels: LABELS(h), ChildNumber: r.Num?, AllowedIds: allowedIds }
ELSE NULL
END as has
RETURN
LABELS(n) as labels,
ID(n) as id,
n as node,
COLLECT(has) as children;
Is this even remotely possible?
Related
I have two nodes A and B (A->B) connected via two relationships. Those 2 relationships are unique. For example, you can think of the two rels as two paths from A to B at two different times. Converting them to one relationship is not viable at this time.
For displaying in UI, query result should be a tree structure. Used following query to get the tree
MATCH p=(n:Label1 {name:'main'})-[:calls*..2]->(m)
WITH COLLECT(p) AS ps
CALL apoc.convert.toTree(ps) yield value
RETURN value;
the resulting tree is
Preferred result is
How can I get a tree with duplicate children is more than one relationship exists between same two nodes?
Thanks.
I offer a hack on this SO question. Although it is not exactly the resulting nested document as the apoc function but I think it can server your purpose.
MATCH p=(n:Label1 {name:'main'})-[:calls*..2]->(m)
// each path will be returned
WITH COLLECT(p) AS ps, p, n
CALL apoc.convert.toTree(ps) yield value
// return node n and it's relationships as a nested document
RETURN {_id: ID(n), _type: labels(n), node:n, relationships: collect(value['calls'][0])} as result
Sample result:
[
{
"result": {
"_id": 31,
"_type": [
"Label1"
],
"node": {
"name": "main"
},
"relationships": [
{
"calls.y": "prop y",
"_type": "Main",
"_id": 32
},
{
"_type": "Main",
"_id": 32,
"calls.x": "prop x"
}
]
}
}
]
For example I have list as:
List<Map<String, dynamic>> employees = [
{"name": 'Kris', 'departmentId': 18, CityId: 1},
{"name": 'Ana', 'departmentId': 18, CityId: 2},
{"name": 'Monty', 'departmentId': 18, CityId: 3},
{"name": 'John', 'departmentId': 18, CityId: 4},
];
If you have a List, and you want to make sure that all items in the list have a certain property, you can use List.every. For example:
bool isEven(int n) => n % 2 == 0;
[1, 2, 3, 4].every(isEven) // false, some values are not even
[2, 4, 6, 8].every(isEven) // true, every value is even
In your case, you can check that every value of departmentId has the same value as the first (if you don't know that id ahead of time).
bool allHaveSameDepartment(List<Map<String, dynamic>> employees) {
bool sameIdAsFirst(Map<String, dynamic> employee) => employee['departmentId'] == employees[0]['departmentId'];
return employees.every(sameIdAsFirst);
}
Note that in this case, an empty list will return true (https://en.wikipedia.org/wiki/Vacuous_truth). You may wish to override this behaviour by checking employees.isEmpty at the start and maybe throwing an error or returning false.
I got the solution by using toSet().
Solution: employees.map((value) => value.departmentId).toSet().length.
I have this relatively complex search query that's already being built and working with perfect sorting.
But I think here searching is slow just because of script so all I want to remove script and write query accordingly.
current code :-
"sort": [
{
"_script": {
"type": "number",
"script": {
"lang": "painless",
"source": "double pscore = 0;for(id in params.boost_ids){if(params._source.midoffice_master_id == id){pscore = -999999999;}}return pscore;",
"params": {
"boost_ids": [
3,
4,
5
]
}
}
}
}]
Above code explaination:-
For example, if a match query would give a result like:
[{m_id: 1, name: A}, {m_id: 2, name: B}, {m_id: 3, name: C}, {m_id: 4, name: D}, ...]
So I want to boost document with m_id array [3, 4, 5] which would then transform the result into:
[{m_id: 3, name: C}, {m_id: 4, name: D}, {m_id: 1, name: A}, {m_id: 2, name: B}, ...]
You can make use of the below query using Function Score Query(for boosting) and Terms Query (used to query array of values)
Note that the logic I've mentioned is in the should clause of the bool query.
POST <your_index_name>/_search
{
"query": {
"bool": {
"must": [
{
"match_all": {} //just a sample must clause to retrieve all docs
}
],
"should": [
{
"function_score": { <---- Function Score Query
"query": {
"terms": { <---- Terms Query
"m_id": [
3,4,5
]
}
},
"boost": 100 <---- Boosting value
}
}
]
}
}
}
So basically, you can remove the sort logic completely and add the above function query in your should clause, which would give you the results in the order you are looking for.
Note that you'd have to find a way to add the logic correctly in case if you have much complex query, and if you are struggling with anything, do let me know. I'd be happy to help!!
Hope this helps!
This request below usually works when I have a match on c_from and c_to:
MATCH (u:User {uid: $userId})
WITH u UNWIND $statements as statement
WITH u, statement
UNWIND statement.conceptsRelations as conceptsRelation
MATCH (c_from:Concept{name: conceptsRelation.from})
MATCH (c_to:Concept{name: conceptsRelation.to})
CREATE (c_from)-[:TO {context:conceptsRelation.context,statement:conceptsRelation.statement,user:u.uid,timestamp:conceptsRelation.timestamp, uid:apoc.create.uuid(), gapscan:conceptsRelation.gapscan, weight: conceptsRelation.weight}]->(c_to)
WITH u, statement
UNWIND statement.mentionsRelations as mentionsRelation
MATCH (m_from:Concept{name: mentionsRelation.from})
MATCH (m_to:Concept{name: mentionsRelation.to}) return m_from, m_to
However, as soon as I don't have any match the last part of the query (the last UNWIND) does not get executed. I checked it by removing the 2nd UNWIND and then the 3rd one works again. As in:
MATCH (u:User {uid: $userId})
WITH u UNWIND $statements as statement
WITH u, statement
UNWIND statement.mentionsRelations as mentionsRelation
MATCH (m_from:Concept{name: mentionsRelation.from})
MATCH (m_to:Concept{name: mentionsRelation.to}) return m_from, m_to
Just in case, my params are:
{
"userId": "15229100-b20e-11e3-80d3-6150cb20a1b9",
"contextNames": [
{
"uid": "af8debb0-1f71-11e9-a572-691cc47b060f",
"name": "dsfasdf"
}
],
"statements": [
{
"text": "#submit desire",
"concepts": [
"desire"
],
"mentions": [
"#submit"
],
"timestamp": 15482915128250000,
"name": "#desire ##submit ",
"uid": "2bd1f170-1f73-11e9-a508-0d8a16ad5cf6",
"uniqueconcepts": [
"desire"
],
"conceptsRelations": [],
"mentionsRelations": [
{
"from": "desire",
"to": "#submit",
"context": "af8debb0-1f71-11e9-a572-691cc47b060f",
"statement": "2bd1f170-1f73-11e9-a508-0d8a16ad5cf6",
"user": "15229100-b20e-11e3-80d3-6150cb20a1b9",
"timestamp": 15482915128250000,
"uid": "apoc.create.uuid()",
"gapscan": "1",
"weight": 3
}
],
"uniquementions": [
"#submit"
]
}
],
"timestamp": 15482915128250000
}
This seems like a strange illogical behavior. Any idea why it arises? Thanks!
The use of UNWIND is to turn a list into rows
If you have an empty list you get zero rows.
You could add an element to the list if it's empty:
UNWIND case when size(list) = 0 then [null] else list end
or
UNWIND list + null
And then skip the special value in your 2nd part.
I have four nodes that -[belongTo]-> (ContainerNode)
I want the json to return as a single container node which contains an array of all the nodes that link to it. For example:
"nodes": [
{
"id": "240",
"name":"MyNodeContainer",
"Type": "ContainerNode"
"SubNodes": [
{
"id": "1",
"name":"MyNodeA",
"Type": "node"
},
{
"id": "2",
"name":"MyNodeB",
"Type": "node"
}
]
},
It seems simple but all i can get is the default of all nodes being returned as equal. I want the result to make it clear that the container node is separate from the rest. An array property seems most intuitive but i would also be content with two lists - one for the single nodeContainer and one for the contained nodes
Does something like this steer you towards your end goal? It builds a collection of the contained nodes and then returns it as a property of the ContainerNode.
MATCH (c:ContainerNode)<-[:BELONGS_TO]-(n:Node)
WITH c, collect({ id: id(n), name: n.name, type: labels(n)[0] }) AS nodes
WITH { id: id(c), name: c.name, type: labels(c)[0], SubNodes: nodes } AS containerNode
RETURN {nodes: collect(containerNode) }