betajs/betajs-data

View on GitHub
src/data/queries/constrained_queries.js

Summary

Maintainability
A
1 hr
Test Coverage
Scoped.define("module:Queries.Constrained", [
    "module:Queries",
    "base:Types",
    "base:Objs",
    "base:Tokens",
    "base:Comparators"
], function(Queries, Types, Objs, Tokens, Comparators) {
    return {

        /*
         * 
         * { query: query, options: options }
         * 
         * options:
         *  limit: int || null
         *  skip: int || 0
         *  sort: {
         *    key1: 1 || -1,
         *    key2: 1 || -1
         *  }
         * 
         */

        rectify: function(constrainedQuery) {
            var base = ("options" in constrainedQuery || "query" in constrainedQuery) ? constrainedQuery : {
                query: constrainedQuery
            };
            return Objs.extend({
                query: {},
                options: {}
            }, base);
        },

        skipValidate: function(options, capabilities) {
            if ("skip" in options) {
                if (capabilities)
                    return capabilities.skip;
            }
            return true;
        },

        limitValidate: function(options, capabilities) {
            if ("limit" in options) {
                if (capabilities)
                    return capabilities.limit;
            }
            return true;
        },

        sortValidate: function(options, capabilities) {
            if ("sort" in options) {
                if (capabilities && !capabilities.sort)
                    return false;
                if (capabilities && Types.is_object(capabilities.sort)) {
                    var supported = Objs.all(options.sort, function(dummy, key) {
                        return key in capabilities.sort;
                    });
                    if (!supported)
                        return false;
                }
            }
            return true;
        },

        constraintsValidate: function(options, capabilities) {
            return Objs.all(["skip", "limit", "sort"], function(prop) {
                return this[prop + "Validate"].call(this, options, capabilities);
            }, this);
        },

        validate: function(constrainedQuery, capabilities) {
            constrainedQuery = this.rectify(constrainedQuery);
            return this.constraintsValidate(constrainedQuery.options, capabilities) && Queries.validate(constrainedQuery.query, capabilities.query || {});
        },

        fullConstrainedQueryCapabilities: function(queryCapabilties) {
            return {
                query: queryCapabilties || Queries.fullQueryCapabilities(),
                skip: true,
                limit: true,
                sort: true // can also be false OR a non-empty object containing keys which can be ordered by
            };
        },

        normalize: function(constrainedQuery) {
            constrainedQuery = this.rectify(constrainedQuery);
            return {
                query: Queries.normalize(constrainedQuery.query),
                options: constrainedQuery.options
            };
        },

        serialize: function(constrainedQuery) {
            return JSON.stringify(this.rectify(constrainedQuery));
        },

        unserialize: function(constrainedQuery) {
            return JSON.parse(constrainedQuery);
        },

        hash: function(constrainedQuery) {
            return Tokens.simple_hash(this.serialize(constrainedQuery));
        },

        subsumizes: function(constrainedQuery, constrainedQuery2) {
            constrainedQuery = this.rectify(constrainedQuery);
            constrainedQuery2 = this.rectify(constrainedQuery2);
            var qskip = constrainedQuery.options.skip || 0;
            var qskip2 = constrainedQuery2.options.skip || 0;
            var qlimit = constrainedQuery.options.limit || null;
            var qlimit2 = constrainedQuery2.options.limit || null;
            var qsort = constrainedQuery.options.sort;
            var qsort2 = constrainedQuery.options.sort;
            if (qskip > qskip2)
                return false;
            if (qlimit) {
                if (!qlimit2)
                    return false;
                if (qlimit2 + qskip2 > qlimit + qskip)
                    return false;
            }
            if ((qskip || qlimit) && (qsort || qsort2) && JSON.stringify(qsort) != JSON.stringify(qsort2))
                return false;
            return Queries.subsumizes(constrainedQuery.query, constrainedQuery2.query);
        },

        mergeable: function(constrainedQuery, constrainedQuery2) {
            constrainedQuery = this.rectify(constrainedQuery);
            constrainedQuery2 = this.rectify(constrainedQuery2);
            if (Queries.serialize(constrainedQuery.query) != Queries.serialize(constrainedQuery2.query))
                return false;
            var qopts = constrainedQuery.options;
            var qopts2 = constrainedQuery2.options;
            if (JSON.stringify(qopts.sort || {}) != JSON.stringify(qopts2.sort || {}))
                return false;
            if ("skip" in qopts) {
                if ("skip" in qopts2) {
                    if (qopts.skip <= qopts2.skip)
                        return !qopts.limit || (qopts.skip + qopts.limit >= qopts2.skip);
                    else
                        return !qopts2.limit || (qopts2.skip + qopts2.limit >= qopts.skip);
                } else
                    return (!qopts2.limit || (qopts2.limit >= qopts.skip));
            } else
                return !("skip" in qopts2) || (!qopts.limit || (qopts.limit >= qopts2.skip));
        },

        merge: function(constrainedQuery, constrainedQuery2) {
            constrainedQuery = this.rectify(constrainedQuery);
            constrainedQuery2 = this.rectify(constrainedQuery2);
            var qopts = constrainedQuery.options;
            var qopts2 = constrainedQuery2.options;
            return {
                query: constrainedQuery.query,
                options: {
                    skip: "skip" in qopts ? ("skip" in qopts2 ? Math.min(qopts.skip, qopts2.skip) : null) : null,
                    limit: "limit" in qopts ? ("limit" in qopts2 ? Math.max(qopts.limit, qopts2.limit) : null) : null,
                    sort: constrainedQuery.sort
                }
            };
        }


    };
});