2 * # angular-elastic-builder
3 * ## Angular Module for building an Elasticsearch Query
6 * @link https://github.com/dncrews/angular-elastic-builder.git
8 * @author Dan Crews <crewsd@gmail.com>
12 * angular-elastic-builder
16 * Angular Module for building an Elasticsearch query
22 angular.module('angular-elastic-builder', [
29 * angular-elastic-builder
31 * /src/directives/BuilderDirective.js
33 * Angular Directive for injecting a query builder form.
39 angular.module('angular-elastic-builder')
40 .directive('elasticBuilder', [
41 'elasticQueryService',
43 function EB(elasticQueryService) {
47 data: '=elasticBuilder',
50 templateUrl: 'angular-elastic-builder/BuilderDirective.html',
52 link: function(scope) {
53 var data = scope.data;
58 * Removes either Group or Rule
60 scope.removeChild = function(idx) {
61 scope.filters.splice(idx, 1);
67 scope.addRule = function() {
68 scope.filters.push({});
72 * Adds a Group of Rules
74 scope.addGroup = function() {
83 * Any time "outside forces" change the query, they should tell us so via
86 scope.$watch('data.needsUpdate', function(curr) {
91 scope.filters = elasticQueryService.toFilters(data.query, scope.data.fields);
92 scope.data.needsUpdate = false;
96 * Changes on the page update the Query
98 scope.$watch('filters', function(curr) {
103 data.query = elasticQueryService.toQuery(scope.filters, scope.data.fields);
114 * angular-elastic-builder
116 * /src/directives/Chooser.js
118 * This file is to help recursively, to decide whether to show a group or rule
124 var app = angular.module('angular-elastic-builder');
126 app.directive('elasticBuilderChooser', [
130 function elasticBuilderChooser(RH, groupClassHelper) {
135 item: '=elasticBuilderChooser',
139 templateUrl: 'angular-elastic-builder/ChooserDirective.html',
141 compile: function(element) {
142 return RH.compile(element, function(scope, el, attrs) {
143 var depth = scope.depth = (+attrs.depth);
144 var item = scope.item;
146 scope.getGroupClassName = function() {
148 if (item.type === 'group') {
152 return groupClassHelper(level);
164 * angular-elastic-builder
166 * /src/directives/Group.js
172 var app = angular.module('angular-elastic-builder');
174 app.directive('elasticBuilderGroup', [
178 function elasticBuilderGroup(RH, groupClassHelper) {
183 group: '=elasticBuilderGroup',
187 templateUrl: 'angular-elastic-builder/GroupDirective.html',
189 compile: function(element) {
190 return RH.compile(element, function(scope, el, attrs) {
191 var depth = scope.depth = (+attrs.depth);
192 var group = scope.group;
194 scope.addRule = function() {
195 group.rules.push({});
197 scope.addGroup = function() {
205 scope.removeChild = function(idx) {
206 group.rules.splice(idx, 1);
209 scope.getGroupClassName = function() {
210 return groupClassHelper(depth + 1);
222 * angular-elastic-builder
224 * /src/directives/Rule.js
230 var app = angular.module('angular-elastic-builder');
232 app.directive('elasticBuilderRule', [
234 function elasticBuilderRule() {
238 rule: '=elasticBuilderRule',
242 templateUrl: 'angular-elastic-builder/RuleDirective.html',
244 link: function(scope) {
245 scope.getType = function() {
246 var fields = scope.elasticFields;
247 var field = scope.rule.field;
249 if (!fields || !field) {
253 if (fields[field].subType === 'boolean') {
257 return fields[field].type;
268 * angular-elastic-builder
270 * /src/directives/RuleTypes.js
272 * Determines which Rule type should be displayed
278 var app = angular.module('angular-elastic-builder');
280 app.directive('elasticType', [
285 type: '=elasticType',
290 template: '<ng-include src="getTemplateUrl()" />',
292 link: function(scope) {
293 scope.getTemplateUrl = function() {
294 var type = scope.type;
299 type = type.charAt(0).toUpperCase() + type.slice(1);
301 return 'angular-elastic-builder/types/' + type + '.html';
304 // This is a weird hack to make sure these are numbers
305 scope.booleans = ['False', 'True'];
306 scope.booleansOrder = ['True', 'False'];
308 scope.inputNeeded = function() {
319 return ~needs.indexOf(scope.rule.subType);
330 * angular-elastic-builder
332 * /src/services/GroupClassHelper.js
334 * This keeps all of the groups colored correctly
340 angular.module('angular-elastic-builder')
341 .factory('groupClassHelper', function groupClassHelper() {
343 return function(level) {
346 // 'list-group-item-info',
347 // 'list-group-item-success',
348 // 'list-group-item-warning',
349 // 'list-group-item-danger',
352 return levels[level % levels.length];
359 * angular-elastic-builder
361 * /src/services/QueryService.js
363 * This file is used to convert filters into queries, and vice versa
369 angular.module('angular-elastic-builder')
370 .factory('elasticQueryService', [
374 toFilters: toFilters,
380 function toFilters(query, fieldMap) {
381 var filters = query.map(parseQueryGroup.bind(query, fieldMap));
385 function toQuery(filters, fieldMap) {
386 var query = filters.map(parseFilterGroup.bind(filters, fieldMap)).filter(function(item) {
392 function parseQueryGroup(fieldMap, group, truthy) {
393 if (truthy !== false) {
397 var key = Object.keys(group)[0];
403 var type = typeMap[key] || 'item';
404 var obj = getFilterTemplate(type);
409 obj.rules = group[key].map(parseQueryGroup.bind(group, fieldMap));
414 obj.field = group[key].field;
417 missing: 'notExists',
423 obj.field = Object.keys(group[key])[0];
424 var fieldData = fieldMap[Object.keys(group[key])[0]];
426 if (fieldData.type === 'multi') {
427 var vals = group[key][obj.field];
428 if (typeof vals === 'string') {
431 obj.values = fieldData.choices.reduce(function(prev, choice) {
432 prev[choice] = truthy === (group[key][obj.field].indexOf(choice) > -1);
436 obj.subType = truthy ? 'equals' : 'notEquals';
437 obj.value = group[key][obj.field];
439 if (typeof obj.value === 'number') {
440 obj.subType = 'boolean';
445 obj.field = Object.keys(group[key])[0];
446 obj.subType = Object.keys(group[key][obj.field])[0];
447 obj.value = group[key][obj.field][obj.subType];
450 obj = parseQueryGroup(fieldMap, group[key].filter, false);
453 obj.field = Object.keys(group[key])[0];
460 function parseFilterGroup(fieldMap, group) {
462 if (group.type === 'group') {
463 obj[group.subType] = group.rules.map(parseFilterGroup.bind(group, fieldMap)).filter(function(item) {
469 var fieldName = group.field;
470 var fieldData = fieldMap[fieldName];
476 switch (fieldData.type) {
478 if (fieldData.subType === 'boolean') {
479 group.subType = 'boolean';
482 if (!group.subType) {
485 switch (group.subType) {
488 if (group.value === undefined) {
491 obj[fieldName] = group.value;
494 if (group.value === undefined) {
512 throw new Error('unexpected subtype ' + group.subType);
521 switch (group.subType) {
523 obj[fieldName] = group.value;
526 obj[fieldName][group.subType] = group.value;
532 if (group.subType === 'exists') {
536 } else if (group.subType === 'notExists') {
541 throw new Error('unexpected subtype');
548 obj.terms[fieldName] = Object.keys(group.values || {}).reduce(function(prev, key) {
549 if (group.values[key]) {
559 // obj.terms[fieldName] = Object.keys(group.values || {}).reduce(function(prev, key) {
560 // if (group.values[key]) {
569 throw new Error('unexpected type');
575 function getFilterTemplate(type) {
594 return angular.copy(templates[type]);
599 (function(angular) {"use strict"; angular.module("angular-elastic-builder").run(["$templateCache", function($templateCache) {$templateCache.put("angular-elastic-builder/BuilderDirective.html","<!-- <div class=\"elastic-builder\">\n <div class=\"filter-panels\"> -->\n <div class=\"list-group form-inline list-group-item-custom\">\n <div\n data-ng-repeat=\"filter in filters\"\n data-elastic-builder-chooser=\"filter\"\n data-elastic-fields=\"data.fields\"\n data-on-remove=\"removeChild($index)\"\n data-depth=\"0\"></div>\n <div class=\"list-group-item actions\">\n <a href=\"#\" class=\"btn green btn-sm\" data-ng-click=\"addRule()\">\n <i class=\"icon-plus\"></i> {{ \'APPLICATION_ADD_CONDITION\' | translate }}\n </a>\n <a href=\"#\" class=\"btn green btn-sm\" data-ng-click=\"addGroup()\">\n <i class=\"icon-plus\"></i> {{ \'APPLICATION_ADD_GROUP\' | translate }}\n </a>\n </div>\n </div>\n <!-- </div>\n</div> -->\n");
600 $templateCache.put("angular-elastic-builder/ChooserDirective.html","<div class=\"list-group-item elastic-builder-chooser list-group-item-custom\" data-ng-class=\"getGroupClassName()\">\n <div data-ng-if=\"item.type === \'group\'\"\n data-elastic-builder-group=\"item\"\n data-depth=\"{{ depth }}\"\n data-elastic-fields=\"elasticFields\"\n data-on-remove=\"onRemove()\"></div>\n <div data-ng-if=\"item.type !== \'group\'\"\n data-elastic-builder-rule=\"item\"\n data-elastic-fields=\"elasticFields\"\n data-on-remove=\"onRemove()\"></div>\n</div>\n");
601 $templateCache.put("angular-elastic-builder/GroupDirective.html","<!-- BEGIN Portlet PORTLET-->\n<div class=\"portlet light bordered\">\n <div class=\"portlet-title\">\n <div class=\"caption font-green-sharp\">\n <!-- <i class=\"icon-people font-green-sharp\"></i> -->\n <h5><span class=\"caption-subject\">If</span>\n <select data-ng-model=\"group.subType\" class=\"form-control\">\n <option value=\"$and\"><strong>all</strong></option>\n <option value=\"$or\"><strong>any</strong></option>\n </select>\n <span class=\"caption-subject\">of these conditions are met</span>\n </h5>\n </div>\n </div>\n <div class=\"portlet-body\">\n <div class=\"elastic-builder-group\">\n <div\n data-ng-repeat=\"rule in group.rules\"\n data-elastic-builder-chooser=\"rule\"\n data-elastic-fields=\"elasticFields\"\n data-depth=\"{{ +depth + 1 }}\"\n data-on-remove=\"removeChild($index)\"></div>\n\n <div class=\"list-group-item actions list-group-item-custom\" data-ng-class=\"getGroupClassName()\">\n <a href=\"#\" class=\"btn green btn-sm\" data-ng-click=\"addRule()\">\n <i class=\"icon-plus\"></i> {{ \'APPLICATION_ADD_CONDITION\' | translate }}\n </a>\n <a class=\"btn red btn-sm remover\" data-ng-click=\"onRemove()\">\n <i class=\"icon-trash\"></i> {{ \'APPLICATION_REMOVE_GROUP\' | translate }}\n </a>\n </div>\n </div>\n </div>\n</div>\n<!-- END Portlet PORTLET-->\n");
602 $templateCache.put("angular-elastic-builder/RuleDirective.html","<!-- <div class=\"elastic-builder-rule\"> -->\n <select class=\"form-control\" data-ng-model=\"rule.field\" data-ng-options=\"key as key for (key, value) in elasticFields\"></select>\n\n <span data-elastic-type=\"getType()\" data-rule=\"rule\" data-guide=\"elasticFields[rule.field]\"></span>\n\n <!-- <a class=\"btn btn-xs btn-danger remover\" data-ng-click=\"onRemove()\">\n <i class=\"fa fa-minus\"></i>\n </a> -->\n\n <a class=\"btn remover\" data-ng-click=\"onRemove()\">\n <i class=\"icon-trash\"></i>\n </a>\n\n<!-- </div> -->\n");
603 $templateCache.put("angular-elastic-builder/types/Boolean.html","<span class=\"boolean-rule\">\n Equals\n\n <!-- This is a weird hack to make sure these are numbers -->\n <select\n data-ng-model=\"rule.value\"\n class=\"form-control\"\n data-ng-options=\"booleans.indexOf(choice) as choice for choice in booleansOrder\">\n </select>\n</span>\n");
604 $templateCache.put("angular-elastic-builder/types/Date.html","<span class=\"date-rule\">\n <select data-ng-model=\"rule.subType\" class=\"form-control\">\n\n <optgroup label=\"Generic\">\n <option value=\"exists\">Exists</option>\n <option value=\"notExists\">! Exists</option>\n </optgroup>\n </select>\n\n</span>\n");
605 $templateCache.put("angular-elastic-builder/types/Multi.html","<span class=\"multi-rule\">\n <span data-ng-repeat=\"choice in guide.choices\">\n <label class=\"checkbox\">\n <input type=\"checkbox\" data-ng-model=\"rule.values[choice]\">\n {{ choice }}\n </label>\n </span>\n</span>\n");
606 $templateCache.put("angular-elastic-builder/types/Number.html","<span class=\"number-rule\">\n <select data-ng-model=\"rule.subType\" class=\"form-control\">\n <!-- <optgroup label=\"Numeral\"> -->\n <option value=\"equals\">=</option>\n <option value=\"$gt\">></option>\n <option value=\"$gte\">≥</option>\n <option value=\"$lt\"><</option>\n <option value=\"$lte\">≤</option>\n <!-- </optgroup> -->\n\n <!-- <optgroup label=\"Generic\">\n <option value=\"exists\">Exists</option>\n <option value=\"notExists\">! Exists</option>\n </optgroup> -->\n </select>\n\n <!-- Range Fields -->\n <input data-ng-if=\"inputNeeded()\"\n class=\"form-control\"\n data-ng-model=\"rule.value\"\n type=\"number\"\n min=\"{{ guide.minimum }}\"\n max=\"{{ guide.maximum }}\">\n</span>\n");
607 $templateCache.put("angular-elastic-builder/types/Select.html","<span class=\"number-rule\">\n <select data-ng-model=\"rule.subType\" class=\"form-control\">\n <!-- <optgroup label=\"Numeral\"> -->\n <option value=\"equals\">=</option>\n <!-- <option value=\"$gt\">></option>\n <option value=\"$gte\">≥</option>\n <option value=\"$lt\"><</option>\n <option value=\"$lte\">≤</option> -->\n <!-- </optgroup> -->\n\n <!-- <optgroup label=\"Generic\">\n <option value=\"exists\">Exists</option>\n <option value=\"notExists\">! Exists</option>\n </optgroup> -->\n </select>\n\n <!-- Range Fields -->\n <select data-ng-model=\"rule.value\" class=\"form-control\">\n <!-- <optgroup label=\"Numeral\"> -->\n <option data-ng-repeat=\"choice in guide.choices\" value=\"{{choice.value}}\">{{choice.key}}</option>\n <!-- </optgroup> -->\n\n <!-- <optgroup label=\"Generic\">\n <option value=\"exists\">Exists</option>\n <option value=\"notExists\">! Exists</option>\n </optgroup> -->\n </select>\n\n <!-- <input data-ng-if=\"inputNeeded()\"\n class=\"form-control\"\n data-ng-model=\"rule.value\"\n type=\"number\"\n min=\"{{ guide.minimum }}\"\n max=\"{{ guide.maximum }}\"> -->\n</span>\n");
608 $templateCache.put("angular-elastic-builder/types/Term.html","<span class=\"elastic-term\">\n <select data-ng-model=\"rule.subType\" class=\"form-control\">\n <!-- Term Options -->\n <!-- <optgroup label=\"Text\"> -->\n <option value=\"equals\">{{ \'APPLICATION_EQUALS_TO\' | translate }}</option>\n <option value=\"notEquals\">{{ \'APPLICATION_NOT_EQUALS_TO\' | translate }}</option>\n <!-- </optgroup> -->\n\n <!-- Generic Options -->\n <!-- <optgroup label=\"Generic\">\n <option value=\"exists\">Exists</option>\n <option value=\"notExists\">! Exists</option>\n </optgroup> -->\n\n </select>\n <input\n data-ng-if=\"inputNeeded()\"\n class=\"form-control\"\n data-ng-model=\"rule.value\"\n type=\"text\">\n</span>\n");}]);})(window.angular);