【问题标题】:Getting hierchical data from redis with node.js使用 node.js 从 redis 获取分层数据
【发布时间】:2023-03-21 21:47:01
【问题描述】:

我希望外面有人能用这个来拯救我。出于某种原因,我无法理解 node.js 中的递归。如果有其他方法,它甚至不必是递归。

我正在使用 redis 集合来存储集合中的层次结构:

SADD parents.<name> <parent1> <parent2>

然后,parent1 和 parent2 也将有条目,并且向上。我想将其转换为 JSON 对象数组。

JSON 将如下所示:

[
 {
     label: <name>,
     parents: [
         { label: <parent1>,
           parents: [ {label: <grandparent1>}] },
         { label: <parent2> }
     ]
 }
]

等等等等。这应该能够适用于任何深度,尽管平均而言它只有 4-6 个节点深。

这是我一直在玩的一些代码,它只是让我进入第一级:

var redis = require('node-redis');
var r_client = redis.createClient();

function get_parents (name, current, cb) {

        var output = new Array;
        output.push( { label: name, parents: [] } );

        r_client.smembers('parents.' + name, function(err, reply) {
            for (var i = 0; i < reply.length; i++)
            {
                var name = reply[i].toString('utf8');
                output[i].parents.push({label: name, parents: [] });
             }
             cb (output);
        });
}

get_parents( 'bob', function(out) {console.log('Final output: ' + JSON.stringify( out ))} );

我基本上是想这样做:

  • 从根节点开始。打电话给redis,找父母。
  • 为根构建对象 节点。
  • 调用相同的函数来构建其他对象。
  • 随着对 redis 的调用开始返回 null,这些调用将返回并开始合并对象。

任何帮助将不胜感激。

编辑:更新 get_parents(仍然不起作用):

function get_parents (name, cb) {
    r_client.smembers('parents.' + name, function(err, reply) {
    for (var i = 0; i < reply.length; i++)
    {
      var name = reply[i].toString('utf8');
      output.push( { label: name, parents: [] } );
      output[i].parents = get_parents (output[i].parents.name, cb);
    }

    cb (output);
  });
}

编辑:我决定使用 Promises,所以I pursued that option。感谢大家的帮助!

【问题讨论】:

  • 一种简单的方法是将json存储到每个parent.name而不是一个集合
  • @GeoPhoenix - 我同意,但这是一个更大项目的一部分,所以这样做是有意义的。
  • 请记住,redis 并不是一个数据库,例如 mongoDB 或 mysql,它们具有执行此操作的构建功能,或者以“有意义”的方式存储,redis 是只是一个键值存储,所以恕我直言,将您从另一个来源获取的内容存储到 Redis,让我们说 tree.NAME 过期.. 比查询分层数据更有意义,我的意思是,twitter 关注者/关注它的 ok ,但是以这种方式存储树之类的东西,有点错过了redis的重点。不是说那不可行..

标签: node.js recursion redis


【解决方案1】:

TL;DR:我制作了一个 NodeJS 模块 (RedisTree) 和一个 blog post 来解释实现。


以上代码为初始实现

不错的挑战!这是一个符合您要求的实现,我添加了一个“.save(tree, f)”方法,它使用lodashasync,当然还有node_redis

var sampleTree = [{
  label: 'label1',
  parents: [{
    label: 'label2',
    parents: [{
      label: 'label4',
      parents: []
    }]
  }, {
    label: 'label3',
    parents: []
  }]
}];

// Note: it's required to use a set in order to retrieve data
// SET:
// sadd label1 label2 label3
// sadd label2 label4
var _     = require('lodash');
var async = require('async');
var redis = require('redis').createClient();


function RedisTree(){}

/**
 * Iteratively & asynchronously retrieve an item from Redis
 * @param  {String} label label name (set name)
 * @param  {Function} f(err, arrayOfItems)
 */
RedisTree.prototype._getItem = function(label, f) {
  var parent = _.isArray(_.last(arguments)) ? _.last(arguments) : [];

  this.members(label, function(err, cards){
    var item = {
      label: this.label(label),
      parents: []
    };
    parent.push(item);

    if(cards.length === 0){return f(null, parent);}

    async.map(cards, _.partialRight(this._getItem.bind(this), item.parents), function(e){f(e, parent);});
  }.bind(this));
};

RedisTree.prototype._saveItem = function(item, f) {
  var labels = _.pluck(item.parents, 'label');
  if(labels.length === 0){return f();}

  redis.sadd(item.label, labels, function(err){
    if(err){return f(err);}
    this._saveItems(item.parents, f);
  }.bind(this));
};

/**
 *
 * @param  {Array} arrToSave array of items
 * @param  {Function} f(err)
 */
RedisTree.prototype._saveItems = function(arrToSave, f) {
  async.forEach(arrToSave, this._saveItem.bind(this), f);
};

/**
 * Retrieve a name from the label
 * Can be overridden to provide namespace support
 * e.g. return label.substring(2);
 * @return {[type]} [description]
 */
RedisTree.prototype.label = function(label){return label;};

/**
 * Retrieve every members of the `label` set from redis
 * Can be overridden to provide namespace support
 * e.g. redis.smembers('ns.'+label, f);
 * @param {[type]} label [description]
 * @param {[type]} f     [description]
 */
RedisTree.prototype.members = function(label, f) {redis.smembers(label, f);};

/**
 * Load a tree from Redis
 * @param  {String} startLabel Were to start
 * @param  {Function} f(err, tree)
 */
RedisTree.prototype.load = function(startLabel, f){this._getItem(startLabel, f);};

/**
 * Save a tree from Redis
 * @param  {Array} treeArray
 * @param  {Function} f(err, tree)
 */
RedisTree.prototype.save = function(treeArray, f){this._saveItems(treeArray, f);};

这里是如何使用它:

var t = new RedisTree();

// Save the sampleTree
t.save(sampleTree, function(err){
  // or ... retrieve it starting at "label1"
  t.load('label1', function(err, tree){
    console.log("sampleTree === ", JSON.stringify(tree, null, 1));
  });
});

【讨论】:

  • 感谢您的回复。在我的脑海中回顾这个。
  • 您还需要什么或者我的回答是否足够?
  • 我觉得你的回答已经足够了,但是我在 Node 和 JS 方面还不够好,无法完全理解一切。我在 reddit 上发布了一个链接,有人回复了一个使用 Promise 的解决方案。我认为这对我来说更有意义。今晚我会和他们一起玩。感谢您接受挑战!
  • @coding_hero 我很想看到 Reddit 帖子的链接。 FGRibreau,非常好!
  • @BrandonTilley - This 是我最终得到的。 FGRibreau 的回答很棒,但 promise 方法对我来说更有意义。
【解决方案2】:

您确定不能将其作为 JSON 存储在磁盘上或使用 MongoDB 吗?另外,为什么您想要的结果列出父母而不是孩子?

【讨论】:

  • 是的,剩下的流程已经在 Redis 中实现了。这就是结果需要的方式,从根(请求)开始,所有父母和祖父母都跟随。这不是针对家谱,而是针对另一种类型的分层数据。
猜你喜欢
  • 2017-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多