Skip to content

Commit 636a622

Browse files
committed
v2.0 init
1 parent 0b701bb commit 636a622

File tree

77 files changed

+3836
-181
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+3836
-181
lines changed

pom.xml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
</properties>
1313
<groupId>org.wowtools</groupId>
1414
<artifactId>neo4j-rtree</artifactId>
15-
<version>1.4.2.RELEASE</version>
15+
<version>2.0</version>
1616
<name>neo4j-rtree</name>
1717

1818
<description>a spatial index for neo4j 4.x.</description>
@@ -50,6 +50,7 @@
5050
<groupId>org.neo4j</groupId>
5151
<artifactId>neo4j</artifactId>
5252
<version>${neo4j.version}</version>
53+
<scope>provided</scope>
5354
</dependency>
5455
<dependency>
5556
<groupId>org.locationtech.jts</groupId>
@@ -60,7 +61,8 @@
6061
<dependency>
6162
<groupId>org.wowtools</groupId>
6263
<artifactId>catframe-common</artifactId>
63-
<version>1.5</version>
64+
<version>1.4.2</version>
65+
<scope>test</scope>
6466
</dependency>
6567
<dependency>
6668
<groupId>junit</groupId>
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
package org.wowtools.neo4j.rtree;
2+
3+
import org.neo4j.graphdb.*;
4+
import org.wowtools.neo4j.rtree.internal.RtreeLock;
5+
import org.wowtools.neo4j.rtree.internal.define.Labels;
6+
import org.wowtools.neo4j.rtree.internal.define.Relationships;
7+
import org.wowtools.neo4j.rtree.internal.edit.RTree;
8+
import org.wowtools.neo4j.rtree.pojo.RectNd;
9+
import org.wowtools.neo4j.rtree.util.TxCell;
10+
import org.wowtools.neo4j.rtree.util.VoidDataNodeVisitor;
11+
12+
import java.util.ArrayDeque;
13+
import java.util.Map;
14+
import java.util.concurrent.locks.Lock;
15+
16+
/**
17+
* rtree编辑器,此对象实例化时,会启动一个事务,并在索引上加写锁,所以务必在结束时调用close方法
18+
*
19+
* @author liuyu
20+
* @date 2021/12/24
21+
*/
22+
public class RtreeEditor implements AutoCloseable {
23+
24+
private final RTree rTree;
25+
private final Lock writeLock;
26+
private final TxCell txCell;
27+
28+
private RtreeEditor(RTree rTree, String name, TxCell txCell) {
29+
this.rTree = rTree;
30+
writeLock = RtreeLock.getUseReadWriteLock(name).writeLock();
31+
this.txCell = txCell;
32+
writeLock.lock();
33+
}
34+
35+
/**
36+
* 获取索引
37+
*
38+
* @param graphdb neo4j db
39+
* @param commitLimit 操作达到多少个顶点时执行提交操作
40+
* @param name 索引名
41+
* @return
42+
*/
43+
public static RtreeEditor get(GraphDatabaseService graphdb, int commitLimit, String name) {
44+
TxCell txCell = new TxCell(commitLimit, graphdb);
45+
Node metadataNode;
46+
synchronized (RtreeLock.getCreateIndexLock()) {
47+
metadataNode = txCell.getTx().findNode(Labels.METADATA, "name", name);
48+
if (null == metadataNode) {
49+
txCell.close();
50+
throw new RuntimeException("索引 " + name + " 不存在");
51+
}
52+
}
53+
Map<String, Object> properties = metadataNode.getProperties("mMin", "mMax");
54+
int mMin = (int) properties.get("mMin");
55+
int mMax = (int) properties.get("mMax");
56+
RTree rTree = new RTree(new RectNd.Builder(), metadataNode.getId(), mMin, mMax, txCell);
57+
RtreeEditor rtreeEditor = new RtreeEditor(rTree, name, txCell);
58+
return rtreeEditor;
59+
}
60+
61+
/**
62+
* 新建索引
63+
*
64+
* @param graphdb neo4j db
65+
* @param commitLimit 操作达到多少个顶点时执行提交操作
66+
* @param name 索引名
67+
* @param mMin 索引中每个节点最小子节点数
68+
* @param mMax 索引中每个节点最大子节点数
69+
* @return
70+
*/
71+
public static RtreeEditor create(GraphDatabaseService graphdb, int commitLimit, String name, int mMin, int mMax) {
72+
TxCell txCell = new TxCell(commitLimit, graphdb);
73+
Node metadataNode;
74+
synchronized (RtreeLock.getCreateIndexLock()) {
75+
metadataNode = txCell.getTx().findNode(Labels.METADATA, "name", name);
76+
if (null != metadataNode) {
77+
txCell.close();
78+
throw new RuntimeException("索引 " + name + " 已存在");
79+
}
80+
metadataNode = txCell.getTx().createNode(Labels.METADATA);
81+
}
82+
metadataNode.setProperty("mMin", mMin);
83+
metadataNode.setProperty("mMax", mMax);
84+
metadataNode.setProperty("name", name);
85+
86+
RTree rTree = new RTree(new RectNd.Builder(), metadataNode.getId(), mMin, mMax, txCell);
87+
RtreeEditor rtreeEditor = new RtreeEditor(rTree, name, txCell);
88+
return rtreeEditor;
89+
}
90+
91+
/**
92+
* 若指定名称的索引存在,获取索引,若不存在,则新建一个
93+
*
94+
* @param graphdb neo4j db
95+
* @param commitLimit 操作达到多少个顶点时执行提交操作
96+
* @param name 索引名
97+
* @param mMin 索引中每个节点最小子节点数,如索引已存在则使用现有值,此输入值失效
98+
* @param mMax 索引中每个节点最大子节点数,如索引已存在则使用现有值,此输入值失效
99+
* @return RtreeEditor
100+
*/
101+
public static RtreeEditor getOrCreate(GraphDatabaseService graphdb, int commitLimit, String name, int mMin, int mMax) {
102+
TxCell txCell = new TxCell(commitLimit, graphdb);
103+
Node metadataNode;
104+
boolean exist;
105+
synchronized (RtreeLock.getCreateIndexLock()) {
106+
metadataNode = txCell.getTx().findNode(Labels.METADATA, "name", name);
107+
if (null == metadataNode) {
108+
metadataNode = txCell.getTx().createNode(Labels.METADATA);
109+
exist = false;
110+
} else {
111+
exist = true;
112+
}
113+
}
114+
115+
if (exist) {
116+
RTree rTree = new RTree(new RectNd.Builder(), metadataNode.getId(), mMin, mMax, txCell);
117+
RtreeEditor rtreeEditor = new RtreeEditor(rTree, name, txCell);
118+
return rtreeEditor;
119+
} else {
120+
metadataNode.setProperty("mMin", mMin);
121+
metadataNode.setProperty("mMax", mMax);
122+
metadataNode.setProperty("name", name);
123+
124+
RTree rTree = new RTree(new RectNd.Builder(), metadataNode.getId(), mMin, mMax, txCell);
125+
RtreeEditor rtreeEditor = new RtreeEditor(rTree, name, txCell);
126+
return rtreeEditor;
127+
}
128+
129+
}
130+
131+
/**
132+
* 删除索引
133+
*
134+
* @param graphdb neo4j db
135+
* @param name 索引名
136+
* @param dataNodeVisitor 数据节点访问器,具体实现遇到数据节点该如何处置(例如将数据节点删除)
137+
*/
138+
public static void drop(GraphDatabaseService graphdb, String name, VoidDataNodeVisitor dataNodeVisitor) {
139+
//删掉METADATA
140+
long rootId;
141+
int mMax;
142+
try (Transaction tx = graphdb.beginTx()) {
143+
synchronized (RtreeLock.getCreateIndexLock()) {
144+
Node metadataNode = tx.findNode(Labels.METADATA, "name", name);
145+
if (null == metadataNode) {
146+
throw new RuntimeException("索引 " + name + " 不存在");
147+
}
148+
Relationship r = metadataNode.getRelationships(Relationships.RTREE_METADATA_TO_ROOT).iterator().next();
149+
rootId = r.getEndNodeId();
150+
r.delete();
151+
mMax = (int) metadataNode.getProperty("mMax");
152+
metadataNode.delete();
153+
}
154+
}
155+
//删掉树上的节点
156+
String[] keys = new String[mMax];
157+
for (int i = 0; i < mMax; i++) {
158+
keys[i] = "entryDataId" + i;
159+
}
160+
try (Transaction tx = graphdb.beginTx()) {
161+
Node node = tx.getNodeById(rootId);
162+
ArrayDeque<Node> stack = new ArrayDeque<>();
163+
stack.push(node);
164+
do {
165+
node = stack.pop();
166+
String label = node.getLabels().iterator().next().name();
167+
if (label.equals(Labels.RTREE_BRANCH.name())) {
168+
for (Relationship relationship : node.getRelationships(Direction.OUTGOING, Relationships.RTREE_PARENT_TO_CHILD)) {
169+
Node child = relationship.getEndNode();
170+
relationship.delete();
171+
stack.push(child);
172+
}
173+
} else if (label.equals(Labels.RTREE_LEAF.name())) {
174+
Map<String, Object> entryDataIds = node.getProperties(keys);
175+
entryDataIds.forEach((key, id) -> {
176+
dataNodeVisitor.visit((long) id);
177+
});
178+
}
179+
node.delete();
180+
} while (!stack.isEmpty());
181+
}
182+
}
183+
184+
/**
185+
* 向索引中添加数据
186+
*
187+
* @param t
188+
*/
189+
public void add(final RectNd t) {
190+
rTree.add(t);
191+
txCell.addChange();
192+
txCell.limitCommit();
193+
}
194+
195+
/**
196+
* 从索引中移除数据,注意不会删除数据节点,如需删除或其它操作应在自身业务代码中实现
197+
*
198+
* @param t 被移除的节点,min、max、dataNodeId必须与现有节点一致
199+
*/
200+
public void remove(final RectNd t) {
201+
rTree.remove(t);
202+
txCell.addChange();
203+
// txCell.getTx().getNodeById(t.getDataNodeId()).delete(); //由外部自行决定处理是否将其删除
204+
txCell.limitCommit();
205+
}
206+
207+
/**
208+
* 修改现有数据
209+
*
210+
* @param told 现有节点,min、max、dataNodeId必须与现有节点一致
211+
* @param tnew 新节点,dataNodeId必须与现有节点一致
212+
*/
213+
public void update(final RectNd told, final RectNd tnew) {
214+
rTree.update(told, tnew);
215+
txCell.addChange();
216+
txCell.limitCommit();
217+
}
218+
219+
@Override
220+
public void close() {
221+
txCell.commit();
222+
writeLock.unlock();
223+
}
224+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package org.wowtools.neo4j.rtree;
2+
3+
import org.neo4j.graphdb.Direction;
4+
import org.neo4j.graphdb.Node;
5+
import org.neo4j.graphdb.Relationship;
6+
import org.neo4j.graphdb.Transaction;
7+
import org.wowtools.neo4j.rtree.internal.RtreeLock;
8+
import org.wowtools.neo4j.rtree.internal.define.Labels;
9+
import org.wowtools.neo4j.rtree.internal.define.Relationships;
10+
import org.wowtools.neo4j.rtree.pojo.PointNd;
11+
import org.wowtools.neo4j.rtree.pojo.RectNd;
12+
import org.wowtools.neo4j.rtree.util.BooleanDataNodeVisitor;
13+
14+
import java.util.ArrayDeque;
15+
import java.util.Map;
16+
import java.util.concurrent.locks.Lock;
17+
18+
/**
19+
* 相交关系查询器
20+
* 查询器
21+
*
22+
* @author liuyu
23+
* @date 2021/12/24
24+
*/
25+
public class RtreeIntersectsSearcher {
26+
27+
28+
private final long metadataNodeId;
29+
private final Lock readLock;
30+
31+
32+
private RtreeIntersectsSearcher(long metadataNodeId, Lock readLock) {
33+
this.metadataNodeId = metadataNodeId;
34+
this.readLock = readLock;
35+
}
36+
37+
/**
38+
* 获取索引
39+
*
40+
* @param tx 事务 此事务需要在外部手动关闭
41+
* @param name 索引名
42+
* @return
43+
*/
44+
public static RtreeIntersectsSearcher get(Transaction tx, String name) {
45+
Node metadataNode = tx.findNode(Labels.METADATA, "name", name);
46+
if (null == metadataNode) {
47+
throw new RuntimeException("索引 " + name + " 不存在");
48+
}
49+
long metadataNodeId = metadataNode.getId();
50+
Lock readLock = RtreeLock.getUseReadWriteLock(name).readLock();
51+
RtreeIntersectsSearcher rtreeIntersectsSearcher = new RtreeIntersectsSearcher(metadataNodeId, readLock);
52+
return rtreeIntersectsSearcher;
53+
}
54+
55+
56+
/**
57+
* 相交查询
58+
*
59+
* @param bbox 查询的bbox范围
60+
* @param tx 事务 此事务需要在外部手动关闭
61+
* @param visitor 结果访问器
62+
*/
63+
public void intersects(RectNd bbox, Transaction tx, BooleanDataNodeVisitor visitor) {
64+
readLock.lock();
65+
try {
66+
Node metadataNode = tx.getNodeById(metadataNodeId);
67+
Node node = metadataNode.getRelationships(Relationships.RTREE_METADATA_TO_ROOT).iterator().next().getEndNode();
68+
ArrayDeque<Node> stack = new ArrayDeque<>();
69+
stack.push(node);
70+
do {
71+
node = stack.pop();
72+
//判断当前节点是否与bbox相交
73+
Map<String, Object> mbrProperties = node.getProperties("mbrMax", "mbrMin");
74+
PointNd min = new PointNd((double[]) mbrProperties.get("mbrMin"));
75+
PointNd max = new PointNd((double[]) mbrProperties.get("mbrMax"));
76+
RectNd nodeMbr = new RectNd(min, max);
77+
if (!bbox.intersects(nodeMbr)) {
78+
continue;
79+
}
80+
//子节点
81+
String label = node.getLabels().iterator().next().name();
82+
if (label.equals(Labels.RTREE_BRANCH.name())) {
83+
for (Relationship relationship : node.getRelationships(Direction.OUTGOING, Relationships.RTREE_PARENT_TO_CHILD)) {
84+
Node child = relationship.getEndNode();
85+
stack.push(child);
86+
}
87+
} else if (label.equals(Labels.RTREE_LEAF.name())) {
88+
Map<String, Object> properties = node.getAllProperties();
89+
int size = (int) properties.get("size");
90+
for (int i = 0; i < size; i++) {
91+
double[] rMin = (double[]) properties.get("rMin" + i);
92+
double[] rMax = (double[]) properties.get("rMax" + i);
93+
RectNd dataMbr = new RectNd(rMin, rMax);
94+
if (bbox.intersects(dataMbr)) {
95+
if (visitor.visit((long) properties.get("entryDataId" + i))) {
96+
return;
97+
}
98+
}
99+
}
100+
}
101+
} while (!stack.isEmpty());
102+
} finally {
103+
readLock.unlock();
104+
}
105+
106+
}
107+
108+
}

src/main/java/org/wowtools/neo4j/rtree/Constant.java renamed to src/main/java/org/wowtools/neo4j/rtree/geometry2dold/Constant.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* See the License for the specific language governing permissions and
1515
* limitations under the License.
1616
*/
17-
package org.wowtools.neo4j.rtree;
17+
package org.wowtools.neo4j.rtree.geometry2dold;
1818

1919
import org.neo4j.graphdb.Label;
2020
import org.neo4j.graphdb.RelationshipType;

src/main/java/org/wowtools/neo4j/rtree/RTreeIndexManager.java renamed to src/main/java/org/wowtools/neo4j/rtree/geometry2dold/RTreeIndexManager.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@
1414
* See the License for the specific language governing permissions and
1515
* limitations under the License.
1616
*/
17-
package org.wowtools.neo4j.rtree;
17+
package org.wowtools.neo4j.rtree.geometry2dold;
1818

1919
import org.locationtech.jts.geom.Geometry;
2020
import org.locationtech.jts.io.ParseException;
2121
import org.locationtech.jts.io.WKBReader;
2222
import org.neo4j.graphdb.*;
23-
import org.wowtools.neo4j.rtree.spatial.Envelope;
24-
import org.wowtools.neo4j.rtree.spatial.EnvelopeDecoder;
25-
import org.wowtools.neo4j.rtree.spatial.RTreeIndex;
26-
import org.wowtools.neo4j.rtree.util.GeometryBbox;
23+
import org.wowtools.neo4j.rtree.geometry2dold.spatial.Envelope;
24+
import org.wowtools.neo4j.rtree.geometry2dold.spatial.EnvelopeDecoder;
25+
import org.wowtools.neo4j.rtree.geometry2dold.spatial.RTreeIndex;
26+
import org.wowtools.neo4j.rtree.geometry2dold.util.GeometryBbox;
2727

2828
import java.util.ArrayDeque;
2929
import java.util.Deque;

0 commit comments

Comments
 (0)