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

发布于 2024-10-08 12:38:56 字数 847 浏览 0 评论 0原文

想象一下,您计划创建一个在 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. 如果要检索“喜欢书籍的用户”,如何使用多个值(例如“兴趣”)对属性进行建模?

我认为第二种选择更灵活,也许更容易实现,但我想知道其他有经验的开发人员的想法。

谢谢。

Imagine you plan to create a social network running on GAE/Java where each user has a set of properties (i.e. age, current town, interests).

Alternative 1: classical approach - the user_id and every property as a "row"

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

Alternative 2: entity-atributte-value (EAV)

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

What pros/cons do you think each option has? My main concerns are:

  1. What is the impact on multi-criteria searches (i.e. "give me the users with ages under 45 that live in NY and like books")
  2. What GAE/J implications could it have? (i.e. indexes, datastore size...)
  3. How to model attributes with multiple values ("interests" for example) if you want to retrieve "users that like books" ?

I think the second alternative is more flexible and maybe easier to implement, but I would like to know what other experienced developers think.

Thank you.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

白色秋天 2024-10-15 12:38:56

您是否看过在 App Engine 上构建可扩展的复杂应用程序?该视频的音质很差,但涵盖了您的主题。他谈论了列表属性和合并连接及其局限性。

Did you have a look at Building Scalable, Complex Apps on App Engine from Google I/O 2009? The video has terrible sound-quality, but it covers your topics. He talks about list properties and merge-joins and their limitations.

故笙诉离歌 2024-10-15 12:38:56

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

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

这将打印出汤姆的数据。

If the flexibility of EAV is essential for your app then use it, otherwise do not since it'll have pitfalls in querying.

Will return all entities that have books in interests:

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

Trying to fetch entities that have books in interests and aged under 45, but won't result anything since no row will have the two values of a and 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());
}

The result isn't surprising, as the querying in big table is not even close to the flexibility of SQL (no joins for example). The working solution would probably be multiple queries and manually combining and parsing their results.

OTOH with "classical approach" it's trivial:

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

This will print out tom's data.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文