StructuredDataSearcher.java

package de.dlr.shepard.search.unified;

import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import de.dlr.shepard.exceptions.InvalidBodyException;
import de.dlr.shepard.mongoDB.StructuredData;
import de.dlr.shepard.neo4Core.dao.StructuredDataReferenceDAO;
import de.dlr.shepard.neo4Core.entities.BasicReference;
import de.dlr.shepard.neo4Core.entities.StructuredDataReference;
import de.dlr.shepard.neo4Core.io.BasicEntityIO;
import de.dlr.shepard.search.MongoDBEmitter;
import de.dlr.shepard.util.TraversalRules;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.bson.Document;

@RequestScoped
public class StructuredDataSearcher implements ISearcher {

  private StructuredDataReferenceDAO structuredDataReferenceDAO;

  public StructuredDataSearcher() {}

  @Inject
  @Named("mongoDatabase")
  MongoDatabase mongoDatabase;

  @Inject
  public StructuredDataSearcher(StructuredDataReferenceDAO structuredDataReferenceDAO) {
    this.structuredDataReferenceDAO = structuredDataReferenceDAO;
  }

  @Override
  public ResponseBody search(SearchBody searchBody, String userName) {
    var reachableReferences = findReachableReferences(searchBody, userName);
    var matchingReferences = findMatchingReferences(reachableReferences, searchBody);
    return getStructuredDataResponse(matchingReferences, searchBody);
  }

  private ResponseBody getStructuredDataResponse(
    Map<StructuredDataReference, Long> matchingReferences,
    SearchBody searchBody
  ) {
    BasicReference[] references = matchingReferences.keySet().toArray(new BasicReference[0]);
    ResultTriple[] resultTriples = new ResultTriple[references.length];
    BasicEntityIO[] results = new BasicEntityIO[references.length];
    for (var i = 0; i < references.length; i++) {
      // The collection is not loaded at this time, so we have to use the given id
      resultTriples[i] = new ResultTriple(
        matchingReferences.get(references[i]),
        references[i].getDataObject().getId(),
        references[i].getId()
      );
      results[i] = new BasicEntityIO(references[i]);
    }
    ResponseBody responseBody = new ResponseBody(resultTriples, results, searchBody.getSearchParams());
    return responseBody;
  }

  private Map<StructuredDataReference, Long> findMatchingReferences(
    Map<StructuredDataReference, Long> reachableReferences,
    SearchBody searchBody
  ) {
    Map<StructuredDataReference, Long> matchingReferences = new HashMap<>();
    for (var reference : reachableReferences.entrySet()) {
      String mongoContainerId = reference.getKey().getStructuredDataContainer().getMongoId();
      MongoCollection<Document> mongoContainer = mongoDatabase.getCollection(mongoContainerId);
      List<String> mongoStructuredDataIds = new ArrayList<>();
      for (StructuredData structuredData : reference.getKey().getStructuredDatas()) {
        mongoStructuredDataIds.add(makeMongoQueryId(structuredData.getOid()));
      }
      String mongoQuery = "{_id: {$in: " + makeMongoQueryArray(mongoStructuredDataIds) + "}";
      String mongoSearchQuery = searchBody.getSearchParams().getQuery();
      // JSON queries start with a curly bracket ({) so they have to be translated to
      // MongoDB syntax first
      // TODO: Deprecate MongoDB queries
      if (mongoSearchQuery.startsWith("{")) mongoSearchQuery = MongoDBEmitter.emitMongoDB(mongoSearchQuery);
      mongoQuery += ", " + mongoSearchQuery + "}";
      var mongoQueryDocument = Document.parse(mongoQuery);
      var mongoQueryResult = mongoContainer.find(mongoQueryDocument);
      if (mongoQueryResult.first() != null) matchingReferences.put(reference.getKey(), reference.getValue());
    }
    return matchingReferences;
  }

  private Map<StructuredDataReference, Long> findReachableReferences(SearchBody searchBody, String userName) {
    Map<StructuredDataReference, Long> ret = new HashMap<>();
    for (SearchScope searchScope : searchBody.getScopes()) findReachableReferenceFromScope(
      searchScope,
      userName
    ).forEach(r -> ret.put(r, searchScope.getCollectionId()));
    return ret;
  }

  private Set<StructuredDataReference> findReachableReferenceFromScope(SearchScope searchScope, String userName) {
    Set<StructuredDataReference> ret = new HashSet<>();
    TraversalRules[] traversalRules = searchScope.getTraversalRules();
    Long collectionShepardId = searchScope.getCollectionId();
    if (collectionShepardId == null) {
      throw new InvalidBodyException("Collection is necessary");
    }
    // no DataObjectId given
    if (searchScope.getDataObjectId() == null) {
      ret.addAll(structuredDataReferenceDAO.findReachableReferencesByShepardId(collectionShepardId, userName));
    }
    // DataObjectId given
    else {
      long startShepardId = searchScope.getDataObjectId();
      // consider only start node
      if (traversalRules.length == 0) {
        List<StructuredDataReference> reachableReferences =
          structuredDataReferenceDAO.findReachableReferencesByShepardId(collectionShepardId, startShepardId, userName);
        ret.addAll(reachableReferences);
      }
      // search according to traversal rules
      else {
        for (TraversalRules traversalRule : traversalRules) {
          List<StructuredDataReference> reachableReferences =
            structuredDataReferenceDAO.findReachableReferencesByShepardId(
              traversalRule,
              collectionShepardId,
              startShepardId,
              userName
            );
          ret.addAll(reachableReferences);
        }
      }
    }
    return ret;
  }

  private static String makeMongoQueryId(String mongoId) {
    return "{$oid: '" + mongoId + "'}";
  }

  private static String makeMongoQueryArray(List<String> strings) {
    return "[" + String.join(", ", strings) + "]";
  }
}