搜索d3树 - 搜索后重置

时间:2015-03-23 21:49:07

标签: javascript d3.js

我已经编写了一个搜索功能来运行一个可能非常大的d3树,并仅筛选出与搜索模式匹配的节点。

搜索功能运行良好且速度相当快。

但是,由于在服务器上计算数据的数量和时间,我希望能够将树重置为其初始内容,如果用户清除搜索或输入另一个,而不进行其他查询。那不行。

这就是我的尝试:

1 - 克隆d3节点

首先,我看了一下[什么是最有效的方法克隆对象] [1],他们有几个建议。从Jasmine运行,我的克隆似乎工作(不确定为什么实际上)。

在我的应用中运行代码,我立即收到错误消息。确切的错误取决于我使用的代码--JSONery.extend或JSON.stringify的JSON.parse。

我怀疑这是因为每个节点d都有一个d.parent属性和d.children,因此只要你想要递归地克隆它的子节点它就进入一个循环。

2 - 设置d3.layout.tree.children来过滤数据。

然后我注意到有一个d3.layout.tree.children函数,我可以使用它来操作树。所以,我这样做而不是复制json数据,只会返回d.visible为true的节点。我的想法是,当我想要重置时,我不需要做任何事情,我只是回到基地,非过滤,孩子们。

那也不起作用 - 与搜索不匹配的子节点现在已从数据中消失,而不仅仅是图表。

更新:我已删除了我的初始示例代码,该代码无法正常工作,并且我自己给出了一个适合我的初步答案。

1 个答案:

答案 0 :(得分:0)

不确定这是否是最优雅的方法,但它对我有用。

基本上,我只是在进行搜索之前复制对现有_nodes的引用。搜索通过树进行递归并克隆与搜索模式匹配的节点。以及具有匹配搜索模式的子节点的任何节点。 因此克隆量受匹配量的限制。 克隆代码本身非常幼稚 - 它只处理原始值,但这就是我现在所需要的。

注意:_nodes基本上是您在任何在线d3示例中看到的传入json数据。

//support an array of possible attributes for string matches...
var li_tosearch = ["label"];

//a "set" of node attributes that should not be copied    
s_skip = Object.create(null);
s_skip.parent = 1;
s_skip.children = 1;
s_skip._children = 1;
s_skip._unfiltered_children = 1;


function clone_n_filter(pattern, d) {
    /* recursive search by node 
       - a node is visible if one of its searchable attributes
       matches the pattern or if one of its children/_children is
       visible.
       - only visible nodes get cloned 
    */

    if (!d  || !pattern){
        return;
    }

    var visible_child, clone, v, s;
    var visible = false;


    //support an array of possible attributes for string matches...
    for (var i = 0; i < li_tosearch.length; ++i){
        s = d[li_tosearch[i]];
        if (s){
            if (s.match(pattern)){
                visible = true;
                break;
            }
        }
    }


    var children = [], _children = [];

    //if any of the children are visible then the current is visible too
    if (d.children) {
        for (var index = 0; index < d.children.length; ++index) {
            visible_child = clone_n_filter(pattern, d.children[index]);
            if (visible_child) {
                children.push(visible_child);
                visible = true;
            }
        }
    }


    if (d._children) {
        for (var index2 = 0; index2 < d._children.length; ++index2) {
            visible_child = clone_n_filter(pattern, d._children[index2]);
            if (visible_child) {
                _children.push(visible_child);
                visible = true;
            }
        }
    }

    //not being used, but keeps track of all original children
    //this is just a reference, so memory cost is low
    //possible use case:  expand a non-leaf node that has
    //no visible children
    d._unfiltered_children = d.children || d._children;

    if (visible){
        clone = d.constructor(); // changed

        for(var key in d) {
            if (key in s_skip){
                continue;
            }

            if(d.hasOwnProperty(key)) {
                v = d[key];

                //given the domain, only expecting primitives...
                //will address when that breaks
                if (typeof(v) === 'object'){
                    throw("unexpected complex object:[" + typeof(v) + " " + key + "]");
                }

                clone[key] = v;
            }
            else{
                //keeping track of attributes we won't be copying
                s_skip[key] = 1;
            }
        }
        clone.children = children;
        clone._children = _children;

    }

    return clone;

}

整个事情的切入点。 重置非常快 - 它只是将_nodes设置为原始保存的引用。

_chart.search = function(pattern){
  /* external entry point for calling the search*/

    if (!_nodes){
        return;
    }

    if (!pattern){
        //no search pattern
        if  (_o_nodes){
            //restore unfiltered
            _nodes = _o_nodes;            
        }
        chart.render();
        return;
    }

    if (!_o_nodes){
        //no need to clone the _nodes, just copy reference
        _o_nodes = _nodes;        
    }

    var patre = new RegExp(pattern,"gi");
    _nodes = clone_n_filter(patre, _o_nodes);

    chart.render();
};