简体   繁体   中英

MongoTemplate Query array that match at least the values I'm passing

If I have a Documents defined as

[
  {
    "title": "title",
    "tags": [ "cool", "amazing", "funny" ]
  },
  {
    "title": "another title",
    "tags": [ "nice", "amazing", "funny" ]
  }
]

I'd like to be able to query with MongoTemplate in order to pass a list of values like ["cool","amazing"] and have in return the first collection above, not the second. For what I mean to achieve, the $in condition doesn't seems enough. I tried with the $all condition and from the Mongo console it works as I need, but in my code something isn't working and it takes forever for my query to elaborate. With the $in operator my code goes fast, instead. My method in my repository (for other reasons, I have to do an aggregation as below):

public Page<MyDocument> findByProperties(String title, List<ObjectId> tags, Pageable page) {
        final List<Criteria> criteria = new ArrayList<>();
        Aggregation aggregation = null;

        
        if (title != null && !title.isEmpty()) {
            criteria.add(Criteria.where("title").is(title));
        }
        
        if (tags != null && !tags.isEmpty()) {
            criteria.add(Criteria.where("MyBook.tags").all(tags));
        }

        if (!criteria.isEmpty()) {
            aggregation = Aggregation.newAggregation(
                    Aggregation.lookup("from_collection", "_id", "idParent", "MyBook"),
                    Aggregation.unwind("MyBook"),
                    Aggregation.match(new Criteria().andOperator(criteria.toArray(new Criteria[0]))),
                    Aggregation.skip(page.getOffset()),
                    Aggregation.limit(page.getPageSize()),
                    Aggregation.sort(page.getSort())
            );
        } else {
            aggregation = Aggregation.newAggregation(
                    Aggregation.lookup("from_collection", "_id", "idParent", "MyBook"),
                    Aggregation.unwind("MyBook"),
                    Aggregation.skip(page.getOffset()),
                    Aggregation.limit(page.getPageSize()),
                    Aggregation.sort(page.getSort())
            );
        }

        List<MyDocument>  results  = mongoTemplate.aggregate(aggregation, "my_document", MyDocument.class).getMappedResults();

        return PageableExecutionUtils.getPage(results, page,
                () -> (long)results.size());
    } 

Looking at this answer, I tried with

criteria.add(Criteria.where("MyBook.tags").in(tags).all(tags));

But nothing changed, the query takes forever and not with the expected output. Any ideas please? Thank you!

To find if tags array field contains all the members of youe array, your can use the bellow, if you need it to be in aggregation pipeline use the second.

In your query you have more stages, and more code, if you want to ask more if you can give example data and expected output in JSON, and what you want to do, so people can help.

Query1

  • find using $all operator

Test code here

db.collection.find({"tags": {"$all": ["cool","amazing"]}})

Query2

  • aggregation solution with set difference

Test code here

aggregate(
[{"$match": 
    {"$expr": 
      {"$eq": [{"$setDifference": [["cool", "amazing"], "$tags"]}, []]}}}])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM