想象一下,您计划创建一个社交网络在GAE / Java上运行,其中每个用户都有一组属性(即年龄,当前城镇,兴趣)。
备选方案1 :经典方法 - 将user_id和每个属性作为“行”
entity property_1 property_2 property_3
------ ---------- ---------- -----------------
bob missing NY [football, books]
tom 34 missing [books, horses]
备选方案2 :entity-atributte-value(EAV)
entity attribute value
------ --------- -----
bob town NY
bob interests [football, books]
tom age 34
tom interests [books, horses]
您认为每个选项有哪些优缺点?我主要担心的是:
我认为第二种选择更灵活,也可能更容易实现,但我想知道其他有经验的开发人员的想法。
谢谢。
答案 0 :(得分:2)
您是否看过Google I / O 2009的Building Scalable, Complex Apps on App Engine?该视频的音质非常糟糕,但它涵盖了您的主题。他谈到了列表属性和合并连接及其局限性。
答案 1 :(得分:1)
如果EAV的灵活性对您的应用程序至关重要,那么请使用它,否则不要因为它在查询时会有陷阱。
将返回所有拥有感兴趣书籍的实体:
final Iterator<EAV> eavs = Iterators.transform(
datastoreService.prepare(
new Query(EAV.class.getSimpleName()).addFilter("a",
FilterOperator.EQUAL, "interests").addFilter(
"v", FilterOperator.EQUAL, "books"))
.asIterator(), new Function<Entity, EAV>() {
@Override
public EAV apply(final Entity input) {
return new EAV(input);
}
});
while (eavs.hasNext()) {
logger.debug("eav: " + eavs.next());
}
尝试获取有兴趣且年龄低于45岁的图书的实体,但不会产生任何结果,因为没有行会有a
和v
这两个值:
final Iterator<EAV> eavs = Iterators.transform(
datastoreService.prepare(
new Query(EAV.class.getSimpleName()).addFilter("a",
FilterOperator.EQUAL, "interests").addFilter(
"v", FilterOperator.EQUAL, "books").addFilter("a",
FilterOperator.EQUAL, "age").addFilter(
"v", FilterOperator.LESS_THAN, 45))
.asIterator(), new Function<Entity, EAV>() {
@Override
public EAV apply(final Entity input) {
return new EAV(input);
}
});
while (eavs.hasNext()) {
logger.debug("eav: " + eavs.next());
}
结果并不奇怪,因为大表中的查询甚至不接近SQL的灵活性(例如没有连接)。工作解决方案可能是多个查询,并手动组合和解析其结果。
final Iterator<Person> persons = Iterators.transform(
datastoreService
.prepare(
new Query(Person.class.getSimpleName())
.addFilter("interests",
FilterOperator.EQUAL, "books")
.addFilter("age",
FilterOperator.NOT_EQUAL, null)
.addFilter("age",
FilterOperator.LESS_THAN, 45))
.asIterator(), new Function<Entity, Person>() {
@Override
public Person apply(final Entity input) {
return new Person(input);
}
});
while (persons.hasNext()) {
logger.debug("person: " + persons.next());
}
这将打印出汤姆的数据。