skip to Main Content

I am trying to create/store some filtering conditions in JSON. I understand JSON is used to represent data and is not a scripting language but i am working with some limitations here. I have a SQL background and not a OOP language background.

The JSON will serve up the UI for users to build filters. Below is a mockup/wireframe of "LiveCondition". There will be a dialog for "History" and "LiveandHistory" in the same page. "LiveandHistory" will coorelate Live and History.

enter image description here

  1. Using JSON arrays, LiveCondition can be seen as an array with 3 elements in it.

  2. Element [0] is a sub array with 3 elements in it – an object, string with the value and and another object.

  3. Element 1 is a string with the value or.

  4. Element 2 is another object defining some values.

The code can effectively use this tree against which I hope we could do some processing using C# and using it as rules for making decisions.

https://codebeautify.org/jsonviewer/y239dc164

The questions i have been posed is that this representation doesnt separate out logic and entities. The combination of json can signficantly go up if more entities are added in the future. Overall, I am trying to create something that is terse and can scale well and follow a consistent data model. Can this be represented in a different way?


{
    "Criteria": {
        "Name": "test Filter",
        "type": "regular"
 },
    "Live": [
        [{
                "sequenceNumber": 1,
                "property": "HCPCS",
                "operator": "Is",
                "relationalOperator": "[]",
                "valueType": "Simple",
                "value": ["00100", "01999"]
            },
            "and", {
                "sequenceNumber": 2,
                "property": "RelativeWeight",
                "operator": "Is",
                "relationalOperator": "[]",
                "valueType": "simple",
                "value": [1.5, 3.5]
            }
        ],
        "or", {
            "sequenceNumber": 3,
            "property": "Charges",
            "operator": "Is not",
            "relationalOperator": ">=",
            "valueType": "simple",
            "value": "5000"
        }
    ],
    "History": [
        [{
                "sequenceNumber": 1,
                "property": "HCPCS",
                "operator": "Is",
                "relationalOperator": "[]",
                "valueType": "Simple",
                "value": ["00100", "01999"]
            },
            "and", {
                "sequenceNumber": 2,
                "property": "RelativeWeight",
                "operator": "Is",
                "relationalOperator": "[]",
                "valueType": "simple",
                "value": [1.5, 3.5]
            }
        ]
    ],
    "LiveAndHistory": [
        [{
               "sequenceNumber": 1,
                "LiveProperty": "AdmissionDate",
                "relationalOperator": "<",
                "valueType": "DaysOf",
                "value": "2",
                "HistoryProperty": "ThroughDate",
                "IncludeLiveandHistoryClaims": "0"
            },
            "and", {
                "sequenceNumber": 2,
                "LiveProperty": "Charges",
                "relationalOperator": "<=",
                "valueType": "GroupBy",
                "value": "5000",
                "HistoryProperty": "RevCode",
                "IncludeLiveandHistoryClaims": "1"
            }
        ]
    ]
}

2

Answers


  1. Your current JSON representation for defining filtering conditions is reasonable, and it can be used effectively to build a filter system in your application. However, if you want to address concerns related to separating logic and entities, as well as making it more scalable and maintainable, you might consider using a more structured approach.

    Here is my suggestion:
    Separate Entities and Rules

    {
        "Criteria": {
            "Name": "test Filter",
            "type": "regular"
        },
        "Entities": {
            "Live": [
                {
                    "sequenceNumber": 1,
                    "property": "HCPCS",
                    "operator": "Is",
                    "relationalOperator": "[]",
                    "valueType": "Simple",
                    "value": ["00100", "01999"]
                },
                {
                    "sequenceNumber": 2,
                    "property": "RelativeWeight",
                    "operator": "Is",
                    "relationalOperator": "[]",
                    "valueType": "simple",
                    "value": [1.5, 3.5]
                },
                {
                    "sequenceNumber": 3,
                    "property": "Charges",
                    "operator": "Is not",
                    "relationalOperator": ">=",
                    "valueType": "simple",
                    "value": "5000"
                }
            ],
            "History": [
                {
                    "sequenceNumber": 1,
                    "property": "HCPCS",
                    "operator": "Is",
                    "relationalOperator": "[]",
                    "valueType": "Simple",
                    "value": ["00100", "01999"]
                },
                {
                    "sequenceNumber": 2,
                    "property": "RelativeWeight",
                    "operator": "Is",
                    "relationalOperator": "[]",
                    "valueType": "simple",
                    "value": [1.5, 3.5]
                }
            ]
        },
        "Rules": {
            "LiveAndHistory": [
                {
                    "sequenceNumber": 1,
                    "LiveProperty": "AdmissionDate",
                    "relationalOperator": "<",
                    "valueType": "DaysOf",
                    "value": "2",
                    "HistoryProperty": "ThroughDate",
                    "IncludeLiveandHistoryClaims": "0"
                },
                {
                    "sequenceNumber": 2,
                    "LiveProperty": "Charges",
                    "relationalOperator": "<=",
                    "valueType": "GroupBy",
                    "value": "5000",
                    "HistoryProperty": "RevCode",
                    "IncludeLiveandHistoryClaims": "1"
                }
            ]
        }
    }
    

    Updated: represent logical "AND" and "OR" conditions between your objects

    {
        "Criteria": {
            "Name": "test Filter",
            "type": "regular"
        },
        "Entities": {
            "Live": {
                "conditions": [
                    {
                        "sequenceNumber": 1,
                        "property": "HCPCS",
                        "operator": "Is",
                        "relationalOperator": "[]",
                        "valueType": "Simple",
                        "value": ["00100", "01999"]
                    },
                    {
                        "sequenceNumber": 2,
                        "property": "RelativeWeight",
                        "operator": "Is",
                        "relationalOperator": "[]",
                        "valueType": "simple",
                        "value": [1.5, 3.5]
                    }
                ],
                "logicalOperator": "AND"
            },
            "History": {
                "conditions": [
                    {
                        "sequenceNumber": 1,
                        "property": "HCPCS",
                        "operator": "Is",
                        "relationalOperator": "[]",
                        "valueType": "Simple",
                        "value": ["00100", "01999"]
                    },
                    {
                        "sequenceNumber": 2,
                        "property": "RelativeWeight",
                        "operator": "Is",
                        "relationalOperator": "[]",
                        "valueType": "simple",
                        "value": [1.5, 3.5]
                    }
                ],
                "logicalOperator": "AND"
            }
        },
        "Rules": {
            "LiveAndHistory": {
                "conditions": [
                    {
                        "sequenceNumber": 1,
                        "LiveProperty": "AdmissionDate",
                        "relationalOperator": "<",
                        "valueType": "DaysOf",
                        "value": "2",
                        "HistoryProperty": "ThroughDate",
                        "IncludeLiveandHistoryClaims": "0"
                    },
                    {
                        "sequenceNumber": 2,
                        "LiveProperty": "Charges",
                        "relationalOperator": "<=",
                        "valueType": "GroupBy",
                        "value": "5000",
                        "HistoryProperty": "RevCode",
                        "IncludeLiveandHistoryClaims": "1"
                    }
                ],
                "logicalOperator": "OR"
            }
        }
    }
    
    
    Login or Signup to reply.
  2. One way of representing arbitrary conditional logic is to construct an abstract syntax tree that represents a conditional expression, and then figure out how you want to map that to JSON. One way to express these tree nodes in JSON is with objects that have a "type" that indicates the type of syntax, and additional structure for the object that’s type-dependent. For that additional structure, you plug in other nodes that are also JSON objects with a "type" field and additional type-dependent structure.

    Here, it looks like an multi-valued "OR" expression has a nested "AND" expression inside. Using the structure described above, we might write this:

    {
      "type": "or",
      "operands": [
        {
          "type": "and",
          "operands": [
            {...HCPCS...},
            {...RelativeWeight...}
          ]
        },
        {...Charges...}
      ]
    }
    

    The idea here is that, for each operand, you can plug in any kind of thing that makes sense. Both "and" and "or" have a similar undifferentiated structure represented by an array of children, and the array can represent the sequence in which we should render and/or evaluate the children.

    So how do we represent those more fundamental clauses you have? Going the abstract syntax route, I would focus on the operators.

    For HCPCS, it kind of looks to me like you’re trying to say "this property should be in this range". Maybe you mean something more like this?

    {
      "type": "inRange",
      "subject": {"type": "propertyRef", "ref": "HCPCS"},
      "low": {"type": "stringLiteral", "value": "00100"},
      "high": {"type": "stringLiteral", "value": "01999"}
    }
    

    Again, I’m using the "type" to indicate the type of thing I’m plugging in. Here, instead of a generic "operands" array, because the different operands have special meaning, I name them different things: the property I’m comparing, and the range I expect it to be in. (I wouldn’t expect strings for a range comparison, though, so I may not be understanding what you are trying to express.)

    For negation, you could write:

    {"type": "not", "operand": {...}}
    

    So your Charges clause might look something like this:

    {
      "type": "not",
      "operand": {
        "type": "lessThanOrEqual",
        "subject": {"type": "propertyRef", "ref": "Charges"},
        "value": {"type": "integerLiteral", "value": 5000}
      }
    }
    

    BTW, this might be more sensible as:

    {
      "type": "greaterThan",
      "subject": {"type": "propertyRef", "ref": "Charges"},
      "value": {"type": "integerLiteral", "value": 5000}
    }
    

    Abstract syntax trees give you the power to build pretty much any kind of logical expression you want, but it leaves the question of how to separate logic from entities. The easy way to get separation in JSON:

    1. Define your entities in one place in the JSON document and give them unique names.
    2. Refer to those names somehow in other parts of your JSON document.

    The "propertyRef" object above might be an example of this. If you had a part of your document, for example, with:

    "properties: [
      {
        "type": "property",
        "id": "HCPCS",
        ...
      },
      {
        "type": "property",
        "id": "Charges",
        ...
      }
    ]
    

    then we could understand the "propertyRef" as referring to one of these "property" objects. The "propertyRef" object is basically a hyperlink!

    Not only does linking like this help reduce some of the clutter in the expression, but it enables you to not repeat yourself when you need to reference the property in multiple places.

    Neither the AST syntax nor the linking syntax I’ve described above are standard – they are patterns, and there are many ways to express them in JSON. However, if you look at the JSON:API specification, you’ll see that these ideas of type-tagged data and links do pop up all the time in JSON standards.

    Hope this gives you some ideas!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search