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