【问题标题】:Database design in GAE/J : relational modelling vs entity-attribute-valueGAE/J 中的数据库设计:关系建模与实体-属性-值
【发布时间】:2011-05-27 22:44:51
【问题描述】:

假设您计划创建一个在 GAE/Java 上运行的社交网络,其中每个用户都有一组属性(即年龄、当前城镇、兴趣)。

替代方案 1:经典方法 - user_id 和每个属性作为“行”

entity  property_1 property_2 property_3
------  ---------- ---------- -----------------
bob     missing    NY         [football, books]
tom     34         missing    [books, horses]

备选方案 2:实体属性值 (EAV)

entity   attribute   value
------   ---------   -----
bob      town        NY
bob      interests   [football, books]
tom      age         34
tom      interests   [books, horses]

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

  1. 对多标准搜索有何影响(即“给我 45 岁以下居住在纽约并喜欢书籍的用户”
  2. 它可能对 GAE/J 产生什么影响? (即索引、数据存储大小...)
  3. 如果要检索“喜欢书籍的用户”,如何为具有多个值(例如“兴趣”)的属性建模?

我认为第二种选择更灵活,可能更容易实现,但我想知道其他有经验的开发人员是怎么想的。

谢谢。

【问题讨论】:

  • Expando 是要走的路,但你必须深入了解如何在 java 中做到这一点。
  • 感谢@Gabi,但问题并不在于某些属性不是强制性的。问题在于在 1. 多标准搜索 2. GAE/J 影响 3. 具有多个值的属性方面哪个选项更好。

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


【解决方案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());
}

这将打印出汤姆的数据。

【讨论】:

    【解决方案2】:

    您看过 Google I/O 2009 上的 Building Scalable, Complex Apps on App Engine 吗?该视频的音质很差,但涵盖了您的主题。他谈到了列表属性和合并连接及其局限性。

    【讨论】:

    • 如果每次演讲都有相应的纯文本文章就好了,因为像我这样的外国人更容易理解。
    • 嗯,该页面上有一个包含幻灯片的 PDF,YouTube 视频上有字幕。您甚至可以使用 Google 翻译为您翻译幻灯片。
    • 感谢 KRASH,有很多关于 Google 数据存储如何工作的非常有趣的视频。您提供的链接解决了我提出的 3 个问题。就我所见,我的替代方案 1 似乎是最好的选择。
    猜你喜欢
    • 1970-01-01
    • 2014-11-30
    • 2010-10-26
    • 1970-01-01
    • 2011-02-01
    • 2021-07-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多