MongoTemplate: How do I query fields in a specified nested object?

115 Views Asked by At

Spring Boot development involved querying only nested objects in a specified domain, but the methods in the Query/Criteria class that are now used can only fetch the entire document/line:

{
  "_id": {
    "$oid": "<Some Object Id>"
  },
  "identifier": "string1",
  "createTime": {
    "$date": "2024-02-23T02:34:50.225Z"
  },
  "properties": [
    {
      "age": "28"
    },
    {
      "age": "18"
    }
  ]
}

I want to locate the document with the identifier field and retrieve the specified content with property.age ==28

{
  "_id": {
    "$oid": "<Some Object Id>"
  },
  "properties": [
    {
      "age": "28"
    }
  ]
}

I tried

Criteria criteria = Criteria.where("identifier").is("string1").and("properties.xxx").is("value");
Query query = Query.query(criteria);
List<Product> objects = mongoTemplate.find(query, Product.class);
3

There are 3 best solutions below

4
wangyueda On

If you're using Spring Data MongoDB to query nested objects within a document, you can utilize the aggregation framework to perform more complex queries. Here's a general approach:

1.Use Aggregation Framework: MongoDB's Aggregation Framework allows you to perform complex queries and manipulations on data.

2.Match Stage: Use the $match stage to filter documents based on certain criteria.

3.Unwind Stage (if needed): If you have arrays within your document, you might need to $unwind them to query nested objects effectively.

4.Project Stage: Use the $project stage to reshape documents or include/exclude fields.

5.Lookup Stage (if needed): Use the $lookup stage for performing a left outer join to retrieve information from other collections.

6.Group Stage (if needed): Use the $group stage to group documents by some criteria.

Here's an example of how you might structure your query:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.stereotype.Service;

@Service
public class MyService {

    @Autowired
    private MongoTemplate mongoTemplate;

    public void queryNestedObjects() {
        Aggregation aggregation = Aggregation.newAggregation(
            Aggregation.match(Criteria.where("nestedObject.field").is("value")),
            Aggregation.unwind("nestedArray"),
            Aggregation.match(Criteria.where("nestedArray.field").is("value")),
            Aggregation.project("nestedObject"),
            Aggregation.limit(10) // optional: limit the number of results
        );

        AggregationResults<MyDomain> results = mongoTemplate.aggregate(aggregation, "collectionName", MyDomain.class);
        List<MyDomain> resultList = results.getMappedResults();
        // Process resultList as needed
    }
}

In this example:

MyDomain represents your domain class. nestedObject and nestedArray represent the nested objects or arrays you want to query within your document. Criteria class is used to create the query criteria. You can adjust the stages and criteria according to your specific requirements. Remember to replace "collectionName" with the actual name of your MongoDB collection.

1
wangyueda On

this is demo:

import com.mongodb.client.*;
import org.bson.Document;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class MongoDBExample {

    public static void main(String[] args) {
        MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017");
        MongoDatabase database = mongoClient.getDatabase("test");
        MongoCollection<Document> collection = database.getCollection("object_model");

        saveOne(collection);

        selectList(collection);

    }

    private static void saveOne(MongoCollection<Document> collection){
        List<Document> ageList1 = new ArrayList<>();
        ageList1.add(new Document("age","18"));
        ageList1.add(new Document("age","28"));
        Document document1 = new Document("_id", new Document("oid","1"))
                .append("identifier", "string1")
                .append("createTime", new Document("date", new Date()))
                .append("properties", ageList1);
        collection.insertOne(document1);

        List<Document> ageList2 = new ArrayList<>();
        ageList2.add(new Document("age","18"));
        Document document2 = new Document("_id", new Document("oid","2"))
                .append("identifier", "string1")
                .append("createTime", new Document("date", new Date()))
                .append("properties", ageList2);
        collection.insertOne(document2);

        List<Document> ageList3 = new ArrayList<>();
        ageList3.add(new Document("age","28"));
        Document document3 = new Document("_id", new Document("oid","3"))
                .append("identifier", "string1")
                .append("createTime", new Document("date", new Date()))
                .append("properties", ageList3);
        collection.insertOne(document3);
    }

    private static void selectList(MongoCollection<Document> collection){
        Document query = new Document("identifier","string1").append("properties.age", "18");
        FindIterable<Document> result = collection.find(query);

        for (Document document : result) {
            System.out.println(document);
        }
    }
}

This is the output:

Document{{_id=Document{{oid=1}}, identifier=string1, createTime=Document{{date=Tue Feb 27 13:18:47 CST 2024}}, properties=[Document{{age=18}}, Document{{age=28}}]}}
Document{{_id=Document{{oid=2}}, identifier=string1, createTime=Document{{date=Tue Feb 27 13:18:48 CST 2024}}, properties=[Document{{age=18}}]}}
0
5Hang On

The problem is to simply get the field value as the specified second-level field in the nested object/array.
This problem has been solved, the following is the solution process,

  1. Use the shell page in mongodb compass to debug the $unwind,$match,$project expression in the mongodb aggregation function based on the query conditions
    db.col.aggregate([ {$unwind:"$properties"},{$match:{ "properties.age":{$eq:"28"} }},{$project:{age:"$properties.age",_id:0}} ])
  2. Replace the database statement with some of the methods that spring provides for the Aggregation class
Aggregation.match(Criteria.where("identifier").is("info1")),
Aggregation.unwind("properties"),
Aggregation.match(Criteria.where("properties.age").is("28")),
Aggregation.project("age:$properties.age")
);