Top 10 query templates to accelerate your investigations
Do your investigations involve recurring steps? Are you looking to make the power of graph queries available to non technical users? Linkurious Enterprise’s Query Templates are exactly what you need.
Through the Query Template interface, graph queries are turned into easy to use buttons or forms that leverage context (a node, a time period, a type of relationship, etc) provided by analysts.
For instance, the query template below will return all the paths between two given nodes and allows the end user to select which nodes will be used when running the query.
MATCH p = (a)-[*..10]->(b)
WHERE id(a) = {{"Source":node}} AND id(b) = {{"Target":node}}
RETURN p
This blog post showcases 10 examples of what can be done with Query Templates. It covers scenarios such as turning a specific set of graph traversals into a single button, calling an external service to enrich your graph, and how to turn complex code into an easy to use form.
Each example includes an explanation of how the Query Template can be useful, a video showing what it will look like and the code behind it to reproduce the Query Template.
These examples are meant to inspire. As a technical expert, you can directly adapt them or create new ones using the documentation. As a Linkurious Enterprise user, you can work with your tech team to turn your ideas into new features!
Identify the UBOs of a company
Why: Identifying the persons and legal entities controlling a given company can be difficult as it combines; (1) the identification of the company’s relationships and (2) filtering them based on legal considerations such as the % of ownership. Here, the entire process can be done via a single click.
How it works: For any given company, the query template identifies all the companies it is connected to by an “IS_SHAREHOLDER” relationship where the % of ownership is beyond 20%.
MATCH (a)
WHERE ID(a) = {{"Target Company":node:"COMPANY"}}
WITH a
MATCH p = (a)<-[:IS_SHAREHOLDER*..10]-(b)
WHERE ALL (
r IN relationships(p)
WHERE r.property1 > 0.20
)
RETURN p
Run an impact analysis
Why: A given server in your infrastructure goes down. What are the ripple effects? Answering that question requires exploring multiple layers of dependency relationships and can be tedious.
How it works: For any given node, the query template identifies everything it is connected to via outgoing DEPENDS_ON relationships.
MATCH (a)
WHERE ID(a) = {{"My Impacted Database":node:"Database"}}
WITH a
MATCH p = (a)-[*..5]->(b)
RETURN p
Find crimes that occurred within 500 meters from a location
Why: Your data contains some locations? You might want to conduct geospatial queries such as identifying the entities that are present within a certain radius of any given place.
How it works: As an analyst, simply run the query template on a location. Your visualization will immediately be populated by nodes representing crimes that occurred within a 500 meter radius.
//Crime within 500 meters query template
MATCH (l)
WHERE ID(l) = {{"Crime of interest":node:"Location"}}
WITH point(l) as corrie
MATCH (x:Location)-[HAS_POSTCODE]->(p:PostCode), (x)<-[OCCURED_AT]-(c:Crime)
WITH x, p, c, distance(point(x), corrie) as distance
WHERE distance < 500
RETURN x,p,c
Identify a specific type of shortest path between two nodes
Why: Maybe you want to know how two clients are connected. In a real-world graph they can be connected in a number of ways. Some connections may be meaningless, such as the fact that two clients live in the same country. It’s important to identify not just the shortest path between two nodes but the shortest path that takes into account some constraints.
How it works: As a user, start the query template to get the shortest path between two nodes. You can then specify which type of relationship the shortest path should be based on with a dropdown menu and the length of the maximum shortest path.
MATCH p = (a)-[*..{{"Maximum path length":number:{"default": 4, "min": 1, "max": 10}}}]->(b)
WHERE id(a) = {{"Source":node}} AND id(b) = {{"Target":node}} AND ALL (r IN relationships(p)
WHERE type(r)={{"Type of relationship":enum:{"values": ["IS_SHAREHOLDER", "OFFICER_OF"], "default": "IS_SHAREHOLDER"}}})
RETURN p
ORDER BY length(p) asc LIMIT 1
Tag a node as suspicious and add comments
Why: When you uncover something suspicious in a graph, the next step could be to signal it. But how is it possible for end-users to provide this sort of information in a structured way without the graph data becoming too messy?
How it works: Once you click on the query template, the selected node’s “suspicious” property is automatically marked as “true”. In addition, a pop up form appears where you can add text that will be added as a comment to the node. All of this guarantees that users will be able to contribute information in a more structured manner.
MATCH (n)
WHERE id(n) = {{"Suspicious Entity":node:["PERSON"]}}
SET n.suspicious = true
SET n.note = {{"Comment":string:{"placeholder":"Add a note..."}}}
RETURN n;
Hunt for complex patterns via a simple form
Why: Every time 10 clients are interconnected via personal information and are involved in more than $500K worth of transactions, it’s highly suspicious. But what if the amount of money or the number of people involved is smaller? Analysts often need to tweak the parameters of known suspicious patterns to explore what happens when certain conditions change.
How it works: Clicking on the query template starts a form where, as an analyst, you can specify some parameters.
MATCH p = (bank:BANK_ACCOUNT)<-[:HAS_BANK_ACCOUNT]-(a:PERSON)-[n:HAS_ADDRESS|:HAS_IP_ADDRESS|:HAS_PHONE_NUMBER*..10]-(b:PERSON)
WITH p, length(p) as score, bank.TotalAmount as amount
WHERE score > {{"Minimum number of suspicious entities":number}} AND amount > {{"Minimum amount":number}}
RETURN p
Find common neighbors
Why: You have a hunch that a group of suspicious individuals are somehow connected to a common accomplice but you don’t know who and you don’t know how.
How it works: Click on a set of nodes and run the query template to find their common neighbor(s).
//Find common neighbors
MATCH (n) WHERE id(n) in {{"nodes":nodeset}}
MATCH (n)-[e]-(m)
WITH m, collect(e) as edges, count(distinct n) as sharedNeighborCount
WHERE sharedNeighborCount = length({{"nodes":nodeset}})
RETURN m, edges;
Discovering communities with graph analytics
Why: As a data scientist you have enriched your graph by using graph analytics to detect hidden communities. How can this be used to help analysts explore the graph?
How it works: Click on a node and run the query template to identify the other nodes that belong to the same community.
//Same community
MATCH (a)
WHERE ID(a) = {{"Individual of interest":node}}
WITH a
MATCH p = (a)-[*..5]-(b)
WHERE a.community = b.community
RETURN p
LIMIT 50
Looking up the geo coordinates of an address with the Google Maps API
Why: What if you could call external services through the Linkurious Enterprise API? With Neo4j’s APOC, you’re able to do just that. It opens the door to interacting with other apps directly within the Linkurious Enterprise UI.
How it works: The Query Template uses the content of a node’s address property to retrieve information about the latitude and longitude coordinates of the node via the Google Maps API. Once these coordinates are available, the node can be displayed on a map.
MATCH (a)
WHERE ID(a) = {{"Object to localise":node}}
WITH a
CALL apoc.spatial.geocodeOnce(a.address) YIELD location
WITH a, location.latitude AS latitude, location.longitude AS longitude, location.description AS description
SET a.latitude = latitude,
a.longitude = longitude
RETURN a
Aggregating relationships
Why: In real life, graphs can get messy. When there’s a high number of relationships between nodes, it results in dense graphs that can appear as hairballs. But this doesn’t have to be the case. For example, displaying every financial transaction between two clients may result in a lot of unnecessary information when you only want to have a high level view of how much money they have exchanged overall.
How it works: Click on two nodes of your choice. The list of the payments they are both connected to is turned into a single relationship which aggregates the amount of money exchanged. The data then becomes easier to make sense of.
MATCH (a)-[:PAID]->(p:PAYMENT)<-[:RECEIVED]-(b)
WHERE id(a) = {{"Source":node}} AND id(b) = {{"Target":node}}
RETURN a, b, apoc.create.vRelationship(a,'AGGREGATE',{amount:sum(p.amount)},b) as rel;
Conclusion
Typically, building a query template requires matching the needs of the investigators with the knowledge of graph experts. Common repetitive tasks, complex workflows, and advanced searches are usually a good starting point for potential Query Templates. We encourage you to brainstorm ideas within your organization.
If you need any help, don’t hesitate to reach out!
A spotlight on graph technology directly in your inbox.