Skip to content

Commit 6eba27b

Browse files
authored
Merge pull request #3539 from ControlSystemStudio/CSSTUDIO-3159
Support search for referenced snapshots
2 parents cda8ec5 + 4e33a47 commit 6eba27b

File tree

13 files changed

+357
-82
lines changed

13 files changed

+357
-82
lines changed

app/save-and-restore/app/doc/index.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ Brief description of all items in the context menu (details on actions are outli
8888
* Restore from client - restore a snapshot or composite snapshot from the client application.
8989
* Restore from service - restore a snapshot or composite snapshot from the service.
9090
* Edit - edit a configuration.
91-
* Rename - rename a folder or configuration.
91+
* Find references - locates composite snapshot nodes referencing the selected node.
92+
* Rename - rename a folder.
9293
* Copy - put selected items on clipboard.
9394
* Paste - paste items from clipboard.
9495
* Delete - delete selected items.

app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/SaveAndRestoreController.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
import org.phoebus.applications.saveandrestore.ui.contextmenu.CreateSnapshotMenuItem;
8787
import org.phoebus.applications.saveandrestore.ui.contextmenu.EditCompositeMenuItem;
8888
import org.phoebus.applications.saveandrestore.ui.contextmenu.ExportToCSVMenuItem;
89+
import org.phoebus.applications.saveandrestore.ui.contextmenu.FindReferencesMenuItem;
8990
import org.phoebus.applications.saveandrestore.ui.contextmenu.ImportFromCSVMenuItem;
9091
import org.phoebus.applications.saveandrestore.ui.contextmenu.LoginMenuItem;
9192
import org.phoebus.applications.saveandrestore.ui.contextmenu.NewCompositeSnapshotMenuItem;
@@ -232,6 +233,7 @@ public class SaveAndRestoreController extends SaveAndRestoreBaseController
232233
}),
233234
new SeparatorMenuItem(),
234235
new EditCompositeMenuItem(this, selectedItemsProperty, this::editCompositeSnapshot),
236+
new FindReferencesMenuItem(this, selectedItemsProperty, this::findReferences),
235237
new RenameFolderMenuItem(this, selectedItemsProperty, this::renameNode),
236238
copyMenuItem,
237239
pasteMenuItem,
@@ -525,7 +527,7 @@ private void deleteTreeItems(ObservableList<TreeItem<Node>> items) {
525527
/**
526528
* Opens a new snapshot view tab associated with the selected configuration.
527529
*/
528-
public void openConfigurationForSnapshot() {
530+
private void openConfigurationForSnapshot() {
529531
TreeItem<Node> treeItem = browserSelectionModel.getSelectedItems().get(0);
530532
SnapshotTab tab = new SnapshotTab(treeItem.getValue(), saveAndRestoreService);
531533
tab.newSnapshot(treeItem.getValue());
@@ -634,7 +636,7 @@ public void nodeDoubleClicked(Node node) {
634636
* Launches the composite snapshot editor view. Note that a tab showing this view uses the "edit_" prefix
635637
* for the id since it would otherwise clash with a restore view of a composite snapshot.
636638
*/
637-
public void editCompositeSnapshot() {
639+
private void editCompositeSnapshot() {
638640
Node compositeSnapshotNode = browserSelectionModel.getSelectedItem().getValue();
639641
editCompositeSnapshot(compositeSnapshotNode, Collections.emptyList());
640642
}
@@ -709,11 +711,11 @@ private Tab getTab(String id) {
709711
/**
710712
* Creates a new configuration in the selected tree node.
711713
*/
712-
public void createNewConfiguration() {
714+
private void createNewConfiguration() {
713715
launchTabForNewConfiguration(browserSelectionModel.getSelectedItems().get(0).getValue());
714716
}
715717

716-
public void createNewCompositeSnapshot() {
718+
private void createNewCompositeSnapshot() {
717719
launchTabForNewCompositeSnapshot(browserSelectionModel.getSelectedItems().get(0).getValue(),
718720
Collections.emptyList());
719721
}
@@ -1506,4 +1508,9 @@ public boolean doCloseCheck(){
15061508
}
15071509
return true;
15081510
}
1511+
1512+
private void findReferences(){
1513+
SearchAndFilterTab searchAndFilterTab = openSearchWindow();
1514+
searchAndFilterTab.getController().findReferencesForSnapshot(treeView.getSelectionModel().getSelectedItems().get(0).getValue().getUniqueId());
1515+
}
15091516
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright (C) 2024 European Spallation Source ERIC.
3+
*/
4+
5+
package org.phoebus.applications.saveandrestore.ui.contextmenu;
6+
7+
import javafx.collections.ObservableList;
8+
import org.phoebus.applications.saveandrestore.Messages;
9+
import org.phoebus.applications.saveandrestore.model.Node;
10+
import org.phoebus.applications.saveandrestore.model.NodeType;
11+
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreBaseController;
12+
import org.phoebus.ui.javafx.ImageCache;
13+
14+
/**
15+
* {@link javafx.scene.control.MenuItem} for finding references of a snapshot or composite snapshot.
16+
*/
17+
public class FindReferencesMenuItem extends SaveAndRestoreMenuItem {
18+
19+
public FindReferencesMenuItem(SaveAndRestoreBaseController saveAndRestoreController,
20+
ObservableList<Node> selectedItemsProperty,
21+
Runnable onAction) {
22+
super(saveAndRestoreController, selectedItemsProperty, onAction);
23+
setGraphic(ImageCache.getImageView(ImageCache.class, "/icons/sar-search_18x18.png"));
24+
setText(Messages.findSnapshotReferences);
25+
}
26+
27+
@Override
28+
public void configure() {
29+
visibleProperty().set(selectedItemsProperty.size() == 1 &&
30+
(selectedItemsProperty.get(0).getNodeType().equals(NodeType.SNAPSHOT) ||
31+
selectedItemsProperty.get(0).getNodeType().equals(NodeType.COMPOSITE_SNAPSHOT)));
32+
}
33+
}

app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/search/SearchAndFilterTab.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import org.phoebus.applications.saveandrestore.Messages;
2626
import org.phoebus.applications.saveandrestore.SaveAndRestoreApplication;
2727
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreController;
28-
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreService;
2928
import org.phoebus.applications.saveandrestore.ui.SaveAndRestoreTab;
3029
import org.phoebus.framework.nls.NLS;
3130
import org.phoebus.ui.javafx.ImageCache;
@@ -65,7 +64,7 @@ public SearchAndFilterTab(SaveAndRestoreController saveAndRestoreController) {
6564
Node node = loader.load();
6665
controller = loader.getController();
6766
setContent(node);
68-
setOnCloseRequest(event -> ((SearchAndFilterViewController)controller).handleSaveAndFilterTabClosed());
67+
setOnCloseRequest(event -> ((SearchAndFilterViewController) controller).handleSaveAndFilterTabClosed());
6968
} catch (IOException e) {
7069
Logger.getLogger(SearchAndFilterTab.class.getName())
7170
.log(Level.SEVERE, "Unable to load search tab content fxml", e);
@@ -75,4 +74,8 @@ public SearchAndFilterTab(SaveAndRestoreController saveAndRestoreController) {
7574
setText(Messages.search);
7675
setGraphic(new ImageView(ImageCache.getImage(ImageCache.class, "/icons/sar-search_18x18.png")));
7776
}
77+
78+
public SearchAndFilterViewController getController() {
79+
return (SearchAndFilterViewController) controller;
80+
}
7881
}

app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/search/SearchAndFilterViewController.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ public class SearchAndFilterViewController extends SaveAndRestoreBaseController
208208

209209
private final SimpleBooleanProperty goldenOnlyProperty = new SimpleBooleanProperty();
210210

211+
private final SimpleStringProperty referencedProperty = new SimpleStringProperty();
212+
211213
private static final Logger LOGGER = Logger.getLogger(SearchAndFilterViewController.class.getName());
212214

213215
private final SimpleBooleanProperty disableUi = new SimpleBooleanProperty();
@@ -534,6 +536,9 @@ private String buildQueryString() {
534536
map.put(Keys.TAGS.getName(), tags + "," + Tag.GOLDEN);
535537
}
536538
}
539+
if (referencedProperty.get() != null && !referencedProperty.get().trim().isEmpty()) {
540+
map.put(Keys.REFERENCED.getName(), referencedProperty.get());
541+
}
537542
return SearchQueryUtil.toQueryString(map);
538543
}
539544

@@ -649,12 +654,17 @@ public void handleWebSocketMessage(SaveAndRestoreWebSocketMessage<?> saveAndRest
649654
}
650655

651656
@Override
652-
public void handleTabClosed(){
657+
public void handleTabClosed() {
653658
webSocketClientService.removeWebSocketMessageHandler(this);
654659
}
655660

656661
@Override
657-
public boolean doCloseCheck(){
662+
public boolean doCloseCheck() {
658663
return true;
659664
}
665+
666+
public void findReferencesForSnapshot(String nodeId) {
667+
referencedProperty.setValue(nodeId);
668+
updateParametersAndSearch();
669+
}
660670
}

app/save-and-restore/model/src/main/java/org/phoebus/applications/saveandrestore/model/search/SearchQueryUtil.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ public enum Keys {
4141
FROM("from"),
4242
SIZE("size"),
4343
GOLDEN("golden"),
44-
PVS("pvs");
44+
PVS("pvs"),
45+
REFERENCED("referenced");
4546

4647
private final String name;
4748

@@ -58,7 +59,7 @@ public String toString() {
5859
return getName();
5960
}
6061

61-
protected static final Map<String, Keys> lookupTable = new HashMap<>();
62+
private static final Map<String, Keys> lookupTable = new HashMap<>();
6263

6364
static {
6465
lookupTable.put("name", Keys.NAME);
@@ -70,6 +71,7 @@ public String toString() {
7071
lookupTable.put("type", Keys.TYPE);
7172
lookupTable.put("golden", Keys.GOLDEN);
7273
lookupTable.put("pvs", Keys.PVS);
74+
lookupTable.put("referenced", Keys.REFERENCED);
7375
}
7476

7577
public static Keys findKey(String keyName) {

services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/persistence/dao/NodeDAO.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,6 @@ public interface NodeDAO {
6363
*/
6464
List<Node> getNodes(List<String> uniqueNodeIds);
6565

66-
/**
67-
* This is deprecated, use {@link #deleteNodes} instead.
68-
*
69-
* @param nodeId The unique id of the node to delete.
70-
*/
71-
void deleteNode(String nodeId);
7266

7367
/**
7468
* Checks that each of the node ids passed to this method exist, and that none of them

services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/persistence/dao/impl/elasticsearch/CompositeSnapshotDataRepository.java

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,20 @@
3636
import co.elastic.clients.elasticsearch.core.SearchRequest;
3737
import co.elastic.clients.elasticsearch.core.SearchResponse;
3838
import co.elastic.clients.elasticsearch.core.search.Hit;
39+
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
3940
import co.elastic.clients.transport.endpoints.BooleanResponse;
40-
import org.elasticsearch.action.search.SearchRequestBuilder;
41-
import org.phoebus.applications.saveandrestore.model.CompositeSnapshot;
4241
import org.phoebus.applications.saveandrestore.model.CompositeSnapshotData;
42+
import org.phoebus.applications.saveandrestore.model.Node;
43+
import org.phoebus.applications.saveandrestore.model.search.SearchResult;
44+
import org.phoebus.service.saveandrestore.model.ESTreeNode;
45+
import org.phoebus.service.saveandrestore.search.SearchUtil;
4346
import org.springframework.beans.factory.annotation.Autowired;
4447
import org.springframework.beans.factory.annotation.Qualifier;
4548
import org.springframework.beans.factory.annotation.Value;
4649
import org.springframework.data.repository.CrudRepository;
4750
import org.springframework.http.HttpStatus;
4851
import org.springframework.stereotype.Repository;
52+
import org.springframework.util.MultiValueMap;
4953
import org.springframework.web.server.ResponseStatusException;
5054

5155
import java.io.IOException;
@@ -62,13 +66,20 @@
6266
@Repository
6367
public class CompositeSnapshotDataRepository implements CrudRepository<CompositeSnapshotData, String> {
6468

69+
@SuppressWarnings("unused")
6570
@Value("${elasticsearch.composite_snapshot_node.index:saveandrestore_composite_snapshot}")
6671
private String ES_COMPOSITE_SNAPSHOT_INDEX;
6772

73+
@Autowired
74+
private ElasticsearchTreeRepository elasticsearchTreeRepository;
75+
6876
@Autowired
6977
@Qualifier("client")
7078
ElasticsearchClient client;
7179

80+
@Autowired
81+
private SearchUtil searchUtil;
82+
7283
private final Logger logger = Logger.getLogger(CompositeSnapshotDataRepository.class.getName());
7384

7485
@Override
@@ -137,19 +148,20 @@ public boolean existsById(String s) {
137148
* Retrieves all {@link CompositeSnapshotData} documents. Note that to work around the limits in
138149
* Elasticsearch (e.g. max 10000 documents in a search request), the implementation uses paginated search to repeatedly
139150
* query for next round of hits. A page size of 100 is used for each query.
151+
*
140152
* @return An {@link Iterable} of {@link CompositeSnapshotData} objects, potentially empty.
141153
*/
142154
@Override
143-
public Iterable<CompositeSnapshotData> findAll() {
155+
public Iterable<CompositeSnapshotData> findAll() {
144156
List<CompositeSnapshotData> result = new ArrayList<>();
145157
int pageSize = 100;
146158
int from = 0;
147-
while(true){
159+
while (true) {
148160
try {
149161
SearchResponse<CompositeSnapshotData> searchResponse = runPagedMatchAll(pageSize, from);
150162
result.addAll(searchResponse.hits().hits().stream().map(Hit::source).collect(Collectors.toList()));
151163
from += searchResponse.hits().hits().size();
152-
if(searchResponse.hits().hits().size() < pageSize){
164+
if (searchResponse.hits().hits().size() < pageSize) {
153165
break;
154166
}
155167
} catch (IOException e) {
@@ -160,13 +172,13 @@ public Iterable<CompositeSnapshotData> findAll() {
160172
return result;
161173
}
162174

163-
private SearchResponse<CompositeSnapshotData> runPagedMatchAll(int pageSize, int from) throws IOException{
175+
private SearchResponse<CompositeSnapshotData> runPagedMatchAll(int pageSize, int from) throws IOException {
164176
SearchRequest searchRequest =
165177
SearchRequest.of(s ->
166-
s.index(ES_COMPOSITE_SNAPSHOT_INDEX)
167-
.query(new MatchAllQuery.Builder().build()._toQuery())
168-
.size(pageSize)
169-
.from(from));
178+
s.index(ES_COMPOSITE_SNAPSHOT_INDEX)
179+
.query(new MatchAllQuery.Builder().build()._toQuery())
180+
.size(pageSize)
181+
.from(from));
170182
return client.search(searchRequest, CompositeSnapshotData.class);
171183
}
172184

@@ -177,14 +189,13 @@ public Iterable<CompositeSnapshotData> findAllById(Iterable<String> strings) {
177189

178190
@Override
179191
public long count() {
180-
try{
192+
try {
181193
CountRequest countRequest = CountRequest.of(c ->
182194
c.index(ES_COMPOSITE_SNAPSHOT_INDEX));
183195
CountResponse countResponse = client.count(countRequest);
184196
return countResponse.count();
185-
}
186-
catch(Exception e){
187-
logger.log(Level.SEVERE, "Failed to count CompositeSnapshot objects" , e);
197+
} catch (Exception e) {
198+
logger.log(Level.SEVERE, "Failed to count CompositeSnapshot objects", e);
188199
throw new RuntimeException(e);
189200
}
190201
}
@@ -195,10 +206,9 @@ public void deleteById(String s) {
195206
DeleteRequest deleteRequest = DeleteRequest.of(d ->
196207
d.index(ES_COMPOSITE_SNAPSHOT_INDEX).id(s).refresh(Refresh.True));
197208
DeleteResponse deleteResponse = client.delete(deleteRequest);
198-
if(deleteResponse.result().equals(Result.Deleted)){
209+
if (deleteResponse.result().equals(Result.Deleted)) {
199210
logger.log(Level.WARNING, "Composite snapshot with id " + s + " deleted.");
200-
}
201-
else{
211+
} else {
202212
logger.log(Level.WARNING, "Composite snapshot with id " + s + " NOT deleted.");
203213
}
204214
} catch (IOException e) {
@@ -234,4 +244,28 @@ public void deleteAll() {
234244
throw new RuntimeException(e);
235245
}
236246
}
247+
248+
/**
249+
* Finds {@link Node}s of type {@link org.phoebus.applications.saveandrestore.model.NodeType#COMPOSITE_SNAPSHOT} referencing
250+
* the node id present in the search parameters.
251+
*
252+
* @param searchParameters {@link MultiValueMap} that should contain an element keyed &quot;referenced&quot;
253+
* @return A potentially empty {@link SearchResult}
254+
*/
255+
public SearchResult referenced(MultiValueMap<String, String> searchParameters) {
256+
257+
SearchRequest searchRequest = searchUtil.buildSearchRequest(searchParameters);
258+
try {
259+
SearchResponse<CompositeSnapshotData> response = client.search(searchRequest, CompositeSnapshotData.class);
260+
HitsMetadata<CompositeSnapshotData> hitsMetadata = response.hits();
261+
List<CompositeSnapshotData> compositeSnapshotDataList = hitsMetadata.hits().stream().map(Hit::source).toList();
262+
Iterable<ESTreeNode> esTreeNodes = elasticsearchTreeRepository.findAllById(compositeSnapshotDataList.stream().map(CompositeSnapshotData::getUniqueId).toList());
263+
List<Node> list = new ArrayList<>();
264+
esTreeNodes.iterator().forEachRemaining(es -> list.add(es.getNode()));
265+
return new SearchResult((int) hitsMetadata.total().value(), list);
266+
} catch (IOException e) {
267+
logger.log(Level.SEVERE, "Failed to search for referenced snapshot nodes", e);
268+
throw new RuntimeException(e);
269+
}
270+
}
237271
}

services/save-and-restore/src/main/java/org/phoebus/service/saveandrestore/persistence/dao/impl/elasticsearch/ConfigurationDataRepository.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import co.elastic.clients.elasticsearch.core.search.Hit;
2727
import co.elastic.clients.transport.endpoints.BooleanResponse;
2828
import org.phoebus.applications.saveandrestore.model.ConfigurationData;
29+
import org.phoebus.applications.saveandrestore.model.Node;
2930
import org.phoebus.service.saveandrestore.search.SearchUtil;
3031
import org.springframework.beans.factory.annotation.Autowired;
3132
import org.springframework.beans.factory.annotation.Qualifier;
@@ -212,6 +213,5 @@ public List<ConfigurationData> searchOnPvName(MultiValueMap<String, String> sear
212213
} catch (IOException e) {
213214
throw new RuntimeException(e);
214215
}
215-
216216
}
217217
}

0 commit comments

Comments
 (0)