0

嗨,我在使用 Jongo 使用$unwind操作员从我的人员集合中获取地址列表时遇到问题。

如您所见,我将 Person 类定义如下:

public class Person {
    @Id
    private long personId;
    private String name;
    private int age;
    private List<Address> addresses;
//getters and setters

Address 类的定义如下:

public class Address {
    private String houseNumber;
    private String road;
    private String town;
    private String postalCode;
//gettes and setters

查询非常简单:

public List<Address> getAddressByPersonId(long id) {
    List<Address> list = persons.aggregate("{$project:{addresses:1}}")
                                    .and("{$match:{_id:#}}",id)
                                    .and("{$unwind: '$addresses'}")
                                    .as(Address.class);
    return list;
}

我的收藏:

> db.persons.find()
{ "name" : "Bob", "age" : 34, "addresses" : [   {       "houseNumber" : "12",
"road" : "High Street",         "town" : "Small Town",  "postalCode" : "BC2 3DE"
 },     {       "houseNumber" : "12",   "road" : "High Street",         "town" :
 "Small Town",  "postalCode" : "BC2 3DE" } ], "_id" : NumberLong(1) }
>

我也对此进行了 JUnit 测试:

    @Before
    public void setUp(){
        service = new PersonServiceImpl();

        //store a person into the database (manually) before the tests
        MongoCollection persons = Database.getInstance().getCollection(CollectionNames.PERSONS);
        //create a new person
        Person p = new Person();
        p.setPersonId(1L);
        p.setName("Bob");
        p.setAge(34);
        //create two addresses for this person
        Address a = new Address();


        a.setHouseNumber("33");
        a.setRoad("Fake Road");
        a.setPostalCode("AB1 2CD");
        a.setTown("Big Town");
        p.addAddress(a);

        a.setHouseNumber("12");
        a.setRoad("High Street");
        a.setPostalCode("BC2 3DE");
        a.setTown("Small Town");
        p.addAddress(a);

        persons.save(p);
    }

    @After
    public void tearDown(){
        service = null;
    }

    @Test
    public void getAddressesByPersonIdTest(){

        List<Address> list = service.getAddressByPersonId(1L);
        for (Address item : list){
            item.print();
        }

        Assert.assertTrue(list.size() > 0);
    }
}

哪个输出

### Address: null null, null, null 
### Address: null null, null, null 

我不太明白问题是什么。显然测试没有失败(所以list.size()大于零......但打印为空)。该print()方法经过测试并且有效。

我想得到两个 Bob 的地址,但是查询返回空对象。我错过了什么吗?我应该以不同的方式使用$unwind吗?请建议

4

2 回答 2

1

聚合框架返回的格式与进入它的格式完全相同——展开后唯一的区别是addresses字段下嵌入了多少地址。在您的情况下,您会收到两个文件,它们是:

{ "_id": 1,
  "name" : "Bob", 
  "age" : 34, 
  "addresses" : { "houseNumber" : "12", 
                  "road" : "High Street",
                  "town" : "Small Town",
                  "postalCode" : "BC2 3DE"
  }
}

这不会非常有效地转换为 Address 类,因为如您所见,Address 对象是addresses字段的值,而不是顶级对象。

不清楚的是为什么您需要汇总 - 您正在获取一个人的地址列表。当您find()通过 Id 对人员进行简单操作时,“地址”字段已经是一个List对象Address

在 MongoDB中,find() 可以选择只返回选定的字段,或者排除命名字段(类似于 SQL 的 SELECT * 不如 SELECT col1,col2 高效),因此如果您执行Java 等效项, db.persons.find({filter-condition}, {"_id":0, "addresses":1})您将返回一个仅包含地址列表。

于 2013-11-11T07:49:44.637 回答
1

我想添加一些额外的信息来解决使用 Jongo 的问题。

我想将 Mongo 结果直接映射到Addresses对象列表,避免不必要的Person对象实例化。想象一下我的Person班级有这些额外的字段:

...
List<Address> addresses; 
List<Purchases> purchases; 
List<Payments> creditcards;
List<Product> watchlist;
//etc
...

但是我们只需要检索'addresses'. 所以第一种方法可能是:

解决方案1)

public List<Address> getAddressByPersonId(long id) {
        List<Address> list = persons.aggregate("{$match: {_id: #}}", id)
                .and("{$project: {addresses: 1, _id: 0}}")
                .and("{$unwind: '$addresses'}")
                .and("{$project: {houseNumber:'$addresses.houseNumber', road:'$addresses.road', town:'$addresses.town', postalCode:'$addresses.postalCode'}}")
                .as(Address.class);
        return list;
    }

优点:我只需要一个列表Addresses,更少的内存分配。缺点:我需要类的$project每个字段,Address否则这将不会映射到 Jongo(null)。正如所@Asya Kamsky指出的,这不是可扩展的解决方案。

解决方案2)

public List<Address> getAddressByPersonId(long id) {
        List<Person> list = persons.aggregate("{$match:{_id:#}}",id).and("{$project: {addresses: 1, _id: 0}}")
                .as(Person.class);

        return list.get(0).getAddresses();
    }

优点:我不需要指定adddress我们想要列表的哪些字段。因此,在我想添加一个额外字段的情况下,它是一个可扩展的解决方案。缺点:我需要分配整个Person对象,这在处理类似场景中的复杂对象和记录负载时会转化为相当大的开销。

如果您有任何其他工作(Jongo)解决方案,请分享。

于 2013-11-18T19:43:07.553 回答