Question

user defined rule sample

I need to implement a user defined rules engine like the one above (technologies used: nodejs + mongodb)

i come across a SO Q's which does the data store in MySQL, but i have no idea how to query the data and get it eval via javascript:

What is an appropriate data structure and database schema to store logic rules?

any ideas about the design & implementation is highly appreciated.

Sunny

No correct solution

OTHER TIPS

Constructing an sql query with the rules you show would be very easy. Not the same case with constructing a mongo query.

I made a similar engine where i stored the rule as sql rule, then wrote a module to construct mongo query from the sql query (a simple one).

In order to match the mongo rule against a javascript object, i used the sift library (does not have same regex support as mongo query engine) - https://github.com/crcn/sift.js

So, if possible i would suggest you store the data in an sql database, then apply the sql rule you construct after you have done proper escaping (to avoid sql injection)

This is the function i used at the frontend to construct sql-type rule -

(rule here is an array of expressions mapped to each row in the UI)

function getRuleString(rule) {
    var andOrStack = [];
    var lastExpressionDepth = 0;
    var tempRuleString = '';
    var countOpenBrackets = 0;

    if(!rule || !rule.expressionRows) {
        return '';
    }

    rule.expressionRows.forEach(function(expressionRow, index) {
        expressionRow.depth = parseInt(expressionRow.depth, 10);
        if(lastExpressionDepth !== expressionRow.depth) {

            // if this is an outer expression than the previous
            if((lastExpressionDepth - expressionRow.depth) > 0) {
                for(var i=0; i<(lastExpressionDepth - expressionRow.depth); i++) {
                    tempRuleString += ' ) ';
                }
                tempRuleString += ' ' + andOrStack.pop() + ' ';
            } else {
                tempRuleString += ' ' + andOrStack.pop() + ' ( ';
            }

            lastExpressionDepth = expressionRow.depth;
        } else {
            if(index > 0) {
                tempRuleString += ' ' + andOrStack.pop() + ' ';
            }
        }

        tempRuleString += expressionRow.firstColumn + ' ' + expressionRow.operator + ' "' + expressionRow.expression + '"';
        andOrStack.push(expressionRow.andOr);
    });

    tempRuleString += Array.apply(null, Array(tempRuleString.split('(').length-tempRuleString.split(')').length)) // jshint ignore: line
            .map(String.prototype.valueOf, ' ) ').join('');

    return tempRuleString;
}

Here's a simple example illustrating a schema and a MongoDB query similar to the example you gave. I've written this in the mongo shell for simplicity; the node.js version will be similar. Hopefully this will give you an idea how to get started.

First we add some documents to the subscriber collection:

db.subscriber.insert({
    date_added: ISODate('2014-01-01'),
    first_name: 'Fred',
    language: 'en'
})
db.subscriber.insert({
    date_added: ISODate('2014-01-10'),
    first_name: 'Jorge',
    language: 'es'
})
db.subscriber.insert({
    date_added: ISODate('2014-01-20'),
    first_name: 'Jimmy',
    language: 'en'
})
db.subscriber.insert({
    date_added: ISODate('2014-01-30'),
    first_name: 'Frodo',
    language: 'sjn'
})

Here's a query similar to the example you give, matching documents that satisfy any of a condition based on a date range, a substring match of first name, or an exact match of the language:

db.subscriber.find({
    $or: [
        {date_added: {$gte: ISODate('2014-01-30')}},
        {first_name: /Jim/},
        {language: 'es'}
    ]})

And here are the documents returned by that query:

{
    "_id" : ObjectId("530f6edc32292f1f130aae16"),
    "date_added" : ISODate("2014-01-10T00:00:00Z"),
    "first_name" : "Jorge",
    "language" : "es"
},
{
    "_id" : ObjectId("530f6edc32292f1f130aae17"),
    "date_added" : ISODate("2014-01-20T00:00:00Z"),
    "first_name" : "Jimmy",
    "language" : "en"
},
{
    "_id" : ObjectId("530f6edc32292f1f130aae18"),
    "date_added" : ISODate("2014-01-30T00:00:00Z"),
    "first_name" : "Frodo",
    "language" : "sjn"
}

Does that help you get started?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top