您好我在使用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的地址,但查询返回null对象。我错过了什么吗?我应该以不同的方式使用 $ unwind 吗?请建议
答案 0 :(得分:1)
聚合框架返回完全相同的格式 - 在展开之后,唯一的区别是addresses
字段下嵌入了多少个地址。在您的情况下,您将获得两个文件:
{ "_id": 1,
"name" : "Bob",
"age" : 34,
"addresses" : { "houseNumber" : "12",
"road" : "High Street",
"town" : "Small Town",
"postalCode" : "BC2 3DE"
}
}
这不是非常有效地转换为Address类,因为正如您所看到的,Address对象是addresses
字段的值,而不是顶级对象。
不清楚为什么你需要聚合 - 你得到一个人的地址列表。当您通过ID对此人进行简单find()
时,字段“地址”已经是List
个Address
个对象。
在MongoDB find() has the option to return just selected fields中,或者排除命名字段(类似于SQL的SELECT *效率不如SELECT col1,col2),所以如果你the Java equivalent of db.persons.find({filter-condition}, {"_id":0, "addresses":1})
,你会得到一个仅 地址列表的文档。
答案 1 :(得分: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)解决方案,请分享。