【问题标题】:Three-Way Relationship in FirebaseFirebase 中的三向关系
【发布时间】:2017-09-16 16:38:48
【问题描述】:

在过去的几个月里,我学到了很多关于非规范化数据的知识,但我想知道在扁平化架构世界中是否可以实现以下操作。我知道如何在 Firebase 中处理双向关系,但是三向关系呢?让我解释一下……

我的数据库中有 5 个项目,servicesprovidersserviceAtProviderreviewsusers。我希望能够将providers 添加到services,反之亦然。

我还希望在service 内有一个provider 的特定页面,并且有链接到该特定服务的提供商的评论。页面 url 可能如下所示 (site.com/serviceId/providerId)。评级是 providerIdserviceId 内的唯一评级 - 您不能单独评级 serviceIds 或 providerIds。

我不确定如何建立如此复杂的关系。我如何在 serviceAtProvider 项目中加入 serviceIdproviderId

这是我目前得到的:

 "services": {
   "service1": {
     "name": "Hernia Repair",
     "providers": {
       "provider1": true,
       "provider2": true
     }
   }
 },
 "providers": {
   "provider1": { "name": "The Whittington Hospital" },
   "provider2": { "name": "Homerton Hospital" }
 },
 "serviceAtProvider": {
   "service1AtProvider1": { "rating": 4 },
   "service1AtProvider2": { "rating": 3 }
 },
 "reviews": {
   "service1AtProvider1": {
     "review1": {
       "body": "A review from user 1",
       "user": "user1"
     }
   },
   "service1AtProvider2": {
     "review1": {
       "body": "A review from user 2",
       "user": "user2"
     }
   }
 },
 "users": {
   "user1": { "name": "Ben Thomas" },
   "user2": { "name": "Beatrix Potter" }
 }

我不知道如何创建 serviceAtProviderjoin,或者如何访问一页上的 service1.nameprovider1.nameservice1AtProvider1.ratingreviews.service1AtProvider1。谁能解释一下如何做到这一点?

另外,有什么我应该遵循的最佳做法吗?

感谢任何帮助。提前致谢!

更新

{
  "availableServices": {
    "service1": { "name": "Hernia Repair" },
    "service2": { "name": "Chemotherapy" }
  },

  "services": {
    "provider": {
      "name": "The Whittington Hospital",
      "service": {
        "service1": {
          "rating": 4,
          "reviewId1": true
        },
        "service2": {
          "rating": 3,
          "reviewId2": true
        },
      }
    }
  },

  "reviews": {
    "reviewId1": {
      "review1": {
        "rating": 4,
        "body": "A review from user 1",
        "user": "user1"
      }
    }
  },

  "users": {
    "user1": { "name": "Raphael Essoo-Snowdon" },
    "user2": { "name": "Sharlyne Slassi" }
  }
}

【问题讨论】:

  • 这既是一个信息架构问题,也是一个关于如何编写查询代码的问题。从这些数据的含义和关系是什么开始会有很大帮助。例如,乍一看,似乎将其构造为/services/$provider/$service/ 将消除对/services/$service/providersserviceAtProvider/ 的需求,从而大大简化了事情。如果有一个标准化的服务列表,这些可以存储在像 available_services/ 这样的地方,安全规则可以确保 $service 匹配一个以保持一致性。
  • @Kato 我已经更新了上面的代码。你是说这样的话会是一个可行的解决方案,因此删掉serviceAtProvider/???
  • 同样,如果没有您的用例和约束,很难推荐一种格式。很大程度上取决于如何使用和读取数据。这是一个我们可以花几个小时讨论的大问题。以问答形式回答这个问题的最佳方法是将问题集简化为要解决的简单问题,这样可以抓住问题的本质,并帮助您将该策略应用于更广泛的范围。
  • 我基本上有两个对象,providersservices,它们都有很多孩子。可以链接单个provider(来自providers 对象)和service(来自services 对象),并且当链接时,这会在serviceAtProvider 中创建一个唯一项。评级可以应用于这个独特的项目。那有意义吗?我会尝试以更简洁的方式提出另一个问题。

标签: json firebase firebase-realtime-database database nosql


【解决方案1】:

我会首先让数据结构更简单、更直接。如果没有详细的用例,很难确定满足您需求的正确数据结构。我会尽我所能在这里做一些通用的假设。您必须根据需要进行调整。

{
  "service": {
    "service1": { "name": "Foo Service" }, 
    ...
  },

  "provider": {
     "provider1": { name: "Foo Place" }, 
     ...
  },

  "ratings": {
    "service1": { // service id
      "provider1": {  // provider id
        "average_rating": 4
      },
      ...
    },
    ...
  },

  "reviews": {
    "service1": { // service id
      "provider1": {  // provider id
        "user": "user1",
        "rating": 4
      },
      ...
    },
    ...
  },

  "user": {
    "user1": { "name": "Foo Bar" },
    ...
  }
}

现在,要查找提供给定服务的提供商并获取他们的评论,我会执行以下操作:

var ref = new Firebase(...);
ref.child('ratings/service1').on('child_added', function(reviewSnap) {
   console.log(
      'Provider ' + reviewSnap.key(),
      'Average rating ' + reviewSnap.val().average_rating
   );
});

可以通过多种方式加入服务和提供商的名称。这是一种手动技术:

var ref = new Firebase(...);
ref.child('ratings/service1').on('child_added', accumulateReview);

function accumulateReview(reviewSnap) {
  var reviewData = reviewSnap.val();
  var reviewid = reviewSnap.key();
  fetchService(reviewSnap.parent().key(), function(serviceSnap) {
    loadRec('provider', reviewSnap.key(), function(providerSnap) {
        console.log('Provider: ', providerSnap.key(), providerSnap.val().name);
        console.log('Service: ', serviceSnap.key(), serviceSnap.val().name);
        console.log('Average rating: ', reviewData.average_rating);
    });
  });
}

var serviceCache = {};
function fetchService(serviceid, done) {
  // demonstrates creating a local cache for things that will be
  // looked up frequently 
  if( !serviceCache.hasOwnProperty(serviceid) ) {
    cacheService(serviceid, done);
  }
  else {
    done(serviceCache[serviceid]);
  }
}

function cacheService(serviceid, done) {
  loadRec('service', function(ss) {
    serviceCache[serviceid] = ss;
    fetchService(serviceid, done);
  });
}

function loadRec(type, key, done) {
  ref.child(type).child(key).once('value', done);
}

我还可以使用 Firebase.util 的 NormalizedCollection 自动执行其中的一些过程:

var ref = new Firebase(...);
var nc = Firebase.util.NormalizedCollection(
   [ref.child('reviews/service1'), 'review'],
   ref.child('provider'),
   ref.child('user')
)
.select('review.rating', {key: 'provider.name', alias: 'providerName'}, {key: 'user.name', alias: 'userName'})
.ref();

nc.on('child_added', function(snap) {
  var data = snap.val();
  console.log('Provider', data.providerName);
  console.log('User', data.userName);
  console.log('Rating', data.rating);
});

请注意,这里没有什么是一成不变的。这就是我将如何处理它。可能有几十个结构至少一样好或更好。

【讨论】:

    猜你喜欢
    • 2017-11-06
    • 2015-08-03
    • 2018-02-25
    • 1970-01-01
    • 1970-01-01
    • 2015-08-19
    • 1970-01-01
    • 1970-01-01
    • 2013-05-27
    相关资源
    最近更新 更多