Firebase DB架构:基于两个条件,收藏夹和位置进行过滤

时间:2017-06-07 11:55:39

标签: ios firebase firebase-realtime-database

在我的iOS应用中,我使用的是Firebase实时数据库,我有以下型号:用户和场地。

所以用户可以喜欢一个场地。我想要做的就是能够看到我所关注的所有场地,我追随的人都受到了青睐。我的主要问题是我可以生成一个类似于FriendlyPix的“favitedFeed”,但我真的需要考虑距离场地的距离并排除太远的距离。

在我关注的所有场所中建立一个'favoritedFeed',然后在客户端按距离过滤掉这个Feed会不会有好处?另外,如果可能的话,我想对feed进行分页,但是如果我采用这种方法,那可能是不可能的。

我将绘制我的JSON树,也许应该重新构建。

users
   [user_id] 
      …
      comments
         [comment_id]  = true
      favorites
         [venue_id] = true
comments
   [comment_id]
      user = [user_id]
      comment = “…..”
venues
  [venue_id]
      ...
      favorites
         [user_id] = true

现在,我只是迭代用户关注的所有用户,然后获取他们的评论。这种方式不能很好地扩展,但它是我能够弄清楚如何获得评论的唯一方法。

2 个答案:

答案 0 :(得分:3)

因此您可以使用一些不同的声音方法。根据您的使用情况,它们中的任何一个都可能是最优化的解决方案。

在场地中存储收藏夹并在那里查询

优点:实施起来非常简单,大批量生效。

缺点:如果你在成千上万的场馆中拥有成千上万的收藏夹并且每个客户端对这个列表执行大量查询,那么可能会变慢。这可能是一个边缘情况,你可以从这里开始,随着你的应用程序的增长轻松适应其他模型。

这种方法将使用与此类似的数据结构:

venues/$venueid/favoritedby/$userid/<true>
venues/$venueid/meta/<detailed data about the venue could be stored here>
users/$userid/myfriends/$userid/<any data or just a true value here>

现在提取一个场地列表需要我做两件事:查找我的朋友收藏的场地,并根据接近度过滤那些场地。我将在下面单独介绍附近区域,并重点介绍如何在此处获取朋友的最爱。

const ref = firebase.database().ref();
getMyFriends("kato")
  .then(getFavoritesOfMyFriends)
  .then(favorites => console.log(favorites));

function getMyFriends(myUserId) {
  // find everyone I've marked as a friend
  return ref.child(`users/${myUserId}/myfriends`)
    .once('value').then(snap => {
      // return the uids of my friends as an array
      return Object.keys( snap.val() || {} );
    });
}

function getFavoritesOfFriends(myListOfFriends) {
  const promises = [];
  // fetch favorites for each of my friends in parallel
  // and merge them into one object, data will be deduped
  // implicitly since we are storing in a hash
  let mergedFavorites = {};
  myListOfFriends.forEach(uid => {
    promises.push( 
      getFavorites(uid)
        .then(favs => mergeInto(mergedFavorites, favs))
    );
  });
  return Promise.all(promises).then(() => mergedFavorites);
}

function getFavorites(uid) {
  // fetch directly from venues
  return ref.child('venues')
    // but only get items favorited by this uid
    .orderByChild(`favoritedby/${uid}`).equalTo(true)
    .once('value')
    // extract the data from snapshot before returning
    .then(snap => snap.val() || {});
}

function mergeInto(dest, newData) {
  Object.keys(newData).forEach(k => dest[k] = newData[k]);
  return dest;
}

使用扁平化方法

优点:高度灵活,可与数百万收藏家合作,不会造成真正的性能损失

缺点:实施起来有点复杂 - 相当多的箍跳来抓取所有数据

这种方法将使用与此类似的数据结构:

venues/$venueid/<detailed data about the venue is stored here>
users/$userid/myfriends/$userid/<any data or just a true value here>
favorites/$userid/$venueid/<any data or just a true value here>

现在我可以抓住我的朋友列表,并找到所有他们喜欢的场地ID,如下所示。然后我可以查看各个场所以获取姓名等,或根据我的位置过滤。

const ref = firebase.database().ref();

getMyFriends("kato").then(getFavoritesOfMyFriends).then(favorites => console.log(favorites));

function getMyFriends(myUserId) {
  // find everyone I've marked as a friend
  return ref.child(`users/${myUserId}/myfriends`).once('value').then(snap => {
    // return the uids of my friends as an array
    return Object.keys( snap.val() || {} );
  });
}

function getFavoritesOfFriends(myListOfFriends) {
  const promises = [];
  // fetch favorites for each of my friends in parallel
  // and merge them into one object, data will be deduped
  // implicitly since we are storing in a hash
  let mergedVenues = {};
  myListOfFriends.forEach(uid => {
    promises.push(
      getFavorites(uid)
        .then(getVenuesByKey)
        .then(venues => mergeInto(mergedVenues, venues))
    );
  });
  return Promise.all(promises).then(() => mergedVenues);
}

function getFavorites(uid) {
  return ref.child(`favorites/${uid}`).once('value').then(snap => {
    return Object.keys(snap.val()||{});
  });
}

function getVenuesByKey(listOfKeys) {
  const promises = [];
  const venues = {};
  listOfKeys.forEach(venueId => {
    promises.push(
      getVenue(venueId)
        // add each entry to venues asynchronously
        .then(v => venues[venueId] = v)
    );
  });
  // Wait for all items to load then return the compiled list
  return Promise.all(promises).then(() => venues);
}

function getVenue(venueId) {
  return ref.child(`venues/${venueId}`).then(snap => snap.val() || {});
}

function mergeInto(dest, newData) {
  Object.keys(newData).forEach(k => dest[k] = newData[k]);
  return dest;
}

适用于大规模

对于其他人来说,主要是为了后人:请注意,如果您正在运行类似Twitter的Feed,其中名人可能拥有数百万粉丝或类似场景,并且您真的需要在此扩展,那么您可能会集成函数和使用它们类似于存储过程,实际上将关于最喜欢的场所的重复信息写入每个跟随者的“饲料”以提高规模。但同样,大多数应用程序都不需要这样,所以你可以保持简单。

这是迄今为止最复杂和可扩展的 - 当您开始编辑和更改场地数据并且有很多边缘情况时,它会变得毛茸茸。强烈建议在尝试这样的事情之前与NoSQL data structures建立亲密关系。

按地理位置过滤

我会玩这些以了解如何在本地最佳地过滤数据,但我怀疑你最终会得到这样的结果:

  • 抓住靠近我的场地名单(见GeoFire)
  • 抓住我朋友的最爱
  • 执行venuesCloseToMe.some(key => return favoritesOfMyFriends.indexOf(key) > -1);
  • 之类的操作
  • 现在按键取出这些场地,以便在客户端上显示

答案 1 :(得分:1)

您可以为您所关注的用户所喜爱的事件的Geofire位置维护另一个根节点。因此,只要您关注的人喜欢某个活动,使用Geofire,您必须将该地点的位置写为user-feed-location/$userId以获取密钥$venueId

我建议使用Cloud Functions为onWrite创建user-favourites/$userId/$venueId侦听器。当用户使用Geofire更新user-feed-location/$followerId以及地点的位置和ID时,为其每个关注者搜索场地。

"user-favourites": {
    "$userId": {
        "$venueId": { ... }
    }
},
"user-feed-location": {
    "$userId": {
        "$venueId": {
            "g": "xxxxxxxxxx",
            "l": { ... }
        }
    }
},
"user-followers": {
    "$userId": {
        "$followerId": { ... }
    }
},
"user-followees": {
    "$userId": {
        "$followeeId": { ... }
    }
},
"users": {
    "$userId": { ... }
},
"venue-location": {
    "$venueId": {
        "g": "xxxxxxxxxx",
        "l": { ... }
    }
},
"venues": {
    "$venueId": { ... }
}

通过拥有相应的Geofire监听器,在查询半径内向特定用户的馈送添加场地将触发.keyEntered事件。响应块为您提供场地的ID和位置,您可以通过它获取场地信息并确定用户与区块中返回的位置之间的距离。

相关问题