GAE / J中的数据库设计:关系建模与实体属性值

时间:2010-12-19 19:27:56

标签: google-app-engine google-cloud-datastore data-modeling

想象一下,您计划创建一个社交网络在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]

您认为每个选项有哪些优缺点?我主要担心的是:

  1. 对多标准搜索有何影响(例如“给我年龄低于45岁的用户住在纽约并喜欢书籍”
  2. 它可能具有哪些GAE / J含义? (即索引,数据存储区大小......)
  3. 如果要检索“喜欢图书的用户”,如何为具有多个值的属性(例如“兴趣”)建模?
  4. 我认为第二种选择更灵活,也可能更容易实现,但我想知道其他有经验的开发人员的想法。

    谢谢。

2 个答案:

答案 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岁的图书的实体,但不会产生任何结果,因为没有行会有av这两个值:

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的灵活性(例如没有连接)。工作解决方案可能是多个查询,并手动组合和解析其结果。

OTOH与“经典方法”它是微不足道的:

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());
}

这将打印出汤姆的数据。