1 package de.dlr.shepard.data.timeseries.daos;
2
3 import static de.dlr.shepard.common.util.CypherDslHelper.internalIdIs;
4 import static de.dlr.shepard.common.util.CypherDslHelper.notDeleted;
5 import static org.neo4j.cypherdsl.core.Cypher.match;
6 import static org.neo4j.cypherdsl.core.Cypher.node;
7
8 import de.dlr.shepard.common.neo4j.daos.GenericDAO;
9 import de.dlr.shepard.common.search.query.TimeseriesInContainerQueryBuilder;
10 import de.dlr.shepard.common.util.CypherDslHelper;
11 import de.dlr.shepard.common.util.Neo4jLabels;
12 import de.dlr.shepard.data.timeseries.model.Timeseries;
13 import de.dlr.shepard.data.timeseries.model.TimeseriesContainer;
14 import de.dlr.shepard.data.timeseries.model.TimeseriesTuple;
15 import jakarta.enterprise.context.RequestScoped;
16 import java.util.Collections;
17 import java.util.Map;
18 import java.util.NoSuchElementException;
19 import java.util.Optional;
20 import java.util.stream.Stream;
21 import java.util.stream.StreamSupport;
22 import org.neo4j.cypherdsl.core.Condition;
23 import org.neo4j.cypherdsl.core.Cypher;
24 import org.neo4j.cypherdsl.core.Node;
25 import org.neo4j.cypherdsl.core.Relationship;
26
27 @RequestScoped
28 public class TimeseriesDAO extends GenericDAO<Timeseries> {
29
30 private static final String TIMESERIES_CONTAINER_CYPHER_SYMBOLIC_NAME = "tsc";
31
32 @Override
33 public Class<Timeseries> getEntityType() {
34 return Timeseries.class;
35 }
36
37
38
39
40
41
42
43
44
45 private Stream<Timeseries> queryCypherWithRelations(String query) {
46 var result = session.query(query, Map.of(), true);
47 return StreamSupport.stream(result.spliterator(), false).map(resultEntry -> {
48 Timeseries tsObj = (Timeseries) resultEntry.get("ts");
49 TimeseriesContainer tscObj = (TimeseriesContainer) resultEntry.get(TIMESERIES_CONTAINER_CYPHER_SYMBOLIC_NAME);
50 TimeseriesTuple tsTupleObj = (TimeseriesTuple) resultEntry.get("tst");
51 tsObj.setContainer(tscObj);
52 tsObj.setTimeseriesTuple(tsTupleObj);
53 return tsObj;
54 });
55 }
56
57 public Stream<Timeseries> getAllTimeseriesInContainer(long containerId) {
58 var tsc = node(Neo4jLabels.TIMESERIES_CONTAINER).named(TIMESERIES_CONTAINER_CYPHER_SYMBOLIC_NAME);
59 var ts = node(Neo4jLabels.TIMESERIES).named("ts");
60 var tsTuple = node(Neo4jLabels.TIMESERIES_TUPLE).named("tst");
61 var isInContainer = ts.relationshipTo(tsc, Neo4jLabels.IS_IN_CONTAINER);
62 var hasTuple = ts.relationshipTo(tsTuple, Neo4jLabels.HAS_TIMESERIES_TUPLE);
63 var query = match(isInContainer, hasTuple)
64 .where(internalIdIs(tsc, containerId).and(notDeleted(ts)))
65 .returning(ts, tsc, tsTuple)
66 .build()
67 .getCypher();
68 return queryCypherWithRelations(query);
69 }
70
71 public long getCurrentMaximumTimeseriesId() {
72 var ts = node(Neo4jLabels.TIMESERIES);
73 var query = match(ts)
74 .returning(ts.property(Neo4jLabels.TIMESERIES_ID))
75 .orderBy(ts.property(Neo4jLabels.TIMESERIES_ID).descending())
76 .limit(1)
77 .build()
78 .getCypher();
79 try {
80 return session.query(Long.class, query, Collections.emptyMap()).iterator().next();
81 } catch (NoSuchElementException e) {
82
83 return 0;
84 }
85 }
86
87 public Optional<Timeseries> findTimeseries(long containerId, TimeseriesTuple tsTuple) {
88 var tsTupleNode = node(Neo4jLabels.TIMESERIES_TUPLE)
89 .withProperties(
90 Neo4jLabels.MEASUREMENT,
91 Cypher.literalOf(tsTuple.getMeasurement()),
92 Neo4jLabels.DEVICE,
93 Cypher.literalOf(tsTuple.getDevice()),
94 Neo4jLabels.LOCATION,
95 Cypher.literalOf(tsTuple.getLocation()),
96 Neo4jLabels.SYMBOLIC_NAME,
97 Cypher.literalOf(tsTuple.getSymbolicName()),
98 Neo4jLabels.FIELD,
99 Cypher.literalOf(tsTuple.getField())
100 )
101 .named("tst");
102 var tsc = node(Neo4jLabels.TIMESERIES_CONTAINER).named(TIMESERIES_CONTAINER_CYPHER_SYMBOLIC_NAME);
103 var ts = node(Neo4jLabels.TIMESERIES).named("ts");
104 var query = match(
105 tsTupleNode
106 .relationshipFrom(ts, Neo4jLabels.HAS_TIMESERIES_TUPLE)
107 .relationshipTo(tsc, Neo4jLabels.IS_IN_CONTAINER)
108 )
109 .where(internalIdIs(tsc, containerId).and(notDeleted(ts)))
110 .returning(tsTupleNode, tsc, ts)
111 .build()
112 .getCypher();
113 return queryCypherWithRelations(query).findFirst();
114 }
115
116 public Optional<Timeseries> findByTimeseriesId(long timeseriesId) {
117 var ts = node(Neo4jLabels.TIMESERIES).withProperties(Neo4jLabels.TIMESERIES_ID, Cypher.literalOf(timeseriesId));
118 var related = Cypher.anyNode();
119 var rels = ts.relationshipTo(related);
120 var query = match(ts, rels, related).where(notDeleted(ts)).returning(ts, rels, related).build().getCypher();
121 return this.findByQuery(query).findFirst();
122 }
123
124 public Stream<Timeseries> findByQuintupleInContainer(
125 long containerId,
126 String measurement,
127 String device,
128 String location,
129 String symbolicName,
130 String field
131 ) {
132 Node container = Cypher.node(Neo4jLabels.TIMESERIES_CONTAINER).named(TIMESERIES_CONTAINER_CYPHER_SYMBOLIC_NAME);
133 Node timeseries = Cypher.node(Neo4jLabels.TIMESERIES).named("ts");
134 Node timeseriesTuple = Cypher.node(Neo4jLabels.TIMESERIES_TUPLE).named("tsp");
135 Node neighbor = Cypher.anyNode().named("nb");
136 Node timeseriesDummy = Cypher.node(Neo4jLabels.TIMESERIES).named("tsd");
137 Relationship neighborhood = timeseriesDummy.relationshipBetween(neighbor).named("nh");
138 Relationship hasTuple = timeseries.relationshipTo(timeseriesTuple, Neo4jLabels.HAS_TIMESERIES_TUPLE).named("htt");
139 Relationship isInContainer = timeseries.relationshipTo(container, Neo4jLabels.IS_IN_CONTAINER).named("iic");
140 Condition wherePart = CypherDslHelper.notDeleted(timeseries).and(
141 CypherDslHelper.isNotDeletedWithInternalId(container, containerId)
142 );
143 if (measurement != null) wherePart = wherePart.and(
144 TimeseriesInContainerQueryBuilder.hasStringProperty(timeseriesTuple, Neo4jLabels.MEASUREMENT, measurement, "eq")
145 );
146 if (device != null) wherePart = wherePart.and(
147 TimeseriesInContainerQueryBuilder.hasStringProperty(timeseriesTuple, Neo4jLabels.DEVICE, device, "eq")
148 );
149 if (location != null) wherePart = wherePart.and(
150 TimeseriesInContainerQueryBuilder.hasStringProperty(timeseriesTuple, Neo4jLabels.LOCATION, location, "eq")
151 );
152 if (symbolicName != null) wherePart = wherePart.and(
153 TimeseriesInContainerQueryBuilder.hasStringProperty(
154 timeseriesTuple,
155 Neo4jLabels.SYMBOLIC_NAME,
156 symbolicName,
157 "eq"
158 )
159 );
160 if (field != null) wherePart = wherePart.and(
161 TimeseriesInContainerQueryBuilder.hasStringProperty(timeseriesTuple, Neo4jLabels.FIELD, field, "eq")
162 );
163 String query = Cypher.match(timeseries, container, timeseriesTuple, hasTuple, isInContainer)
164 .where(wherePart)
165 .optionalMatch(neighborhood)
166 .where(timeseriesDummy.internalId().eq(timeseries.internalId()))
167 .returning(timeseries, container, timeseriesTuple, neighborhood, hasTuple, isInContainer, neighbor)
168 .build()
169 .getCypher();
170 return findByQuery(query);
171 }
172
173 public void deleteAllTimeseriesInContainer(long containerId) {
174 var ts = node(Neo4jLabels.TIMESERIES);
175 var tsc = node(Neo4jLabels.TIMESERIES_CONTAINER);
176 var query = match(ts.relationshipTo(tsc, Neo4jLabels.IS_IN_CONTAINER))
177 .where(internalIdIs(tsc, containerId))
178 .set(ts.property(Neo4jLabels.DELETED), Cypher.literalOf(true))
179 .build()
180 .getCypher();
181 session.query(query, Collections.emptyMap());
182 }
183 }