quarkusio / quarkus

Quarkus: Supersonic Subatomic Java.
https://quarkus.io
Apache License 2.0
13.74k stars 2.67k forks source link

quarkus-mongodb-panache: How to query a repository for values stored in a Map ? #38505

Closed nicolasduminil closed 8 months ago

nicolasduminil commented 8 months ago

Describe the bug

This isn't a bug but a question and I'm asking it here 'cause I don't want to open a stackoverflow account. I have a Document as shown below:

@MongoEntity(collection="Products")
public class Product
{
  @BsonId
  private BigInteger id;
  ...
  private Map<String, String> attributes = new HashMap<>();
  ...
}

and I want to query it based on an attribute, i.e. a pair of String instances <key, value>. So, given the repository:

public class ProductRepository implements PanacheMongoRepositoryBase<Product, BigInteger>{}

I want to query it something like this:

  public List<Product> findProductsByAttribute(String key, String value)
  {
    return productRepository.find("?1 in attributes and ?2 in attributes", key, value);
  }

Of course, the in operator works for lists but I don't find anything related to maps. Is there any way that I could do that ?

Many thanks in advance. Kind regards, Nicolas

Expected behavior

Would have hoped to find the equivalent of the in operator working for maps.

Actual behavior

Failed to find such an operator.

How to Reproduce?

No reproducer required as that's just a question.

Output of uname -a or ver

$ uname -a Linux nicolas-XPS-15-9570 5.15.0-92-generic #102-Ubuntu SMP Wed Jan 10 09:33:48 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux

Output of java -version

java -version java version "17.0.6" 2023-01-17 LTS Java(TM) SE Runtime Environment (build 17.0.6+9-LTS-190) Java HotSpot(TM) 64-Bit Server VM (build 17.0.6+9-LTS-190, mixed mode, sharing

Quarkus version or git rev

3.0.4.Final

Build tool (ie. output of mvnw --version or gradlew --version)

mvn -version Apache Maven 3.9.5 (57804ffe001d7215b5e7bcb531cf83df38f93546) Maven home: /opt/apache-maven-3.9.5 Java version: 17.0.6, vendor: Oracle Corporation, runtime: /usr/lib/jvm/jdk-17 Default locale: en_US, platform encoding: UTF-8 OS name: "linux", version: "5.15.0-92-generic", arch: "amd64", family: "unix"

Additional information

None

quarkus-bot[bot] commented 8 months ago

/cc @FroMage (panache), @loicmathieu (mongodb,panache)

nicolasduminil commented 8 months ago

Waiting for a better suggestion, I came to the following implementation of my use case. Given the Document instance:

@MongoEntity(collection="Products")
public class Product
{
  @BsonId
  private BigInteger id;
  ...
  private Map<String, String> attributes = new HashMap<>();
  ...
}

and the repository below:

@ApplicationScoped
public class ProductRepository implements PanacheMongoRepositoryBase<Product, BigInteger> {}

I implemented it as follows:

  public class ProductServiceImpl implements ProductService
  {
    @Inject
    ProductRepository productRepository;
    ...
    @Override
    public List<Product> findProductsByAttribute(String key, String value)
    {
      return productRepository.findAll().stream().filter(p -> p.getAttributes().get(key)
        .equals(value)).collect(Collectors.toList());
    }

    @Override
    public List<Product> findProductsByAttributes(HashMap<String, String> attributes)
    {
      return productRepository.findAll().stream().filter(p -> p.getAttributes().entrySet()
        .containsAll(attributes.entrySet())).collect(Collectors.toList());
    }
    ...
  }

The problem I have here is findAll(), as well as the further transformations, and I'm looking for a way I could avoid these operations, which are probably, not only slow, but also very difficult to implement, given the source code line length. Any suggestion please ?

Many thanks in advance. Kind regards, Nicolas

nicolasduminil commented 8 months ago

Anyone concerned there please ?

loicmathieu commented 8 months ago

I think your map is stored as a MongoDB subdocument so you can do something like that:

productRepository.find(key + " =  ?1", value);

You can you query your MongoDB database from mongos or something like that to confirm that.

loicmathieu commented 8 months ago

As it's not an issue but a question, I'll move it to Github discussion.