This repository has been archived by the owner on May 3, 2021. It is now read-only.
/
Relationship.java
181 lines (149 loc) · 4.73 KB
/
Relationship.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/*
* Copyright (c) 2019 "Neo4j,"
* Neo4j Sweden AB [https://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed 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
*
* https://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.
*/
package org.neo4j.springframework.data.core.cypher;
import static java.util.stream.Collectors.*;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import org.apiguardian.api.API;
import org.neo4j.springframework.data.core.cypher.support.Visitor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* See <a href="https://s3.amazonaws.com/artifacts.opencypher.org/railroad/RelationshipPattern.html">RelationshipPattern</a>.
*
* @author Michael J. Simons
* @since 1.0
*/
@API(status = API.Status.INTERNAL, since = "1.0")
public final class Relationship implements
PatternElement, Named, Expression, MapEntry,
ExposesRelationships<RelationshipChain> {
/**
* While the direction in the schema package is centered around the node, the direction here is the direction between two nodes.
* @since 1.0
*/
public enum Direction {
/**
* Left to right
*/
LTR("-", "->"),
/**
* Right to left
*/
RTR("<-", "-"),
/**
* None
*/
UNI("-", "-");
Direction(String symbolLeft, String symbolRight) {
this.symbolLeft = symbolLeft;
this.symbolRight = symbolRight;
}
private final String symbolLeft;
private final String symbolRight;
public String getSymbolLeft() {
return symbolLeft;
}
public String getSymbolRight() {
return symbolRight;
}
}
static Relationship create(Node left,
@Nullable Direction direction, Node right, String... types) {
Assert.notNull(left, "Left node is required.");
Assert.notNull(right, "Right node is required.");
List<String> listOfTypes = Arrays.stream(types)
.filter(type -> !(type == null || type.isEmpty()))
.collect(toList());
RelationshipDetail details = new RelationshipDetail(
Optional.ofNullable(direction).orElse(Direction.UNI),
null, listOfTypes);
return new Relationship(left, details, right);
}
private final Node left;
private final Node right;
private final RelationshipDetail details;
Relationship(Node left, RelationshipDetail details, Node right) {
this.left = left;
this.right = right;
this.details = details;
}
public Node getLeft() {
return left;
}
public Node getRight() {
return right;
}
public RelationshipDetail getDetails() {
return details;
}
/**
* Creates a copy of this relationship with a new symbolic name.
*
* @param newSymbolicName the new symbolic name.
* @return The new relationship.
*/
public Relationship named(String newSymbolicName) {
// Sanity check of newSymbolicName delegated to the details.
return new Relationship(this.left, this.details.named(newSymbolicName), this.right);
}
@Override
public Optional<SymbolicName> getSymbolicName() {
return details.getSymbolicName();
}
@Override
public RelationshipChain relationshipTo(Node other, String... types) {
return RelationshipChain
.create(this)
.add(this.right.relationshipTo(other, types));
}
@Override
public RelationshipChain relationshipFrom(Node other, String... types) {
return RelationshipChain
.create(this)
.add(this.right.relationshipFrom(other, types));
}
@Override
public RelationshipChain relationshipBetween(Node other, String... types) {
return RelationshipChain
.create(this)
.add(this.right.relationshipBetween(other, types));
}
@Override
public void accept(Visitor visitor) {
visitor.enter(this);
left.accept(visitor);
details.accept(visitor);
right.accept(visitor);
visitor.leave(this);
}
/**
* Creates a map projection based on this relationship. The relationship needs a symbolic name for this to work.
* <p>
* Entries of type {@code String} in {@code entries} followed by an {@link Expression} will be treated as map keys
* pointing to the expression in the projection, {@code String} entries alone will be treated as property lookups on the node.
*
* @param entries A list of entries for the projection
* @return A map projection.
*/
public MapProjection project(Object... entries) {
return MapProjection.create(this.getSymbolicName().orElseThrow(() -> new IllegalStateException("Cannot project a relationship without a symbolic name.")), entries);
}
}