6f088b1f119df4ea92095b14c75585fd093b23c9
[motion.git] / public / assets / plugins / angular-elastic-builder / src / services / QueryService.js
1 /**
2  * angular-elastic-builder
3  *
4  * /src/services/QueryService.js
5  *
6  * This file is used to convert filters into queries, and vice versa
7  */
8
9 (function(angular) {
10   'use strict';
11
12   angular.module('angular-elastic-builder')
13     .factory('elasticQueryService', [
14       function() {
15
16         return {
17           toFilters: toFilters,
18           toQuery: toQuery,
19         };
20       }
21     ]);
22
23   function toFilters(query, fieldMap) {
24     var filters = query.map(parseQueryGroup.bind(query, fieldMap));
25     return filters;
26   }
27
28   function toQuery(filters, fieldMap) {
29     var query = filters.map(parseFilterGroup.bind(filters, fieldMap)).filter(function(item) {
30       return !!item;
31     });
32     return query;
33   }
34
35   function parseQueryGroup(fieldMap, group, truthy) {
36     if (truthy !== false) {
37       truthy = true;
38     }
39
40     var key = Object.keys(group)[0],
41       typeMap = {
42         or: 'group',
43         and: 'group',
44         range: 'number',
45       },
46       type = typeMap[key] || 'item',
47       obj = getFilterTemplate(type);
48
49     switch (key) {
50       case 'or':
51       case 'and':
52         obj.rules = group[key].map(parseQueryGroup.bind(group, fieldMap));
53         obj.subType = key;
54         break;
55       case 'missing':
56       case 'exists':
57         obj.field = group[key].field;
58         obj.subType = {
59           exists: 'exists',
60           missing: 'notExists',
61         }[key];
62         delete obj.value;
63         break;
64       case 'term':
65       case 'terms':
66         obj.field = Object.keys(group[key])[0];
67         var fieldData = fieldMap[Object.keys(group[key])[0]];
68
69         if (fieldData.type === 'multi') {
70           var vals = group[key][obj.field];
71           if (typeof vals === 'string') {
72             vals = [vals];
73           }
74           // MY CUSTOM MODIFICATION
75           obj.value = group[key][obj.field];
76           // obj.values = fieldData.choices.reduce(function(prev, choice) {
77           //   prev[choice] = truthy === (group[key][obj.field].indexOf(choice) > -1);
78           //   return prev;
79           // }, {});
80         } else {
81           obj.subType = truthy ? 'equals' : 'notEquals';
82           obj.value = group[key][obj.field];
83
84           if (typeof obj.value === 'number') {
85             obj.subType = 'boolean';
86           }
87         }
88         break;
89       case 'range':
90         obj.field = Object.keys(group[key])[0];
91         obj.subType = Object.keys(group[key][obj.field])[0];
92         obj.value = group[key][obj.field][obj.subType];
93         break;
94       case 'not':
95         obj = parseQueryGroup(fieldMap, group[key].filter, false);
96         break;
97       default:
98         obj.field = Object.keys(group[key])[0];
99         break;
100     }
101
102     return obj;
103   }
104
105   function parseFilterGroup(fieldMap, group) {
106     var obj = {};
107     if (group.type === 'group') {
108       obj[group.subType] = group.rules.map(parseFilterGroup.bind(group, fieldMap)).filter(function(item) {
109         return !!item;
110       });
111       return obj;
112     }
113
114     var fieldName = group.field;
115     var fieldData = fieldMap[fieldName];
116
117     if (!fieldName) {
118       return;
119     }
120
121     switch (fieldData.type) {
122       case 'term':
123       case 'multiterm':
124         if (fieldData.subType === 'boolean') {
125           group.subType = 'boolean';
126         }
127
128         if (!group.subType) {
129           return;
130         }
131         switch (group.subType) {
132           case 'equals':
133           case 'boolean':
134             if (group.value === undefined) {
135               return;
136             }
137             obj.term = {};
138             obj.term[fieldName] = group.value;
139             break;
140           case 'notEquals':
141             if (group.value === undefined) {
142               return;
143             }
144             obj.not = {
145               filter: {
146                 term: {}
147               }
148             };
149             obj.not.filter.term[fieldName] = group.value;
150             break;
151           case 'exists':
152             obj.exists = {
153               field: fieldName
154             };
155             break;
156           case 'notExists':
157             obj.missing = {
158               field: fieldName
159             };
160             break;
161           default:
162             throw new Error('unexpected subtype ' + group.subType);
163         }
164         break;
165
166       case 'number':
167         obj.range = {};
168         obj.range[fieldName] = {};
169         obj.range[fieldName][group.subType] = group.value;
170         break;
171
172       case 'date':
173         if (group.subType === 'exists') {
174           obj.exists = {
175             field: fieldName
176           };
177         } else if (group.subType === 'notExists') {
178           obj.missing = {
179             field: fieldName
180           };
181         } else {
182           throw new Error('unexpected subtype');
183         }
184
185         break;
186
187       case 'multi':
188         obj.terms = {};
189         obj.terms[fieldName] = group.value;
190         // obj.terms[fieldName] = Object.keys(group.values || {}).reduce(function(prev, key) {
191         //   if (group.values[key]) prev.push(key);
192         //
193         //   return prev;
194         // }, []);
195         break;
196
197       default:
198         throw new Error('unexpected type');
199     }
200
201     return obj;
202   }
203
204   function getFilterTemplate(type) {
205     var templates = {
206       group: {
207         type: 'group',
208         subType: '',
209         rules: [],
210       },
211       item: {
212         field: '',
213         subType: '',
214         value: '',
215       },
216       number: {
217         field: '',
218         subType: '',
219         value: null,
220       }
221     };
222
223     return angular.copy(templates[type]);
224   }
225
226 })(window.angular);