blob: 9ded819ef4e4daa034a9a70a700f2a47dd500dc5 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
LOAD 'age';
SET search_path TO ag_catalog;
SELECT create_graph('pattern_expr');
--
-- Setup test data
--
SELECT * FROM cypher('pattern_expr', $$
CREATE (alice:Person {name: 'Alice'})-[:KNOWS]->(bob:Person {name: 'Bob'}),
(alice)-[:WORKS_WITH]->(charlie:Person {name: 'Charlie'}),
(dave:Person {name: 'Dave'})
$$) AS (result agtype);
--
-- Basic pattern expression in WHERE
--
-- Bare pattern: (a)-[:REL]->(b)
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person), (b:Person)
WHERE (a)-[:KNOWS]->(b)
RETURN a.name, b.name
ORDER BY a.name, b.name
$$) AS (a agtype, b agtype);
--
-- NOT pattern expression
--
-- Find people who don't KNOW anyone
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person)
WHERE NOT (a)-[:KNOWS]->(:Person)
RETURN a.name
ORDER BY a.name
$$) AS (result agtype);
--
-- Pattern with labeled first node
--
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person), (b:Person)
WHERE (a:Person)-[:KNOWS]->(b)
RETURN a.name, b.name
ORDER BY a.name
$$) AS (a agtype, b agtype);
--
-- Pattern combined with AND
--
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person), (b:Person)
WHERE (a)-[:KNOWS]->(b) AND a.name = 'Alice'
RETURN a.name, b.name
$$) AS (a agtype, b agtype);
--
-- Pattern combined with OR
--
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person), (b:Person)
WHERE (a)-[:KNOWS]->(b) OR (a)-[:WORKS_WITH]->(b)
RETURN a.name, b.name
ORDER BY a.name, b.name
$$) AS (a agtype, b agtype);
--
-- Left-directed pattern
--
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person), (b:Person)
WHERE (a)<-[:KNOWS]-(b)
RETURN a.name, b.name
ORDER BY a.name
$$) AS (a agtype, b agtype);
--
-- Pattern with anonymous nodes
--
-- Find anyone who has any outgoing KNOWS relationship
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person)
WHERE (a)-[:KNOWS]->()
RETURN a.name
ORDER BY a.name
$$) AS (result agtype);
--
-- Multiple relationship pattern
--
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person), (c:Person)
WHERE (a)-[:KNOWS]->()-[:WORKS_WITH]->(c)
RETURN a.name, c.name
ORDER BY a.name
$$) AS (a agtype, c agtype);
--
-- Existing EXISTS() syntax still works (backward compatibility)
--
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person), (b:Person)
WHERE EXISTS((a)-[:KNOWS]->(b))
RETURN a.name, b.name
ORDER BY a.name
$$) AS (a agtype, b agtype);
--
-- Pattern expression produces same results as EXISTS()
--
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person)
WHERE (a)-[:KNOWS]->(:Person)
RETURN a.name
ORDER BY a.name
$$) AS (result agtype);
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person)
WHERE EXISTS((a)-[:KNOWS]->(:Person))
RETURN a.name
ORDER BY a.name
$$) AS (result agtype);
--
-- Regular (non-pattern) expressions still work (no regression)
--
SELECT * FROM cypher('pattern_expr', $$
RETURN (1 + 2)
$$) AS (result agtype);
SELECT * FROM cypher('pattern_expr', $$
MATCH (n:Person)
WHERE n.name = 'Alice'
RETURN (n.name)
$$) AS (result agtype);
--
-- Pattern expressions in RETURN (boolean projection)
--
-- Each person gets a column showing whether they know someone
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person)
RETURN a.name, (a)-[:KNOWS]->(:Person) AS knows_someone
ORDER BY a.name
$$) AS (name agtype, knows_someone agtype);
-- Mix pattern expression with other projections
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person)
RETURN a.name, (a)-[:KNOWS]->(:Person), (a)-[:WORKS_WITH]->(:Person)
ORDER BY a.name
$$) AS (name agtype, knows agtype, works_with agtype);
--
-- Pattern expressions in CASE WHEN
--
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person)
RETURN a.name,
CASE WHEN (a)-[:KNOWS]->(:Person) THEN 'social'
ELSE 'loner'
END
ORDER BY a.name
$$) AS (name agtype, kind agtype);
--
-- Pattern expressions combined with boolean operators in RETURN
--
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person)
RETURN a.name,
(a)-[:KNOWS]->(:Person) AND (a)-[:WORKS_WITH]->(:Person) AS has_both,
(a)-[:KNOWS]->(:Person) OR (a)-[:WORKS_WITH]->(:Person) AS has_either
ORDER BY a.name
$$) AS (name agtype, has_both agtype, has_either agtype);
--
-- Pattern expression in SET (store boolean as property)
--
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person)
SET a.is_social = (a)-[:KNOWS]->(:Person)
RETURN a.name, a.is_social
ORDER BY a.name
$$) AS (name agtype, is_social agtype);
--
-- Pattern expression in WITH (carry boolean through pipeline)
--
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person)
WITH a.name AS name, (a)-[:KNOWS]->(:Person) AS knows
WHERE knows
RETURN name
ORDER BY name
$$) AS (result agtype);
--
-- Follow-up coverage (review #2360): pattern expressions in additional
-- expression contexts opened up by allowing anonymous_path as an expr_atom.
--
--
-- Single-node pattern on an already-bound variable: (a:Label)
--
-- NOTE: as of #2443 a single-node labeled pattern is a correlated label
-- predicate -- in WHERE / EXISTS it tests whether the bound vertex actually
-- has the label (see the WHERE (a:Person) / EXISTS((a:Company)) cases in the
-- #2443 section below). Here the variable is already bound to the SAME label,
-- so the predicate is trivially true (the label matches). A *different* label
-- on an already-bound variable is still rejected by AGE's pre-existing
-- "multiple labels for variable" restriction rather than evaluating to false;
-- that is an orthogonal limitation, captured here so any future change to
-- single-node-pattern semantics is caught by this test.
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person)
RETURN a.name, (a:Person)
ORDER BY a.name
$$) AS (name agtype, is_person agtype);
-- A non-matching label errors (pre-existing limitation, not a regression)
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person)
RETURN a.name, (a:Animal)
ORDER BY a.name
$$) AS (name agtype, is_animal agtype);
--
-- Pattern expressions inside a list literal
--
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person)
RETURN a.name, [(a)-[:KNOWS]->(:Person), (a)-[:WORKS_WITH]->(:Person)]
ORDER BY a.name
$$) AS (name agtype, flags agtype);
--
-- Pattern expressions inside a map literal
--
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person)
RETURN a.name, {knows: (a)-[:KNOWS]->(:Person), works: (a)-[:WORKS_WITH]->(:Person)}
ORDER BY a.name
$$) AS (name agtype, m agtype);
--
-- Pattern expressions as function arguments
--
-- collect() shows the per-row boolean values are correct (ORDER BY before
-- the aggregate so the collected order is deterministic across scan plans).
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person)
WITH a ORDER BY a.name
RETURN collect((a)-[:KNOWS]->(:Person))
$$) AS (vals agtype);
-- count() counts non-null values; a boolean (including false) is non-null,
-- so this counts every row rather than only the matching ones. This is the
-- expected SQL aggregate semantics, documented here so the value is not
-- mistaken for a bug.
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person)
RETURN count((a)-[:KNOWS]->(:Person))
$$) AS (c agtype);
--
-- Pattern expression in OPTIONAL MATCH ... WHERE (null-preserving)
--
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person)
OPTIONAL MATCH (b:Person) WHERE (a)-[:KNOWS]->(b)
RETURN a.name, b.name
ORDER BY a.name, b.name
$$) AS (a agtype, b agtype);
--
-- EXISTS() and a bare pattern combined in a single predicate
--
SELECT * FROM cypher('pattern_expr', $$
MATCH (a:Person)
WHERE EXISTS((a)-[:KNOWS]->(:Person)) AND (a)-[:WORKS_WITH]->(:Person)
RETURN a.name
ORDER BY a.name
$$) AS (name agtype);
--
-- Single-node labeled pattern as a boolean (#2443)
--
-- A bound vertex carrying a label, e.g. (a:Person), must test that vertex's
-- label rather than be trivially true. Add a non-Person vertex so the filter
-- is observable (every other vertex in this graph is a :Person).
SELECT * FROM cypher('pattern_expr', $$
CREATE (:Company {name: 'Acme'})
$$) AS (result agtype);
-- bare single-node label predicate in WHERE: only the :Person vertices
SELECT * FROM cypher('pattern_expr', $$
MATCH (a)
WHERE (a:Person)
RETURN a.name
ORDER BY a.name
$$) AS (name agtype);
-- negated: only the non-Person vertex
SELECT * FROM cypher('pattern_expr', $$
MATCH (a)
WHERE NOT (a:Person)
RETURN a.name
ORDER BY a.name
$$) AS (name agtype);
-- EXISTS() form of a single-node label predicate
SELECT * FROM cypher('pattern_expr', $$
MATCH (a)
WHERE EXISTS((a:Company))
RETURN a.name
ORDER BY a.name
$$) AS (name agtype);
--
-- Cleanup
--
SELECT * FROM drop_graph('pattern_expr', true);