View Javadoc
1   package de.dlr.shepard.common.search.services;
2   
3   import com.mongodb.client.MongoCollection;
4   import com.mongodb.client.MongoDatabase;
5   import de.dlr.shepard.auth.users.entities.User;
6   import de.dlr.shepard.auth.users.services.UserService;
7   import de.dlr.shepard.common.exceptions.InvalidBodyException;
8   import de.dlr.shepard.common.neo4j.io.BasicEntityIO;
9   import de.dlr.shepard.common.search.io.ResponseBody;
10  import de.dlr.shepard.common.search.io.ResultTriple;
11  import de.dlr.shepard.common.search.io.SearchBody;
12  import de.dlr.shepard.common.search.io.SearchScope;
13  import de.dlr.shepard.common.search.query.MongoDBQueryBuilder;
14  import de.dlr.shepard.common.util.TraversalRules;
15  import de.dlr.shepard.context.references.basicreference.entities.BasicReference;
16  import de.dlr.shepard.context.references.structureddata.daos.StructuredDataReferenceDAO;
17  import de.dlr.shepard.context.references.structureddata.entities.StructuredDataReference;
18  import de.dlr.shepard.data.structureddata.entities.StructuredData;
19  import jakarta.enterprise.context.RequestScoped;
20  import jakarta.inject.Inject;
21  import jakarta.inject.Named;
22  import java.util.ArrayList;
23  import java.util.HashMap;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Set;
28  import org.bson.Document;
29  
30  @RequestScoped
31  public class StructuredDataSearchService {
32  
33    @Inject
34    StructuredDataReferenceDAO structuredDataReferenceDAO;
35  
36    @Inject
37    UserService userService;
38  
39    @Inject
40    @Named("mongoDatabase")
41    MongoDatabase mongoDatabase;
42  
43    public ResponseBody search(SearchBody searchBody) {
44      User user = userService.getCurrentUser();
45  
46      var reachableReferences = findReachableReferences(searchBody, user.getUsername());
47      var matchingReferences = findMatchingReferences(reachableReferences, searchBody);
48      return getStructuredDataResponse(matchingReferences, searchBody);
49    }
50  
51    private ResponseBody getStructuredDataResponse(
52      Map<StructuredDataReference, Long> matchingReferences,
53      SearchBody searchBody
54    ) {
55      BasicReference[] references = matchingReferences.keySet().toArray(new BasicReference[0]);
56      ResultTriple[] resultTriples = new ResultTriple[references.length];
57      BasicEntityIO[] results = new BasicEntityIO[references.length];
58      for (var i = 0; i < references.length; i++) {
59        // The collection is not loaded at this time, so we have to use the given id
60        resultTriples[i] = new ResultTriple(
61          matchingReferences.get(references[i]),
62          references[i].getDataObject().getId(),
63          references[i].getId()
64        );
65        results[i] = new BasicEntityIO(references[i]);
66      }
67      ResponseBody responseBody = new ResponseBody(resultTriples, results, searchBody.getSearchParams());
68      return responseBody;
69    }
70  
71    private Map<StructuredDataReference, Long> findMatchingReferences(
72      Map<StructuredDataReference, Long> reachableReferences,
73      SearchBody searchBody
74    ) {
75      Map<StructuredDataReference, Long> matchingReferences = new HashMap<>();
76      for (var reference : reachableReferences.entrySet()) {
77        String mongoContainerId = reference.getKey().getStructuredDataContainer().getMongoId();
78        MongoCollection<Document> mongoContainer = mongoDatabase.getCollection(mongoContainerId);
79        List<String> mongoStructuredDataIds = new ArrayList<>();
80        for (StructuredData structuredData : reference.getKey().getStructuredDatas()) {
81          mongoStructuredDataIds.add(makeMongoQueryId(structuredData.getOid()));
82        }
83        String mongoQuery = "{_id: {$in: " + makeMongoQueryArray(mongoStructuredDataIds) + "}";
84        String mongoSearchQuery = searchBody.getSearchParams().getQuery();
85        // JSON queries start with a curly bracket ({) so they have to be translated to
86        // MongoDB syntax first
87        // TODO: Deprecate MongoDB queries
88        if (mongoSearchQuery.startsWith("{")) mongoSearchQuery = MongoDBQueryBuilder.getMongoDBQueryString(
89          mongoSearchQuery
90        );
91        mongoQuery += ", " + mongoSearchQuery + "}";
92        var mongoQueryDocument = Document.parse(mongoQuery);
93        var mongoQueryResult = mongoContainer.find(mongoQueryDocument);
94        if (mongoQueryResult.first() != null) matchingReferences.put(reference.getKey(), reference.getValue());
95      }
96      return matchingReferences;
97    }
98  
99    private Map<StructuredDataReference, Long> findReachableReferences(SearchBody searchBody, String userName) {
100     Map<StructuredDataReference, Long> ret = new HashMap<>();
101     for (SearchScope searchScope : searchBody.getScopes()) findReachableReferenceFromScope(
102       searchScope,
103       userName
104     ).forEach(r -> ret.put(r, searchScope.getCollectionId()));
105     return ret;
106   }
107 
108   private Set<StructuredDataReference> findReachableReferenceFromScope(SearchScope searchScope, String userName) {
109     Set<StructuredDataReference> ret = new HashSet<>();
110     TraversalRules[] traversalRules = searchScope.getTraversalRules();
111     Long collectionShepardId = searchScope.getCollectionId();
112     if (collectionShepardId == null) {
113       throw new InvalidBodyException("Collection is necessary");
114     }
115     // no DataObjectId given
116     if (searchScope.getDataObjectId() == null) {
117       ret.addAll(structuredDataReferenceDAO.findReachableReferencesByNeo4jId(collectionShepardId, userName));
118     }
119     // DataObjectId given
120     else {
121       long startShepardId = searchScope.getDataObjectId();
122       // consider only start node
123       if (traversalRules.length == 0) {
124         List<StructuredDataReference> reachableReferences = structuredDataReferenceDAO.findReachableReferencesByNeo4jId(
125           collectionShepardId,
126           startShepardId,
127           userName
128         );
129         ret.addAll(reachableReferences);
130       }
131       // search according to traversal rules
132       else {
133         for (TraversalRules traversalRule : traversalRules) {
134           List<StructuredDataReference> reachableReferences =
135             structuredDataReferenceDAO.findReachableReferencesByNeo4jId(
136               traversalRule,
137               collectionShepardId,
138               startShepardId,
139               userName
140             );
141           ret.addAll(reachableReferences);
142         }
143       }
144     }
145     return ret;
146   }
147 
148   private static String makeMongoQueryId(String mongoId) {
149     return "{$oid: '" + mongoId + "'}";
150   }
151 
152   private static String makeMongoQueryArray(List<String> strings) {
153     return "[" + String.join(", ", strings) + "]";
154   }
155 }