Excluding from a Cypher query paths that go through a specific type of node - neo4j

what I need is to refine this query:
MATCH
(u:User)<-[:CAN_SEE]-(c:Company)-[:CAN_SEE*]->(tc:UserView)
WHERE NOT (u:User)<-[:CAN_SEE]-(c:Company)-[:CAN_SEE*]->(:User)-[:CAN_SEE*]->(tc:UserView)
AND u.entityId=$userId and tc.entityId=$targetUserViewId
RETURN tc
I need to find UserView nodes directly visible from User u without passing through another User.
EDIT: I'M NOT searching "what users are directly pointing to a given view". I want to know what views can be reached from a user passing through his own attached company and potentially other nodes (not presented in the example graph). See sample data and the last phrase of the question for a reference of the intended result.
The query works fine if there is a single path to the UserView: if the path is direct, tc is returned; else, if the path goes through another user, no result is returned.
The problem arises if there are more paths from u to tc, some legal, and some not: I want that tc is returned, because at least a good path exists, but I get no result.
I understand that my query is wrong, because it says "search a tc that can't be accessed passing from another user", but what I really need is "search a tc that can be accessed in some manner without passing from another user"...
How can I fix my query?
Sample data:
[
{
"n": {
"identity": 0,
"labels": [
"Tenant",
"VisibilityGroup"
],
"properties": {
"name": "Tenant 2b2fa6af-b18d-4839-bb61-1657a8573b37",
"entityId": "2b2fa6af-b18d-4839-bb61-1657a8573b37",
"id": "a5910cbe-0084-4e71-a722-724653b96a4e",
"entityRef": "com.tierra.userservice.model.entity.Tenant:2b2fa6af-b18d-4839-bb61-1657a8573b37",
"isViewItem": false
}
}
},
{
"n": {
"identity": 1,
"labels": [
"Company",
"VisibilityGroup"
],
"properties": {
"name": "Company d88cac2a-3c11-4088-8f9c-a6a44edf7023",
"entityId": "d88cac2a-3c11-4088-8f9c-a6a44edf7023",
"id": "2d5e3c31-ff86-410d-8aa5-6d638a38141b",
"entityRef": "com.tierra.userservice.model.entity.company.Company:d88cac2a-3c11-4088-8f9c-a6a44edf7023",
"isViewItem": false
}
}
},
{
"n": {
"identity": 2,
"labels": [
"User",
"VisibilityGroup"
],
"properties": {
"name": "User 76f1bf95-6819-4aea-b1c3-4d138eb40cd3",
"entityId": "76f1bf95-6819-4aea-b1c3-4d138eb40cd3",
"id": "00b77fc9-7c90-4e89-ae24-3fe2468afea5",
"entityRef": "com.tierra.userservice.model.entity.user.User:76f1bf95-6819-4aea-b1c3-4d138eb40cd3",
"isViewItem": false
}
}
},
{
"n": {
"identity": 3,
"labels": [
"Tenant",
"VisibilityGroup"
],
"properties": {
"name": "Tenant 7b8f4284-3e55-4e40-a8e8-6aee0b22dc97",
"entityId": "7b8f4284-3e55-4e40-a8e8-6aee0b22dc97",
"id": "55ad9591-0ec1-44b4-ba20-ff1218e0edce",
"entityRef": "com.tierra.userservice.model.entity.Tenant:7b8f4284-3e55-4e40-a8e8-6aee0b22dc97",
"isViewItem": false
}
}
},
{
"n": {
"identity": 4,
"labels": [
"Company",
"VisibilityGroup"
],
"properties": {
"name": "Company 9eddb71f-1126-443f-8e87-bf0ff14ef582",
"entityId": "9eddb71f-1126-443f-8e87-bf0ff14ef582",
"id": "ccc3e2e7-2001-401d-b199-1aa2e3b99d28",
"entityRef": "com.tierra.userservice.model.entity.company.Company:9eddb71f-1126-443f-8e87-bf0ff14ef582",
"isViewItem": false
}
}
},
{
"n": {
"identity": 5,
"labels": [
"User",
"VisibilityGroup"
],
"properties": {
"name": "User ffe8ac6a-b1fd-43a2-bb5c-112aba2e5288",
"entityId": "ffe8ac6a-b1fd-43a2-bb5c-112aba2e5288",
"id": "eec0daf3-52c5-4ebc-8e4d-2a93316ea40b",
"entityRef": "com.tierra.userservice.model.entity.user.User:ffe8ac6a-b1fd-43a2-bb5c-112aba2e5288",
"isViewItem": false
}
}
},
{
"n": {
"identity": 6,
"labels": [
"Company",
"VisibilityGroup"
],
"properties": {
"name": "Company 0738f2f7-2810-4358-a291-94a5e919469d",
"entityId": "0738f2f7-2810-4358-a291-94a5e919469d",
"id": "5657308e-a20e-4199-b92b-5659f36c39a9",
"entityRef": "com.tierra.userservice.model.entity.company.Company:0738f2f7-2810-4358-a291-94a5e919469d",
"isViewItem": false
}
}
},
{
"n": {
"identity": 7,
"labels": [
"User",
"VisibilityGroup"
],
"properties": {
"name": "User 5c4fef7f-1a41-4b09-a0f5-2034510dcf92",
"entityId": "5c4fef7f-1a41-4b09-a0f5-2034510dcf92",
"id": "d0b9798c-b7de-4761-a5a7-eaed4ca8a058",
"entityRef": "com.tierra.userservice.model.entity.user.User:5c4fef7f-1a41-4b09-a0f5-2034510dcf92",
"isViewItem": false
}
}
},
{
"n": {
"identity": 8,
"labels": [
"UserView",
"VisibilityGroup"
],
"properties": {
"name": "UserView 72bed633-c422-40f9-a7e1-cb41af27d70a",
"entityId": "72bed633-c422-40f9-a7e1-cb41af27d70a",
"id": "c5690b5a-5376-47fe-82dc-ea91799175f6",
"entityRef": "com.tierra.userservice.model.entity.user.UserView:72bed633-c422-40f9-a7e1-cb41af27d70a",
"isViewItem": false
}
}
},
{
"n": {
"identity": 9,
"labels": [
"UserView",
"VisibilityGroup"
],
"properties": {
"name": "UserView fbd5cb12-0685-46e6-a672-2770f2140bf5",
"entityId": "fbd5cb12-0685-46e6-a672-2770f2140bf5",
"id": "185f1b3f-8737-4ef0-b0af-bdee05352d5a",
"entityRef": "com.tierra.userservice.model.entity.user.UserView:fbd5cb12-0685-46e6-a672-2770f2140bf5",
"isViewItem": false
}
}
}
]
Now execute:
MATCH
(u:User)<-[:CAN_SEE]-(c:Company)-[:CAN_SEE*]->(tc:UserView)
WHERE NOT (u:User)<-[:CAN_SEE]-(c:Company)-[:CAN_SEE*]->(:User)-[:CAN_SEE*]->(tc:UserView)
AND u.entityId='76f1bf95-6819-4aea-b1c3-4d138eb40cd3' and tc.entityId='fbd5cb12-0685-46e6-a672-2770f2140bf5'
RETURN tc
This returns nothing, I would like to have the view fbd5cb12-0685-46e6-a672-2770f2140bf5, id=9 because a path exists between the user and the view NOT passing through another User.

This seems to do the trick, I hope it's correct... but I think there should be a much better solution using another description of the path...
MATCH p=
(u:User)<-[:CAN_SEE]-(c:Company)-[:CAN_SEE*]->(tc:UserView)
WHERE u.entityId='76f1bf95-6819-4aea-b1c3-4d138eb40cd3' and tc.entityId='fbd5cb12-0685-46e6-a672-2770f2140bf5'
AND none(x IN NODES(p) WHERE ( 'User' IN LABELS(x) and x<>u ))
RETURN tc

... find UserView nodes directly visible from User u without passing
through another User
MATCH (tc:UserView)<-[:CAN_SEE]-(u:User)
WHERE u.entityId='76f1bf95-6819-4aea-b1c3-4d138eb40cd3'
RETURN tc
Or, if you want to limit it to a specific UserView:
MATCH (tc:UserView)<-[:CAN_SEE]-(u:User)
WHERE
u.entityId='76f1bf95-6819-4aea-b1c3-4d138eb40cd3' AND
tc.entityId='fbd5cb12-0685-46e6-a672-2770f2140bf5'
RETURN tc

Related

Twilio IVR Speech Recognition

I'm new to developing IVR with twilio studio so I started with the basic template and even that's not working.
This is the log:
LOG
Split Based On...
DETAIL
Input evaluated to 'Sales.' from '{{widgets.gather_input.SpeechResult}}'
Transitioning to 'say_play_1' because 'Sales.' did not match any expression
The split is set to "Equal to" sales which then connects the call to a number. It's obviously recognizing the correct speech input but still not working. Any ideas?
{
"description": "IVR",
"states": [
{
"name": "Trigger",
"type": "trigger",
"transitions": [
{
"event": "incomingMessage"
},
{
"next": "gather_input",
"event": "incomingCall"
},
{
"event": "incomingRequest"
}
],
"properties": {
"offset": {
"x": 250,
"y": 50
}
}
},
{
"name": "gather_input",
"type": "gather-input-on-call",
"transitions": [
{
"next": "split_key_press",
"event": "keypress"
},
{
"next": "split_speech_result",
"event": "speech"
},
{
"event": "timeout"
}
],
"properties": {
"voice": "alice",
"speech_timeout": "auto",
"offset": {
"x": 290,
"y": 250
},
"loop": 1,
"hints": "support,sales",
"finish_on_key": "",
"say": "Hello, how can we direct your call? Press 1 for sales, or say sales. To reach support, press 2 or say support.",
"language": "en",
"stop_gather": false,
"gather_language": "en-US",
"profanity_filter": "false",
"timeout": 5
}
},
{
"name": "split_key_press",
"type": "split-based-on",
"transitions": [
{
"event": "noMatch"
},
{
"next": "connect_call_to_sales",
"event": "match",
"conditions": [
{
"friendly_name": "1",
"arguments": [
"{{widgets.gather_input.Digits}}"
],
"type": "equal_to",
"value": "1"
}
]
},
{
"next": "connect_call_to_support",
"event": "match",
"conditions": [
{
"friendly_name": "2",
"arguments": [
"{{widgets.gather_input.Digits}}"
],
"type": "equal_to",
"value": "2"
}
]
}
],
"properties": {
"input": "{{widgets.gather_input.Digits}}",
"offset": {
"x": 100,
"y": 510
}
}
},
{
"name": "split_speech_result",
"type": "split-based-on",
"transitions": [
{
"next": "say_play_1",
"event": "noMatch"
},
{
"next": "connect_call_to_sales",
"event": "match",
"conditions": [
{
"friendly_name": "sales",
"arguments": [
"{{widgets.gather_input.SpeechResult}}"
],
"type": "equal_to",
"value": "sales"
}
]
},
{
"next": "connect_call_to_support",
"event": "match",
"conditions": [
{
"friendly_name": "support",
"arguments": [
"{{widgets.gather_input.SpeechResult}}"
],
"type": "equal_to",
"value": "support"
}
]
}
],
"properties": {
"input": "{{widgets.gather_input.SpeechResult}}",
"offset": {
"x": 510,
"y": 510
}
}
},
{
"name": "connect_call_to_sales",
"type": "connect-call-to",
"transitions": [
{
"event": "callCompleted"
}
],
"properties": {
"offset": {
"x": 100,
"y": 750
},
"caller_id": "{{contact.channel.address}}",
"noun": "number",
"to": "12222222",
"timeout": 30
}
},
{
"name": "connect_call_to_support",
"type": "connect-call-to",
"transitions": [
{
"event": "callCompleted"
}
],
"properties": {
"offset": {
"x": 520,
"y": 750
},
"caller_id": "{{contact.channel.address}}",
"noun": "number",
"to": "12222222",
"timeout": 30
}
},
{
"name": "say_play_1",
"type": "say-play",
"transitions": [
{
"next": "gather_input",
"event": "audioComplete"
}
],
"properties": {
"offset": {
"x": 710,
"y": 200
},
"loop": 1,
"say": "not valid choice."
}
}
],
"initial_state": "Trigger",
"flags": {
"allow_concurrent_calls": true
}
}
Twilio developer evangelist here.
That is weird behaviour, mainly because the log says "evaluated to 'Sales.'". Split widgets conditions are not case-sensitive and should trim leading and following white-space. For some reason this appears to have a capital "S" and a full-stop.
I would suggest a couple of things. Firstly, raise a ticket with Twilio support to look into why the condition didn't match correctly.
Then, try some of the other conditions. When I generate a new IVR template in Studio, the conditions use "Matches any of" instead of "Equal to". You might also try "Contains".
So, experiment with the ways you can match the operators, but get support involved to drill down into why it didn't work in the first place.
The period is needed after the word for some reason...I just put both, for example:
"1, 1., Uno, Uno., Una, Una., Uno, Uno., Español, Español., Español, Español., Español, Español."

Neo4j Cypher : return nodes with id as a dictionary

I have nodes with these properties:
MATCH (n:A) RETURN n
[
{
"name": "114s09A.1",
"_id": "114s09A.1",
"id": "114s09A.1",
"created_n4j": "2020-12-21T09:56:11.256000000Z",
"type": "A",
"updated_n4j": "2020-12-21T09:56:11.256000000Z"
},
{
"name": "114s09A.2",
"_id": "114s09A.2",
"id": "114s09A.2",
"created_n4j": "2020-12-21T09:56:11.257000000Z",
"type": "A",
"updated_n4j": "2020-12-21T09:56:11.257000000Z"
}
]
Is there a way to build the cypher query so that the result would be shaped as a dictionary where id would be the key ?
[
{
"114s09A.1": {
"name": "114s09A.1",
"id": "114s09A.1",
"created_n4j": "2020-12-21T09:56:11.256000000Z",
"type": "A",
"updated_n4j": "2020-12-21T09:56:11.256000000Z"
}
},
{
"114s09A.2": {
"name": "114s09A.2",
"id": "114s09A.2",
"created_n4j": "2020-12-21T09:56:11.257000000Z",
"type": "A",
"updated_n4j": "2020-12-21T09:56:11.257000000Z"
}
}
]
The closest I came up to so far is :
MATCH (n:A) RETURN n._id AS _id, properties(n) AS properties
[
{
"_id":"114s09A.1",
"properties":{
"name": "114s09A.1",
"id": "114s09A.1",
"created_n4j": "2020-12-21T09:56:11.256000000Z",
"type": "A",
"updated_n4j": "2020-12-21T09:56:11.256000000Z"
}
},
{
"_id":"114s09A.2",
"properties":{
"name": "114s09A.2",
"id": "114s09A.2",
"created_n4j": "2020-12-21T09:56:11.257000000Z",
"type": "A",
"updated_n4j": "2020-12-21T09:56:11.257000000Z"
}
}
]
It's not possible with the default Cypher syntax, however if you have the apoc library installed, you can do this :
MATCH (n:A)
RETURN apoc.map.setKey({}, n.id, n{.*})

Creating an avro schema for an array with multiple record types?

I am creating an avro schema for a JSON payload that appear to have an array of multiple objects. I'm not sure exactly how to represent this in the schema. The key in question is content:
{
"id": "channel-id",
"name": "My Channel with a New Title",
"description": "Herpy me derpy merpus herpsum ner berp berps derp ter tee",
"privacyLevel": "<private|org>",
"planId": "some-plan-id",
"owner": "a-user-handle",
"curators": [
"user-handle-1",
"user-handle-2"
],
"members": 5,
"content": [
{
"id": "docker",
"slug": "docker",
"index": 1,
"type": "path"
},
{
"id": "such-linkage",
"slug": "such-linkage",
"index": 2,
"type": "external-link",
"details": {
"url": "http://some-dank-link.com",
"title": "My Dank Link",
"contentType": "External Link",
"level": "Beginner",
"duration": "PT34293H33M9S"
}
},
{
"id": "21f1e812-b10a-40df-8b52-3a1d05fc215c",
"slug": "windows-azure-storage-in-depth",
"index": 3,
"type": "course"
},
{
"id": "7c346c05-6416-42dd-80b2-d5e758de7926",
"slug": "7c346c05-6416-42dd-80b2-d5e758de7926",
"index": 4,
"type": "project"
}
],
"imageUrls": ["https://url/to/an/image", "https://url/to/another/image"],
"analyticsEnabled": true,
"orgDiscoverable": false,
"createdDate": "2015-12-31T01:23:45+00:00",
"archiveDate": "2015-12-31T01:23:45+00:00",
"messagePublishedAt": "2015-12-31T01:23:45+00:00"
}
If you are asking if it is possible create an array with different kind of records, it is. Avro support this through union. it would looks like .
{
"name": "myRecord",
"type":"record",
"fields":[
{
"name":"myArrayWithMultiplesTypes",
"type":{
"type": "array",
"items":[
{
"name":"typeOne",
"type":"record",
"fields":[
{"name":"name", "type":"string"}
]
},
{
"name":"typeTwo",
"type":"record",
"fields":[
{"name":"id", "type":"int"}
]
}
]
}
}
]
}
If you already have the records defined previously, then it could look like this:
{
"name": "mulitplePossibleTypes",
"type": [
"null",
{
"type": "array",
"items": [
"com.xyz.kola.cloud.events.itemmanager.Part",
"com.xyz.kola.cloud.events.itemmanager.Document",
"com.xyz.kola.cloud.events.itemmanager.DigitalModel",
"com.xyz.kola.cloud.events.itemmanager.Interface"
]
}
]
},

How can I restore data from a previous result in the browser?

I have a query that I ran in the neo4j web browser:
MATCH p=(z)<-[*]-(a)-[:foo]->(b) WHERE b.value = "bar" return p
This returned a large number of connected nodes. Something happened that seems to have deleted all of these nodes (in a separate incident), but I still have the output of the old query. The code section of the browser has the response data listed:
...
"graph": {
"nodes": [
{
"id": "1148578",
"labels": [
"text"
],
"properties": {
"value": "bar",
"timestamp": 1478747946867
}
},
...
Is there a way for me to recreate all of this data from the output of an old query?
You could use apoc.load.json to do this. Note that this solution will not preserve the internal node ids. APOC is a procedure library that extends built-in Neo4j functionality.
Given the JSON file
{"graph": {
"nodes": [
{
"id": "32496",
"labels": [
"Person"
],
"properties": {
"born": 1967,
"name": "Carrie-Anne Moss"
}
},
{
"id": "32505",
"labels": [
"Movie"
],
"properties": {
"tagline": "Evil has its winning ways",
"title": "The Devil's Advocate",
"released": 1997
}
},
{
"id": "32494",
"labels": [
"Movie"
],
"properties": {
"tagline": "Welcome to the Real World",
"title": "The Matrix",
"released": 1999
}
},
{
"id": "32495",
"labels": [
"Person"
],
"properties": {
"born": 1964,
"name": "Keanu Reeves"
}
}
],
"relationships": [
{
"id": "83204",
"type": "ACTED_IN",
"startNode": "32495",
"endNode": "32505",
"properties": {
"role": "Kevin Lomax"
}
},
{
"id": "83183",
"type": "ACTED_IN",
"startNode": "32496",
"endNode": "32494",
"properties": {
"role": "Trinity"
}
},
{
"id": "83182",
"type": "ACTED_IN",
"startNode": "32495",
"endNode": "32494",
"properties": {
"role": "Neo"
}
}
]
}
}
}
We can recreate the graph using this query:
CALL apoc.load.json("https://dl.dropboxusercontent.com/u/67572426/small_movie_graph.json") YIELD value AS row
WITH row, row.graph.nodes AS nodes
UNWIND nodes AS node
CALL apoc.create.node(node.labels, node.properties) YIELD node AS n
SET n.id = node.id
WITH row
UNWIND row.graph.relationships AS rel
MATCH (a) WHERE a.id = rel.startNode
MATCH (b) WHERE b.id = rel.endNode
CALL apoc.create.relationship(a, rel.type, rel.properties, b) YIELD rel AS r
RETURN *

How do I create (or update) a QBO BillPayment so that it has an unapplied payment amount?

I created a bill through the QuickBooks Online (QBO) web UI. Then I queried using the API (v3, documented here). The response:
{
"SyncToken": "16",
"domain": "QBO",
"VendorRef": {
"name": "MyVendor",
"value": "237"
},
"TxnDate": "2014-12-07",
"TotalAmt": 1.83,
"CurrencyRef": {
"name": "United States Dollar",
"value": "USD"
},
"PayType": "Check",
"PrivateNote": "test billpayment description (mod)",
"sparse": false,
"Line": [
{
"Amount": 1.22,
"LinkedTxn": [
{
"TxnId": "1412",
"TxnType": "Bill"
}
]
}
],
"Id": "1413",
"CheckPayment": {
"PrintStatus": "NeedToPrint",
"BankAccountRef": {
"name": "MyBankAcct#1234",
"value": "137"
}
},
"MetaData": {
"CreateTime": "2014-12-07T18:44:51-08:00",
"LastUpdatedTime": "2014-12-10T20:20:28-08:00"
}
}
As you can see, it has $0.61 of the TotalAmt unapplied to any Bills. (The other $1.22 is applied to Bill 1412.) Now, when I try to update this bill to change JUST the amount applied to the linked transaction, I get unexpected results.
Here's the update request body:
{
"SyncToken": "16",
"domain": "QBO",
"VendorRef": {
"name": "MyVendor",
"value": "237"
},
"TxnDate": "2014-12-07",
"TotalAmt": 1.83,
"CurrencyRef": {
"name": "United States Dollar",
"value": "USD"
},
"PayType": "Check",
"PrivateNote": "test billpayment description (mod)",
"sparse": false,
"Line": [
{
"Amount": 1.21,
"LinkedTxn": [
{
"TxnId": "1412",
"TxnType": "Bill"
}
]
}
],
"Id": "1413",
"CheckPayment": {
"PrintStatus": "NeedToPrint",
"BankAccountRef": {
"name": "MyBankAcct#1234",
"value": "137"
}
},
"MetaData": {
"CreateTime": "2014-12-07T18:44:51-08:00",
"LastUpdatedTime": "2014-12-10T20:20:28-08:00"
}
}
In the response, it appears that the API just basically can't handle the situation (even though it clearly existed when I created it in the web UI). Look what happens to the TotalAmt attribute! It's reduced to whatever the payment was in the LinkedTxn:
{
"BillPayment": {
"VendorRef": {
"value": "237",
"name": "MyVendor"
},
"PayType": "Check",
"CheckPayment": {
"BankAccountRef": {
"value": "137",
"name": "MyBankAcct#1234"
},
"PrintStatus": "NeedToPrint"
},
"TotalAmt": 1.21,
"domain": "QBO",
"sparse": false,
"Id": "1413",
"SyncToken": "17",
"MetaData": {
"CreateTime": "2014-12-07T18:44:51-08:00",
"LastUpdatedTime": "2014-12-10T20:25:02-08:00"
},
"TxnDate": "2014-12-07",
"CurrencyRef": {
"value": "USD",
"name": "United States Dollar"
},
"PrivateNote": "test billpayment description (mod)",
"Line": [
{
"Amount": 1.21,
"LinkedTxn": [
{
"TxnId": "1412",
"TxnType": "Bill"
}
]
}
]
},
"time": "2014-12-10T20:25:01.91-08:00"
}
I don't see discussion of this on the known issues page, so I'm pretty confused.
If you have any suggestions as to how to get this to do what I want it to, I thank you for your thoughts!

Resources