How to implementing Advanced Filter in Dash AG Grid(ServerSide RowModel)

I want to apply Advanced Filtering to my dash_ag_grid code, similar to what’s shown in the AG Grid Documentation.

  1. JavaScript Data Grid: SSRM Filtering (ag-grid.com)

  2. JavaScript Data Grid: Advanced Filter (ag-grid.com)

Please someone help me with it.
I want to know if its possible, and if so, any tips or methods on how to implement this would be greatly appreciated.

Below is the code I’m currently working with.

app.py:

from dash import Dash, Input, Output, html, dcc, State
import dash_ag_grid as dag
import requests, json
import flask
import pandas as pd
import pprint
from log import logger

app = Dash(__name__)

server = app.server

df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)
df = df.dropna()
rowData = df.to_dict("records")


def filterDf(df, data, col):
    operators = {
        "greaterThanOrEqual": "ge",
        "lessThanOrEqual": "le",
        "lessThan": "lt",
        "greaterThan": "gt",
        "notEqual": "ne",
        "equals": "eq",
    }

    if "filter" in data:
        crit1 = data["filter"]
        crit1 = pd.Series(crit1).astype(df[col].dtype)[0]

    if "type" in data:
        if data["type"] == "contains":
            df = df.loc[df[col].str.contains(crit1, na=False)]
        elif data["type"] == "notContains":
            df = df.loc[~df[col].str.contains(crit1, na=False)]
        elif data["type"] == "startsWith":
            df = df.loc[df[col].str.startswith(crit1, na=False)]
        elif data["type"] == "notStartsWith":
            df = df.loc[~df[col].str.startswith(crit1, na=False)]
        elif data["type"] == "endsWith":
            df = df.loc[df[col].str.endswith(crit1, na=False)]
        elif data["type"] == "notEndsWith":
            df = df.loc[~df[col].str.endswith(crit1, na=False)]
        elif data["type"] == "blank":
            df = df.loc[df[col].isnull()]
        elif data["type"] == "notBlank":
            df = df.loc[~df[col].isnull()]
        else:
            df = df.loc[getattr(df[col], operators[data["type"]])(crit1)]
    elif data["filterType"] == "set":
        df = df.loc[df[col].isin(data["values"])]
    return df


def extractRowsFromData(request, df):
    response = []

    logger.info(f"{pprint.pformat(request)}")

    dff = df.copy()

    if request["filterModel"]:
        fils = request["filterModel"]
        for k in fils:
            try:
                if "operator" in fils[k]:
                    if fils[k]["operator"] == "AND":
                        for fil in fils[k]["conditions"]:
                            dff = filterDf(dff, fil, k)
                    else:
                        dffs = []
                        for fil in fils[k]["conditions"]:
                            dffs.append(filterDf(dff, fil, k))
                        dff = pd.concat(dffs)
                else:
                    dff = filterDf(dff, fils[k], k)
            except:
                pass

    sorting = []
    asc = []
    if request["sortModel"]:
        for sort in request["sortModel"]:
            sorting.append(sort["colId"])
            asc.append(sort["sort"] == "asc")

        # dff = dff.sort_values(by=sorting, ascending=asc)

    groupBy = []
    if request["rowGroupCols"]:
        groupBy = [i["id"] for i in request["rowGroupCols"]]

    if groupBy:
        group_counts = dff.groupby(groupBy).size().reset_index(name="childCount")
        dff = dff.merge(group_counts, on=groupBy, how="left")

    agg = {}
    if request["valueCols"]:
        agg = {i["id"]: i["aggFunc"] for i in request["valueCols"]}
    if not request["groupKeys"]:
        if groupBy:
            if agg:
                dff = dff.groupby(groupBy[0]).agg(agg).reset_index()
            else:
                dff = dff.groupby(groupBy[0]).agg("count").reset_index()
    else:
        for i in range(len(request["groupKeys"])):
            dff = dff[dff[request["rowGroupCols"][i]["id"]] == request["groupKeys"][i]]
        if len(request["groupKeys"]) != len(groupBy):
            if agg:
                dff = (
                    dff.groupby(groupBy[: len(request["groupKeys"]) + 1])
                    .agg(agg)
                    .reset_index()
                )
            else:
                dff = (
                    dff.groupby(groupBy[: len(request["groupKeys"]) + 1])
                    .agg("count")
                    .reset_index()
                )

    if groupBy:
        logger.info(f"sorting: {sorting}")
        logger.info(f"asc: {asc}")
        logger.info(f"groupBy[0]: {groupBy[0]}")

        if groupBy[0] in sorting:
            logger.info(f"dff: {dff.head()}")
            logger.info(f"asc[0]: {asc[0]}")

            dff = dff.sort_values(by="childCount", ascending=asc[0])
        else:
            dff = dff.sort_values(by=sorting, ascending=asc)
    else:
        dff = dff.sort_values(by=sorting, ascending=asc)

    logger.info(f"{pprint.pformat(dff)}")
    return {
        "rowData": dff.to_dict("records")[request["startRow"] : request["endRow"]],
        "rowCount": len(dff),
    }


@server.route("/api/serverData", methods=["POST"])
def serverData():
    response = extractRowsFromData(flask.request.json, df)
    # logger.info(pprint.pformat(response))
    return json.dumps(response)


grid = html.Div(
    [
        dag.AgGrid(
            id="grid",
            columnDefs=[
                {"field": "country"},
                {"field": "year"},
                {"field": "athlete"},
                {"field": "age"},
                {"field": "date"},
                {"field": "sport"},
                {"field": "total"},
                # {"field": "childCount", "hide": True},  # 기본적으로 숨김
            ],
            defaultColDef={
                "filter": True,
                "sortable": True,
                "resizable": True,
                "enableRowGroup": True,
                "enableValue": True,
                "enablePivot": True,
            },
            dashGridOptions={
                "rowSelection": "multiple",
                "sideBar": True,
                "rowGroupPanelShow": "always",
                "suppressRowGroupHidesColumns": True,
                "getChildCount": {"function": "getChildCount(params)"},
            },
            enableEnterpriseModules=True,
            rowModelType="serverSide",
            style={"overflow": "auto", "resize": "both", "height": "60vh"},
        ),
    ]
)

app.layout = html.Div(
    [
        dcc.Markdown("Example: Organisational Hierarchy using Tree Data "),
        grid,
    ]
)

app.clientside_callback(
    """async function (id) {
        
        const updateData = (grid) => {
          var datasource = createServerSideDatasource();
          grid.setServerSideDatasource(datasource);
        };
        
        var grid;
        grid = await window.dash_ag_grid.getApiAsync(id)
        
        if (grid) {
            updateData(grid)
        }
        
        return window.dash_clientside.no_update
    }""",
    Output("grid", "id"),
    Input("grid", "id"),
)

if __name__ == "__main__":
    app.run(debug=True)

assets\dashAgGridFunctions.js :

async function getServerData(request) {
  response = await fetch('./api/serverData', {'method': 'POST', 'body': JSON.stringify(request),
    'headers': {'content-type': 'application/json'}})
  return response.json()
}


function createServerSideDatasource() {
  const dataSource = {
    getRows: async (params) => {
      console.log('ServerSideDatasource.getRows: params = ', params);
      var result = await getServerData(params.request);
      console.log('getRows: result = ', result);
      params.success(result);
    },
  };
  return dataSource;
}



var dagfuncs = window.dashAgGridFunctions = window.dashAgGridFunctions || {};

dagfuncs.getChildCount =  function(data) {
  // here child count is stored in the 'childCount' property
  // console.log('data = ', data);
  return data.childCount;
}

Hello @wowwwn,

Can you please provide some info in what you mean by advanced filters?

Hi @wowwwn
The Advanced Filter is new in AG Grid 30 and that’s not available Dash AG Grid V2.4.0 which uses V29.3.5 of AG Grid. It will be available in the next release though!

Thank you so much for responding to my question.

  1. JavaScript Data Grid: Advanced Filter

  2. JavaScript Data Grid: SSRM Filtering

If you see the ag-grid documentation link above, it shows advanced filter examples by setting enableAdvancedFilter: true in the gridOptions.

Filter-related Menubar is added under the ag-grid column and , clicking on the Builder button provides an interface for easily manipulating filters through a GUI.

I want to know how to apply this to my dash_ag_grid code which uses ServerSideRowModel.

Please let me know if you need any more information.

Hi @AnnMarieW,

Thank you so much for the update.
It’s great to hear that the Advanced Filter feature from AG Grid 30 will be available in the next release of Dash AG Grid.
Do you have any information on when the next release might be expected?
I’m really looking forward to integrating this feature into my projects.

Thanks again for your help and for keeping the community informed!

Hi @wowwwn

I’m not sure when they will be released. There are a lot of new features and some breaking changes. We are just getting started on the docs and still doing testing on the new version. I expect it will be at least a few weeks.

1 Like