Skip to main content

Extend MongoDB Repositories for advanced filtering

đŸŽ¯context

You have an already existing Java Domain Service and you want to retrieve root entities by complex filter criterias. To achieve this, the generated Repository class has to be extended.

Description​

When working in a Java Domain Service, it is by default only possible to filter by id or an example object. The latter works fine when filtering for direct propoerties or when using simple references. But it does not work e.g. when trying to filter for something in an array. To solve this issue there is a workaround to extend the generated Repository class with a deviated class in order to use additional find methods provided by the mongo db.

Steps​

Consider you created a party service in a CRM system and the task is to provide the functionality of searching a party for multiple criteria. Some criteria might be simple, but there are also some complex criteria. Then at first you have to create a new file called e.g. CustomPartyRepo.java. Then proceed with the following steps:

  1. Import dependencies: Insert the necessary dependencies before the class definition.
package de.knowis.partydir.partydir.domain.pty.repository;

import static org.mockito.ArgumentMatchers.anyString;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Primary;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
import org.springframework.stereotype.Service;

import de.knowis.partydir.partydir.sdk.domain.pty.entity.FindPartiesInput;
import de.knowis.partydir.partydir.sdk.domain.pty.entity.Party;
import de.knowis.partydir.partydir.sdk.domain.pty.repository.PartyRepository;
import de.knowis.partydir.partydir.sdk.domain.pty.type.PartyStatusValue;
import de.knowis.partydir.partydir.sdk.domain.pty.type.PartyType;
  1. Define the properties of the class: Add annotations, the inheritance from class PartyRepository, the class attributes and the constructor.
@Service
@Primary
public class CustomPartyRepo extends PartyRepository {
private static final Logger log = LoggerFactory.getLogger(CustomPartyRepo.class);

public CustomPartyRepo(MongoEntityInformation<Party, String> metadata, MongoOperations mongoOperations) {
super(metadata, mongoOperations);
}
}
  1. Define methods: Add methods to the class that utilize suitable find-methods of the MongoDB class.

    1. Example for finding instances due to criterias.
      /**
    * Finds all parties based on the given input criteria.
    *
    * @param findPartiesInput the input criteria for finding parties
    * @return a list of parties that match the given criteria
    */
    public List<Party> findAllParties(FindPartiesInput findPartiesInput) {
    Query query = new Query();
    List<Criteria> criterias = new ArrayList<>();

    // A string property is checked
    // Returns true if the values are equal (because of "is(...)")
    if(findPartiesInput.getTaxIdentifikationNumber() != null) {
    criterias.add(Criteria.where("identification.taxIdentifikationNumber").is(findPartiesInput.getTaxIdentifikationNumber()));
    }

    // An enum property is checked, so the value inside is(...) has to be converted to a string
    // Returns true if the values are equal
    if(findPartiesInput.getIncludeDeletedParties() == null || !findPartiesInput.getIncludeDeletedParties()) {
    criterias.add(Criteria.where("partyStatus.partyStatusValue").is(PartyStatusValue.ACTIVE.toString()));
    }

    // A class (or document type) is checked
    // You have to check in your database which values the property "_class" has for the corresponding classes
    if(findPartiesInput.getPartyType() != null) {
    if (findPartiesInput.getPartyType().equals(PartyType.ORGANISATION)) {
    // Returns true if the database instance is of class "Organsiation"
    criterias.add(Criteria.where("_class").is("pty:Organisation"));
    }
    else if(findPartiesInput.getPartyType().equals(PartyType.PERSON)) {
    // Returns true if the database instance is of class "Person"
    criterias.add(Criteria.where("_class").is("pty:Person"));
    }
    }

    // A date property is checked
    // Returns true if the values are equal
    if(findPartiesInput.getBirthDate() != null) {
    criterias.add(Criteria.where("birthDate").is(findPartiesInput.getBirthDate()));
    }

    // A string property is checked, but not for (exact) equality
    // If it is not searched for the exact string, a regex-pattern must be used
    // Returns true if the input value is contained in the property of the database instance (because of "regex(...)")
    if(findPartiesInput.getLastName() != null) {
    String inputString = findPartiesInput.getLastName();
    Pattern containPattern = Pattern.compile(".*"+Pattern.quote(inputString)+".*", Pattern.CASE_INSENSITIVE);
    criterias.add(Criteria.where("identification.partyName.lastName").regex(containPattern));
    }

    // A string property is checked
    // Returns true if the values are equal
    if(findPartiesInput.getGivenName() != null) {
    criterias.add(Criteria.where("identification.partyName.givenName").is(findPartiesInput.getGivenName()));
    }



    // A string property is checked
    // Returns true if the values are equal
    if(findPartiesInput.getLegalName() != null) {
    criterias.add(Criteria.where("identification.partyName.legalName").is(findPartiesInput.getLegalName()));
    }

    // A date property is checked
    // Returns true if the values are equal
    if(findPartiesInput.getEstablishmentDate() != null) {
    criterias.add(Criteria.where("establishmentDate").is(findPartiesInput.getEstablishmentDate()));
    }

    // Check if any criteria was added
    if(!criterias.isEmpty()) {
    // Create AND criteria that matches the given input
    Criteria andCriteria = new Criteria().andOperator(criterias);

    // Log the criteria object for debugging purposes
    String jsonCriteriaObject = andCriteria.getCriteriaObject().toJson();
    log.debug("Criteria-Object: {}", jsonCriteriaObject);

    return mongoOperations.find(query.addCriteria(andCriteria), entityInformation.getJavaType(), entityInformation.getCollectionName());
    }

    log.debug("No criterias added");
    return mongoOperations.findAll(entityInformation.getJavaType(), entityInformation.getCollectionName());
    }

    1. Example for checking duplicates.

    /**
    * Checks if there is a duplicate party based on the tax identification number and party ID.
    *
    * @param taxIdentificationNumber The tax identification number of the party.
    * @param partyId The ID of the party.
    * @return True if a duplicate party exists, false otherwise.
    */
    public boolean isDuplicateParty(String taxIdentificationNumber, String partyId) {
    Query query = new Query();
    List<Criteria> criterias = new ArrayList<>();

    // If taxIdentificationNumber exists, add it
    /**
    * Checks if there is a duplicate party based on the tax identification number and party ID.
    *
    * @param taxIdentificationNumber The tax identification number of the party.
    * @param partyId The ID of the party.
    * @return True if a duplicate party exists, false otherwise.
    */
    public boolean isDuplicateParty(String taxIdentificationNumber, String partyId) {
    Query query = new Query();
    List<Criteria> criterias = new ArrayList<>();

    if(taxIdentificationNumber == null) {
    return false;
    } else {
    // A string property is checked
    // Returns true of the values are equal
    criterias.add(Criteria.where("identification.taxIdentifikationNumber").is(taxIdentificationNumber));
    }

    // A string property is checked
    // Returns true if the values are not equal (because of "ne(...)")
    if(partyId != null) {
    criterias.add(Criteria.where("partyId").ne(partyId));
    }

    // Create AND criteria that matches the given input
    Criteria andCriteria = new Criteria().andOperator(criterias);

    return mongoOperations.exists(query.addCriteria(andCriteria), entityInformation.getJavaType(), entityInformation.getCollectionName());
    }

â„šī¸note

The phrase Criteria.where("identification.number").is(number) is valid for both when property identification is an array or a single object.

  • If the property is a single object, then it is checked if property number of object identification matches the value inside is(...).
  • If the property is an array, then it is checked if property number of one object of the array identification matches the value inside is(...).
🌟Congratulations!

Now you have successfully learned how to retrieve root entities by complex filter criterias.