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     } else {
119       // DataObjectId given
120       long startShepardId = searchScope.getDataObjectId();
121       // consider only start node
122       if (traversalRules.length == 0) {
123         List<StructuredDataReference> reachableReferences = structuredDataReferenceDAO.findReachableReferencesByNeo4jId(
124           collectionShepardId,
125           startShepardId,
126           userName
127         );
128         ret.addAll(reachableReferences);
129       } else {
130         // search according to traversal rules
131         for (TraversalRules traversalRule : traversalRules) {
132           List<StructuredDataReference> reachableReferences =
133             structuredDataReferenceDAO.findReachableReferencesByNeo4jId(
134               traversalRule,
135               collectionShepardId,
136               startShepardId,
137               userName
138             );
139           ret.addAll(reachableReferences);
140         }
141       }
142     }
143     return ret;
144   }
145 
146   private static String makeMongoQueryId(String mongoId) {
147     return "{$oid: '" + mongoId + "'}";
148   }
149 
150   private static String makeMongoQueryArray(List<String> strings) {
151     return "[" + String.join(", ", strings) + "]";
152   }
153 }