<commit_before>'use strict';
/*
 * Copyright 2015 Next Century Corporation
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

angular.module("neonDemo.services")
.factory("FilterService",
    function() {
        var service = {};

        /**
         * Creates and returns a mapping of names from the given table names to unique filter keys for each table.
         * @param {String} The name of the visualization
         * @param {Array} The array of table names
         * @method createFilterKeys
         * @return {Object} The mapping of table names to filter keys
         */
        service.createFilterKeys = function(visualizationName, databaseNamesToTableNames) {
            var filterKeys = {};
            var databaseNames = Object.keys(databaseNamesToTableNames);
            for(var i = 0; i < databaseNames.length; ++i) {
                filterKeys[databaseNames[i]] = {};
                var tableNames = databaseNamesToTableNames[databaseNames[i]];
                for(var j = 0; j < tableNames.length; ++j) {
                    filterKeys[databaseNames[i]][tableNames[j]] = visualizationName + "-" + databaseNames[i] + "-" + tableNames[j] + "-" + uuid();
                }
            }
            return filterKeys;
        };

        /**
         * Returns the list of field names or arrays based on the data contained within the map of fields in the given relation to be used by a filter clause
         * creation function.
         * @param {Object} relation A relation object containing a map of field names to arrays of related field names
         * @method getArgumentFieldsList
         * @return {Array} A list of {String} related field names if the map of field names in the given relation object only contains one field name key;
         * otherwise, a list of {Array} lists of {String} related field names representing each combination of the different field name keys.  Either way, the
         * elements of this list will be used to call the filter clause creation functions in filterService.createFilter() below.
         */
        service.getArgumentFieldsList = function(relation) {
            // The relation will contain each field used by the filter clause creation function mapped to a list of the related fields for that field.
            // Note:  Sort the keys so, if multiple fields exist, the filter clause creation function can expect them in alphabetical order.
            var fieldNames = Object.keys(relation.fields).sort();

            // If only one field is used by the filter clause creation function, just return the list of related fields for that field.
            if(fieldNames.length === 1) {
                return relation.fields[fieldNames[0]];
            }

            // Else we need to create a list of all combinations of the related fields.  First, create a list containing all the lists of related fields.
            var relationFieldsList = [];
            for(var i = 0; i < fieldNames.length; ++i) {
                relationFieldsList.push(relation.fields[fieldNames[i]]);
            }

            // Create a list of arguments representing the fields using all combinations of the related fields.
            var getArgumentFieldsListHelper = function(unfinishedArgumentFields, unusedRelationFields) {
                var argumentFieldsList = [];
                // Iterate over each element (list) in the first unused relation field.
                for(var i = 0; i < unusedRelationFields[0].length; ++i) {
                    // Clone the unfinished arguments array and append the current unused relation field element.
                    var fields = unfinishedArgumentFields.slice(0);
                    fields.push(unusedRelationFields[0][i]);

                    if(unusedRelationFields.length === 1) {
                        // If there are no more unused relation fields, we have finished creating this arguments array.
                        argumentFieldsList.push(fields);
                    } else {
                        // Else, get the next element for the arguments array from the next unused relation field.
                        argumentFieldsList = argumentFieldsList.concat(getArgumentFieldsListHelper(fields, unusedRelationFields.slice(1)));
                    }
                }
                return argumentFieldsList;
            };

            return getArgumentFieldsListHelper([], relationFieldsList);
        };

        /**
         * Creates and returns a filter on the given table and field(s) using the given callback.
         * @param {Object} relation A relation object containing:
         * <ul>
         * <li> {String} database The database name </li>
         * <li> {Stirng} table The table name </li>
         * <li> {Object} fields The map of field names to arrays of related field names </li>
         * </ul>
         * @param {Function} createFilterClauseFunction The function used to create the filter clause for each field, with arguments:
         *  <ul>
         *      <li> {String} The table name </li>
         *      <li> {String} or {Array} The field name(s) </li>
         *  </ul>
         * @method createFilter
         * @return {Object} A neon.query.Filter object
         */
        service.createFilter = function(relation, createFilterClauseFunction) {
            // Creates a list of arguments for the filter clause creation function.  Each element is either a {String} or an {Array} depending on the number
            // of field keys in relation.fields.
            var argumentFieldsList = service.getArgumentFieldsList(relation);

            var filterClause;
            if(argumentFieldsList.length === 1) {
                filterClause = createFilterClauseFunction(relation.table, argumentFieldsList[0]);
            } else {
                var filterClauses = [];
                for(var i = 0; i < argumentFieldsList.length; ++i) {
                    filterClauses.push(createFilterClauseFunction(relation.table, argumentFieldsList[i]));
                }
                filterClause = neon.query.or.apply(neon.query, filterClauses);
            }
            return new neon.query.Filter().selectFrom(relation.database, relation.table).where(filterClause);
        };

        /**
         * Adds filters for the given relations using the given filter keys.
         * @param {Object} messenger The messenger object used to add the filters
         * @param {Array} relations The array of relations containing a database name, a table name, and a map of fields
         * @param {Object} filterKeys The map of database and table names to filter keys used by the messenger
         * @param {Function} createFilterClauseFunction The function used to create the filter clause for each field, with arguments:
         *  <ul>
         *      <li> {String} The table name </li>
         *      <li> {String} or {Array} The field name(s) </li>
         *  </ul>
         * @param {Function} successCallback The function called once all the filters have been added (optional)
         * @param {Function} errorCallback The function called if an error is returned for any of the filter calls (optional)
         * @method addFilters
         */
        service.addFilters = function(messenger, relations, filterKeys, createFilterClauseFunction, successCallback, errorCallback) {
            var addFilter = function(relationsToAdd) {
                var relation = relationsToAdd.shift();
                var filter = service.createFilter(relation, createFilterClauseFunction);
                messenger.addFilter(filterKeys[relation.database][relation.table], filter, function() {
                    if(relationsToAdd.length) {<commit_msg>NEON-1082 placeholder call for filter name from filter service<commit_after>'use strict';
/*
 * Copyright 2015 Next Century Corporation
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

angular.module("neonDemo.services")
.factory("FilterService",
    function() {
        var service = {};

        /**
         * Creates and returns a mapping of names from the given table names to unique filter keys for each table.
         * @param {String} The name of the visualization
         * @param {Array} The array of table names
         * @method createFilterKeys
         * @return {Object} The mapping of table names to filter keys
         */
        service.createFilterKeys = function(visualizationName, databaseNamesToTableNames) {
            var filterKeys = {};
            var databaseNames = Object.keys(databaseNamesToTableNames);
            for(var i = 0; i < databaseNames.length; ++i) {
                filterKeys[databaseNames[i]] = {};
                var tableNames = databaseNamesToTableNames[databaseNames[i]];
                for(var j = 0; j < tableNames.length; ++j) {
                    filterKeys[databaseNames[i]][tableNames[j]] = visualizationName + "-" + databaseNames[i] + "-" + tableNames[j] + "-" + uuid();
                }
            }
            return filterKeys;
        };

        /**
         * Returns the list of field names or arrays based on the data contained within the map of fields in the given relation to be used by a filter clause
         * creation function.
         * @param {Object} relation A relation object containing a map of field names to arrays of related field names
         * @method getArgumentFieldsList
         * @return {Array} A list of {String} related field names if the map of field names in the given relation object only contains one field name key;
         * otherwise, a list of {Array} lists of {String} related field names representing each combination of the different field name keys.  Either way, the
         * elements of this list will be used to call the filter clause creation functions in filterService.createFilter() below.
         */
        service.getArgumentFieldsList = function(relation) {
            // The relation will contain each field used by the filter clause creation function mapped to a list of the related fields for that field.
            // Note:  Sort the keys so, if multiple fields exist, the filter clause creation function can expect them in alphabetical order.
            var fieldNames = Object.keys(relation.fields).sort();

            // If only one field is used by the filter clause creation function, just return the list of related fields for that field.
            if(fieldNames.length === 1) {
                return relation.fields[fieldNames[0]];
            }

            // Else we need to create a list of all combinations of the related fields.  First, create a list containing all the lists of related fields.
            var relationFieldsList = [];
            for(var i = 0; i < fieldNames.length; ++i) {
                relationFieldsList.push(relation.fields[fieldNames[i]]);
            }

            // Create a list of arguments representing the fields using all combinations of the related fields.
            var getArgumentFieldsListHelper = function(unfinishedArgumentFields, unusedRelationFields) {
                var argumentFieldsList = [];
                // Iterate over each element (list) in the first unused relation field.
                for(var i = 0; i < unusedRelationFields[0].length; ++i) {
                    // Clone the unfinished arguments array and append the current unused relation field element.
                    var fields = unfinishedArgumentFields.slice(0);
                    fields.push(unusedRelationFields[0][i]);

                    if(unusedRelationFields.length === 1) {
                        // If there are no more unused relation fields, we have finished creating this arguments array.
                        argumentFieldsList.push(fields);
                    } else {
                        // Else, get the next element for the arguments array from the next unused relation field.
                        argumentFieldsList = argumentFieldsList.concat(getArgumentFieldsListHelper(fields, unusedRelationFields.slice(1)));
                    }
                }
                return argumentFieldsList;
            };

            return getArgumentFieldsListHelper([], relationFieldsList);
        };

        /**
         * Creates and returns a filter on the given table and field(s) using the given callback.
         * @param {Object} relation A relation object containing:
         * <ul>
         * <li> {String} database The database name </li>
         * <li> {Stirng} table The table name </li>
         * <li> {Object} fields The map of field names to arrays of related field names </li>
         * </ul>
         * @param {Function} createFilterClauseFunction The function used to create the filter clause for each field, with arguments:
         *  <ul>
         *      <li> {String} The table name </li>
         *      <li> {String} or {Array} The field name(s) </li>
         *  </ul>
         * @method createFilter
         * @return {Object} A neon.query.Filter object
         */
        service.createFilter = function(relation, createFilterClauseFunction) {
            // Creates a list of arguments for the filter clause creation function.  Each element is either a {String} or an {Array} depending on the number
            // of field keys in relation.fields.
            var argumentFieldsList = service.getArgumentFieldsList(relation);

            var filterClause;
            if(argumentFieldsList.length === 1) {
                filterClause = createFilterClauseFunction(relation.table, argumentFieldsList[0]);
            } else {
                var filterClauses = [];
                for(var i = 0; i < argumentFieldsList.length; ++i) {
                    filterClauses.push(createFilterClauseFunction(relation.table, argumentFieldsList[i]));
                }
                filterClause = neon.query.or.apply(neon.query, filterClauses);
            }
            return new neon.query.Filter().selectFrom(relation.database, relation.table).name("Test").where(filterClause);
        };

        /**
         * Adds filters for the given relations using the given filter keys.
         * @param {Object} messenger The messenger object used to add the filters
         * @param {Array} relations The array of relations containing a database name, a table name, and a map of fields
         * @param {Object} filterKeys The map of database and table names to filter keys used by the messenger
         * @param {Function} createFilterClauseFunction The function used to create the filter clause for each field, with arguments:
         *  <ul>
         *      <li> {String} The table name </li>
         *      <li> {String} or {Array} The field name(s) </li>
         *  </ul>
         * @param {Function} successCallback The function called once all the filters have been added (optional)
         * @param {Function} errorCallback The function called if an error is returned for any of the filter calls (optional)
         * @method addFilters
         */
        service.addFilters = function(messenger, relations, filterKeys, createFilterClauseFunction, successCallback, errorCallback) {
            var addFilter = function(relationsToAdd) {
                var relation = relationsToAdd.shift();
                var filter = service.createFilter(relation, createFilterClauseFunction);
                messenger.addFilter(filterKeys[relation.database][relation.table], filter, function() {
                    if(relationsToAdd.length) {
{'fraction_non_alphanumeric': 0.07922109337203677, 'fraction_numerical': 0.002902757619738752, 'mean_word_length': 2.2765999603724985, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2095565', 'n_tokens_mistral': 3909, 'n_tokens_neox': 3790, 'n_words': 1580}