I have a set of cypher clauses that I want to apply repeatedly until the graph stops changing. For that, I require that the clauses check, whether they have already run. For example, if I have a clause adding an edge (a:X) --> (b:Y), then that clause should only run, iff (a:X) --> (b:Y) does not yet exist. I tried to use MERGE like so:
Clause 1 (add root node): CREATE (root:root)
Clause 2 (add child node): MERGE (root:root)-[r:some_rel]->(b:child)
However, when running both clauses, it will produce three nodes. Is there a way, to make the (root:root) from Clause 2 be matched to the one from Clause 1?
Try running them as a single query, and use the node created from the CREATE statement, like this:
CREATE (root:root)
MERGE (root)-[r:some_rel]->(b:child)
Update, to keep them independent, try this:
Query 1 - CREATE (root:root)
Query 2 -
MATCH (root:root)
MERGE (root)-[r:some_rel]->(b:child)
Related
I’m currently working with the Nebula graph database for the first time and I’m running into some issues with a query. In terms of the schema, I have “Person” nodes, which have a “name” property, as well as Location nodes also with a name property. These node types can be connected by a relationship edge, called HAS_LIVED (to signify whether a person has lived in a certain location). Now for the query, I have a list of names (strings). The query looks like:
UNWIND [“Anna”, “Emma”, “Zach”] AS n
MATCH (p:Person {name: n})-[:HAS_LIVED]->(loc)
RETURN loc.Location.name
This should return a list of three places, i.e. [“London”, “Paris”, “Berlin”]. However, I am getting nothing as a result from the query. When I get rid of the UNWIND and write three separate MATCH queries with each name, it works individually. Not sure why.
Try this instead. It is using "where" clause.
UNWIND [“Anna”, “Emma”, “Zach”] AS n
MATCH (p:Person)-[:HAS_LIVED]->(loc)
where p.name = n
RETURN loc.Location.name
I'm attempting to build a recommendation engine for a library system.
This is my db schema:
My starting point is a LoanerCard. The flow is then supposed to look like this: Get all copies -> get the material -> get all copies of the material (including the original) -> get LoanerCard from copy -> get all loaned copies -> return the material name of the copies + an aggregated count to indicate the strength of the recommendation.
My best attempt so far has resulted in this query:
MATCH (L:LoanerCard {Barcode:"10007"})-[:LOANED]->(myLoans)-[:COPY_OF]-
(masterMaterial),
(masterMaterial)<-[:COPY_OF]-(allCopies),
(allCopies)<-[:LOANED]-(coLoaners),
(coLoaners)-[r:LOANED]->(theirCopies),
(theirCopies)-[:COPY_OF]-(materials)
RETURN materials.Title as Recommended, count(*) as Strength ORDER BY Strength DESC
My issue here is that when I traverse the graph it doesn't include the original copy and the adjacent LoanerCards of that so essentially it only traverses the area circled in red and never reaches LoanerCard 10817 and 10558
How can I design my query so it includes these?
A MATCH clause automatically filters out duplicate relationships. Therefore, in order to traverse the same relationships twice, you need to split your MATCH clause in two.
Try this:
MATCH (:LoanerCard {Barcode:"10007"})-[:LOANED]->()-[:COPY_OF]-(masterMaterial)
MATCH (masterMaterial)<-[:COPY_OF]-()<-[:LOANED]-()-[:LOANED]->()-[:COPY_OF]-(materials)
RETURN materials.Title as Recommended, count(*) as Strength ORDER BY Strength DESC
The question is about SQLite.
Suppose I have a view which is a result of a join of multiple tables, the tables are linked with foreign key constraints and all indexes are in place, and a select statement against such a view is executed and it uses a where condition which selects only a subset of the available rows. For each statement, the view must be 'materialized' (I don't know what the real word is), but is it materialized fully for all rows and the filtering from the select takes place then, or is the view materialized in a 'smart' way in which it already pre-filters the data somehow? Does it even matter? How does this work?
When you are querying a view like this:
SELECT ...
FROM MyView
WHERE ...
then the view gets merged as a subquery, like this:
SELECT ...
FROM (SELECT ...
FROM ...
WHERE ...)
WHERE ...
Futher processing is the same, whether the subquery originated from a view or was written explicitly in the query.
If possible, SQLite tries to flatten the subquery so that the end result is a single, simple query with all the joins and WHERE conditions merged together.
If that is not possible, SQLite tries to implement the subquery as a coroutine, i.e., it executes the inner query until it gets one row, and then applies any processing of the outer query to that row; then outputs the result, if any; then repeats.
Only if the inner query must compute all rows before returning the first one (for example, when using an ORDER BY that cannot be flattened), then using a coroutine is not possible, and the inner query is actually materialized into a temporary table.
If I do a simple select, I can order the result using ORDER BY. But I cannot do this with a WITH RECURSIVE CTE, because I am using it to find a path from a leaf in a tree back up to the root, and the order that the CTE creates the result is not an order that can be obtained by sorting, therefore there is no ORDER BY I can reverse to get the reverse order.
The problem I have is, this constructs the results from leaf to root, but for a subsequent part of the query I need it to be in the reverse order, from the root to the leaf. But I cannot construct the query this way because it would wind up following all branches in the tree instead of the single path that I need. Thus, I need to somehow reverse the order of the resulting CTE. How can I do this?
I have done a bit of looking and there are some similar questions for other (non SQLite) database which seem to suggest that the result of the CTE table doesn't actually have any defined order. I am not sure if that is true for SQLite - I always see it output the table in the same child to parent order, and in fact there are other cases (such as in creating temporary tables, as in a previous question I asked) where if the table were not guaranteed to have this property it would break the only possible solution rendering it an impossible problem to solve.
The documentation says:
An ORDER BY clause on the recursive-select can be used to control whether the search of a tree is depth-first or breadth-first.
However, you want to sort the ultimate output of the CTE.
This can be done easily because you are using a normal SELECT to access the CTE:
WITH RECURSIVE test1(id, parent) AS (
VALUES(3, 2)
UNION ALL
SELECT test.id, test.parent
FROM test JOIN test1 ON test1.parent = test.id)
SELECT *
FROM test1
ORDER BY id -- this sorts normally
You can use multiple elements in your order by. For example the following will order the tree by name after performing the depth first search. Here I am sorting first by LEVEL descending then NAME ascending. The output would be a sorted tree with children underneath the appropriate parent.
WITH RECURSIVE TEMPTREE (
id,
name,
level
)
AS (
SELECT flow_id,
name,
0
FROM DATATABLE
WHERE parent_id IS NULL
UNION ALL
SELECT DATATABLE.id,
DATATABLE.name,
TEMPTREE.level + 1
FROM DATATABLE
JOIN
TEMPTREE ON DATATABLE.parent_id = TEMPTREE .id
ORDER BY 3 DESC, 2 ASC
)
SELECT substr('..........', 1, level * 3) || name AS Name,
id
FROM TEMPTREE;
I have recently stumbled upon a problem with selecting relationship details from a 1 table and inserting into another table, i hope someone can help.
I have a table structure as follows:
ID (PK) Name ParentID<br>
1 Myname 0<br>
2 nametwo 1<br>
3 namethree 2
e.g
This is the table i need to select from and get all the relationship data. As there could be unlimited number of sub links (is there a function i can create for this to create the loop ?)
Then once i have all the data i need to insert into another table and the ID's will now have to change as the id's must go in order (e.g. i cannot have id "2" be a sub of 3 for example), i am hoping i can use the same function for selecting to do the inserting.
If you are using SQL Server 2005 or above, you may use recursive queries to get your information. Here is an example:
With tree (id, Name, ParentID, [level])
As (
Select id, Name, ParentID, 1
From [myTable]
Where ParentID = 0
Union All
Select child.id
,child.Name
,child.ParentID
,parent.[level] + 1 As [level]
From [myTable] As [child]
Inner Join [tree] As [parent]
On [child].ParentID = [parent].id)
Select * From [tree];
This query will return the row requested by the first portion (Where ParentID = 0) and all sub-rows recursively. Does this help you?
I'm not sure I understand what you want to have happen with your insert. Can you provide more information in terms of the expected result when you are done?
Good luck!
For the retrieval part, you can take a look at Common Table Expression. This feature can provide recursive operation using SQL.
For the insertion part, you can use the CTE above to regenerate the ID, and insert accordingly.
I hope this URL helps Self-Joins in SQL
This is the problem of finding the transitive closure of a graph in sql. SQL does not support this directly, which leaves you with three common strategies:
use a vendor specific SQL extension
store the Materialized Path from the root to the given node in each row
store the Nested Sets, that is the interval covered by the subtree rooted at a given node when nodes are labeled depth first
The first option is straightforward, and if you don't need database portability is probably the best. The second and third options have the advantage of being plain SQL, but require maintaining some de-normalized state. Updating a table that uses materialized paths is simple, but for fast queries your database must support indexes for prefix queries on string values. Nested sets avoid needing any string indexing features, but can require updating a lot of rows as you insert or remove nodes.
If you're fine with always using MSSQL, I'd use the vendor specific option Adrian mentioned.