2

I have been learning myself MongoDB implementation in Spring Boot. However, I came into a problem with complex queries.

I cannot find any right solution for how to implement complex queries to MongoDB from Spring boot.

I am querying the database with MongoRepository interface implementation.

Let's say that I have three collections:

  • Person - 1 Person can have many Pets.
  • Pet - 1 Pet can have 1 PetToy and 1 Person who owns him.
  • PetToy - 1 PetToy can belong to 1 Pet.

POJO classes are bellow

What do I want to achieve?

I want to make a query, which would be returned me a Person, whose Pet has a Toy (PetToy) with the name "Teddy".

I could not have found a way how to do it. Furthermore, is it the best practice to even use such complex queries, or is it better to write more of little ones in MongoDB?

POJOs:

@Document
@Data
@ToString
public class Person {

    @Id
    private String id;

    private String firstname;

    private String lastname;

    private int age;

    @DBRef
    private Pet pet;
}

@Document
@Data
@ToString
public class Pet {

    @Id
    private String id;

    private String name;

    private int age;

    @DBRef
    private List<PetToy> toys;
}

@Document
@Data
@ToString
public class PetToy {

    @Id
    private String id;

    private String name;
}

I have tried to use MongoRepositories; however, I was not able to make the complex query.

How can one write such a query to a MongoDB from Spring Boot?

Thank you very much in advance.

2
  • Is there any reason for using @DBRef in the relationships between Person->Pet and Pet->Pet Toys instead embedded attributes? Commented May 19, 2019 at 2:19
  • I used the @DBRef, because I wanted to know how to connect two Documents from separate Collections. It was mere example. Commented May 20, 2019 at 17:29

1 Answer 1

3

If you can use embedded attributes, the class model should be:

@Document
@Data
@Builder
public class Person {

    @Id
    private String id;

    private String firstName;

    private String lastName;

    private int age;

    private List<Pet> pets;
}
@Data
@Builder
public class Pet {

    private String name;

    private int age;

    private List<PetToy> toys;
}
@Data
@Builder
public class PetToy {

    private String name;
}

The repository with the method that achieves what you want:

public interface PersonRepository extends MongoRepository<Person, String> {
    List<Person> getByPetsToysName(String name);
}

The getByPetsToysName method basically navigate between Person's attributes Person->pets->toys->name. More info here.

An example

@Configuration
@EnableMongoRepositories
public class TestMongo implements CommandLineRunner {

  private final PersonRepository repository;

  public TestMongo(PersonRepository repository) {
    this.repository = repository;
  }

  @Override
  public void run(String... args) throws Exception {

    repository.save(Person.builder()
      .firstName("John")
      .lastName("Doe")
      .age(20)
      .pets(Stream.of(Pet.builder()
        .name("Ursa")
        .age(1)
        .toys(Stream.of(PetToy.builder()
          .name("Teddy")
          .build())
          .collect(Collectors.toList()))
        .build())
        .collect(Collectors.toList()))
      .build());

    repository.save(Person.builder()
      .firstName("Phillip")
      .lastName("Larson")
      .age(21)
      .pets(Stream.of(Pet.builder()
        .name("Bella")
        .age(5)
        .toys(Stream.of(PetToy.builder()
          .name("Lolo")
          .build())
          .collect(Collectors.toList()))
        .build())
        .collect(Collectors.toList()))
      .build());

    List<Person> persons = repository.getByPetsToysName("Teddy");
    System.out.println(persons.size());
    List<Person> persons1 = repository.getByPetsToysName("Lolo");
    System.out.println(persons1.size());
  }
}

Logs:

find using query: { "pets.toys.name" : "Teddy" } fields: Document{{}} for class: class Person in collection: person

If you want more complex queries you can to take a look at the Spring Data MongoDB docs.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you, I see that it is better to use Embedded Documents, when it is possible. And in the situation, when you have to use Document Reference, you should probably use Aggregation Framework.
just a piece of advice, @Builder should be used when classes have more than 4 properties. it is a good practice

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.