Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How add crossfilter function filterRange. #4

Open
spectr747 opened this issue Oct 9, 2020 · 2 comments
Open

How add crossfilter function filterRange. #4

spectr747 opened this issue Oct 9, 2020 · 2 comments

Comments

@spectr747
Copy link

spectr747 commented Oct 9, 2020

I moved calculations to the server side, because data is large.

I want to draw dc.seriesChart, but after specifying date range

.x(d3.scaleTime().domain([minDate, maxDate]))
an error appears on page load:

Uncaught TypeError: t.filterRange is not a function.
Please help implement filterRange in script.js, app.js and script.js code is attached.

I'm using the crossfilter express pattern from lastlegion/dc.js-server-side-crossfilter.

app.js

var express                         = require('express');
var path                            = require('path');
var anyToJSON                       = require('anytojson');
var crossfilter                     = require('crossfilter');
var d3                              = require('d3');

var app                             = express();
app.listen(3000, () => console.log('Listening at Port 3000'));
app.use(express.static('public'));

var dimensions                      = {};
var groups                          = {};

app.get("/data", function(req, res, next) {
    
    var results                     = {};
    filter                          = req.param("filter") ? JSON.parse(req.param("filter")) : {}

    console.log(filter);

    for (dimension in dimensions) {
        console.log("Dimension: " + dimension);
        if (filter[dimension]) {
            if ((filter[dimension]).length > 1) {
                console.log(filter);
                dimensions[dimension].filterFunction(function (d) {
                    var filters = (filter[dimension]);
                    for (var i = 1; i < filters.length; i++) {
                        var filter = filters[i];
                        if (filter.isFiltered && filter.isFiltered(d)) {
                            return true;
                        } else if (filter <= d && filter >= d) {
                            return true;
                        }
                    }
                    return false;
                });
            }
            else {
                console.log(filter[dimension]);
                dimensions[dimension].filter(filter_value(filter[dimension]));
            }
        } else {
            dimensions[dimension].filterAll()
        }
    }

    for (dimension in groups) {
    
        console.log("Group: " + dimension);
    
        var group                   = groups[dimension];
        results[dimension] = {
            values: group.all(),
            top: group.top(1)[0].value
        };
    }
    
    res.writeHead(200, {
        'content-type': 'application/json'
    });
    res.end((JSON.stringify(results)));

});

function filter_value(dir) {
    
    console.log( "dfsk");
    
    if ((dir).length === 1) {
        return dir[0];
    } else if ((dir).length === 0) {
        return null
    }
}

anyToJSON.csv({ path: "input/dataset.csv"}, function(records) {

    var dateFormat = d3.timeFormat("%Y-%m-%d %H:%M:%S");
    var dateFormatParser = d3.timeParse("%Y-%m-%d %H:%M:%S");

    records.forEach(function(d) {
        d.timestamp = dateFormat(dateFormatParser(d.timestamp));
    });

    var ndx = crossfilter(records);
    var all = ndx.groupAll();

    console.log("...");

    var dateDim                     = ndx.dimension(function (d) { return d.timestamp; });
    var datePriorityDim             = ndx.dimension(function (d) { return [d.priority, d.timestamp];});
    var numRecordsByDate            = dateDim.group();
    var datePriorityGroup           = datePriorityDim.group().reduceCount();
    dimensions.dateDim              = dateDim;
    dimensions.datePriorityDim      = datePriorityDim;  
    groups.numRecordsByDate         = numRecordsByDate;
    groups.datePriorityGroup        = datePriorityGroup;

});

module.exports                      = app;

public/js/script.js

var priorityTimeChart       = new dc.seriesChart("#priority-timeline");

var filteredData = {};
var queryFilter = {};

window.filter = function(filters) {
    filters.forEach(function(d, i) { charts[i].filter(d); });
    renderAll();
};
window.reset = function(i) {
    charts[i].filter(null);
    renderAll();
};

var refresh = function(queryFilter){
    d3.json("/data?filter="+JSON.stringify(queryFilter), function(d){
        filteredData = d;
        dc.redrawAll();
    });
};

var dateDim = {
    filter:function(f){
        if(f){
            queryFilter["dateDim"]=f;
            refresh(queryFilter);
        }
    },
    filterAll:function(){
    },
    filterRange:function(){
    }
};
var numRecordsByDate = {
    all:function(){
        return filteredData["numRecordsByDate"].values;
    },
    order: function(){
    },
    top:function(){
    }
};

var datePriorityDim = {
    filter:function(f){
        if(f){
            queryFilter["datePriorityDim"]=f;
            refresh(queryFilter);
        }
    },
    filterAll:function(){
    }
};
var datePriorityGroup = {
    all:function(){
        return filteredData["datePriorityGroup"].values;
    },
    order: function(){
    },
    top:function(){
    }
};


var minDate = "Fri Mar 13 2020 11:49:35 GMT+0300";
var maxDate = "Tue Mar 17 2020 14:50:03 GMT+0300";

priorityTimeChart
    .height(h_row2)
    .chart(function(c) { return new dc.lineChart(c).curve(d3.curveCardinal).evadeDomainFilter(true); })
    .x(d3.scaleTime().domain([minDate, maxDate]))
    .brushOn(true)
    .yAxisLabel("Events")
    .xAxisLabel("Date")
    .elasticY(true)
    .dimension(dateDim)
    .group(datePriorityGroup)
    .mouseZoomable(true)
    .colors(d3.scaleOrdinal().domain(['High','Middle','Low']).range(["red", "green", "yellow"]))
    .seriesAccessor(function(d) {return "priority: " + d.key[0];})
    .keyAccessor(function(d) {return +d.key[1];})
    .valueAccessor(function(d) {return +d.value;})
    

.legend(dc.legend().x(350).y(350).itemHeight(13).gap(5).horizontal(1).legendWidth(140).itemWidth(70))
        .yAxis().ticks(3);
    priorityTimeChart.yAxis().tickFormat(function(d) {return d3.format(',d')(d);});
    priorityTimeChart.margins().left += 10;
    priorityTimeChart.filterHandler(function(dimension, filters){
        if(filters)
            dimension.filter(filters);
        else
            dimension.filter(null);
        return filters;
    });

    function init(){
        d3.json("/data?filter={}", function(d){
            filteredData = d;
            dc.renderAll();
        });
    };
    init();

This link question on stackoverflow - https://stackoverflow.com/q/64193027/7556914

@lastlegion
Copy link
Owner

Hi @spectr747 this is an experimental feature that I wrote many years ago. I'll have to look into the current crossfilter APIs to see what they use now instead of filterFunction.

@gordonwoodhull
Copy link

Thought I would check the repo before trying to answer the question, and I'm glad that @spectr747 posted it here as well.

TBH I didn't understand the question until I read your response, @lastlegion.

The crossfilter API hasn't changed in any backward-incompatible way. I think what changed is that dc.js switched to using the more efficient filterRange and filterExact methods, instead of always using filterFunction.

That said, I haven't dug into this repo to understand how it works, so I could be wrong. Hope to give it a try this week, unless someone beats me to it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants