03bc7f0d140258e5b67c5bf77dab3ac9c92f0fd9
[ccsdk/features.git] /
1 /**
2  * Copyright 2010-2013 Ben Birch
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this software except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 (function( app ) {
17
18         /*
19         notes on elasticsearch terminology used in this project
20
21         indices[index] contains one or more
22         types[type] contains one or more
23         documents contain one or more
24         paths[path]
25         each path contains one element of data
26         each path maps to one field
27
28         eg PUT, "/twitter/tweet/1"
29         {
30                 user: "mobz",
31                 date: "2011-01-01",
32                 message: "You know, for browsing elasticsearch",
33                 name: {
34                         first: "Ben",
35                         last: "Birch"
36                 }
37         }
38
39         creates
40                 1 index: twitter
41                                 this is the collection of index data
42                 1 type: tweet
43                                 this is the type of document (kind of like a table in sql)
44                 1 document: /twitter/tweet/1
45                                 this is an actual document in the index ( kind of like a row in sql)
46                 5 paths: [ ["user"], ["date"], ["message"], ["name","first"], ["name","last"] ]
47                                 since documents can be heirarchical this maps a path from a document root to a piece of data
48                 5 fields: [ "user", "date", "message", "first", "last" ]
49                                 this is an indexed 'column' of data. fields are not heirarchical
50
51                 the relationship between a path and a field is called a mapping. mappings also contain a wealth of information about how es indexes the field
52
53         notes
54         1) a path is stored as an array, the dpath is  <index> . <type> . path.join("."),
55                         which can be considered the canonical reference for a mapping
56         2) confusingly, es uses the term index for both the collection of indexed data, and the individually indexed fields
57                         so the term index_name is the same as field_name in this sense.
58
59         */
60
61         var data = app.ns("data");
62         var ux = app.ns("ux");
63
64         var coretype_map = {
65                 "string" : "string",
66                 "keyword" : "string",
67                 "text" : "string",
68                 "byte" : "number",
69                 "short" : "number",
70                 "long" : "number",
71                 "integer" : "number",
72                 "float" : "number",
73                 "double" : "number",
74                 "ip" : "number",
75                 "date" : "date",
76                 "boolean" : "boolean",
77                 "binary" : "binary",
78                 "multi_field" : "multi_field"
79         };
80
81         var default_property_map = {
82                 "string" : { "store" : "no", "index" : "analysed" },
83                 "number" : { "store" : "no", "precision_steps" : 4 },
84                 "date" : { "store" : "no", "format" : "dateOptionalTime", "index": "yes", "precision_steps": 4 },
85                 "boolean" : { "store" : "no", "index": "yes" },
86                 "binary" : { },
87                 "multi_field" : { }
88         };
89
90         // parses metatdata from a cluster, into a bunch of useful data structures
91         data.MetaData = ux.Observable.extend({
92                 defaults: {
93                         state: null // (required) response from a /_cluster/state request
94                 },
95                 init: function() {
96                         this._super();
97                         this.refresh(this.config.state);
98                 },
99                 getIndices: function(alias) {
100                         return alias ? this.aliases[alias] : this.indicesList;
101                 },
102                 // returns an array of strings containing all types that are in all of the indices passed in, or all types
103                 getTypes: function(indices) {
104                         var indices = indices || [], types = [];
105                         this.typesList.forEach(function(type) {
106                                 for(var i = 0; i < indices.length; i++) {
107                                         if(! this.indices[indices[i]].types.contains(type))
108                                                 return;
109                                 }
110                                 types.push(type);
111                         }, this);
112                         return types;
113                 },
114                 refresh: function(state) {
115                         // currently metadata expects all like named fields to have the same type, even when from different types and indices
116                         var aliases = this.aliases = {};
117                         var indices = this.indices = {};
118                         var types = this.types = {};
119                         var fields = this.fields = {};
120                         var paths = this.paths = {};
121
122                         function createField( mapping, index, type, path, name ) {
123                                 var dpath = [ index, type ].concat( path ).join( "." );
124                                 var field_name = mapping.index_name || path.join( "." );
125                                 var field = paths[ dpath ] = fields[ field_name ] || $.extend({
126                                         field_name : field_name,
127                                         core_type : coretype_map[ mapping.type ],
128                                         dpaths : []
129                                 }, default_property_map[ coretype_map[ mapping.type ] ], mapping );
130
131                                 if (field.type === "multi_field" && typeof field.fields !== "undefined") {
132                                         for (var subField in field.fields) {
133                                                 field.fields[ subField ] = createField( field.fields[ subField ], index, type, path.concat( subField ), name + "." + subField );
134                                         }
135                                 }
136                                 if (fields.dpaths) {
137                                         field.dpaths.push(dpath);
138                                 }
139                                 return field;
140                         }
141                         function getFields(properties, type, index, listeners) {
142                                 (function procPath(prop, path) {
143                                         for (var n in prop) {
144                                                 if ("properties" in prop[n]) {
145                                                         procPath( prop[ n ].properties, path.concat( n ) );
146                                                 } else {
147                                                         var field = createField(prop[n], index, type, path.concat(n), n);                                                       
148                                                         listeners.forEach( function( listener ) {
149                                                                 listener[ field.field_name ] = field;
150                                                         } );
151                                                 }
152                                         }
153                                 })(properties, []);
154                         }
155                         for (var index in state.metadata.indices) {
156                                 indices[index] = {
157                                         types : [], fields : {}, paths : {}, parents : {}
158                                 };
159                                 indices[index].aliases = state.metadata.indices[index].aliases;
160                                 indices[index].aliases.forEach(function(alias) {
161                                         (aliases[alias] || (aliases[alias] = [])).push(index);
162                                 });
163                                 var mapping = state.metadata.indices[index].mappings;
164                                 for (var type in mapping) {
165                                         indices[index].types.push(type);
166                                         if ( type in types) {
167                                                 types[type].indices.push(index);
168                                         } else {
169                                                 types[type] = {
170                                                         indices : [index], fields : {}
171                                                 };
172                                         }
173                                         getFields(mapping[type].properties, type, index, [fields, types[type].fields, indices[index].fields]);
174                                         if ( typeof mapping[type]._parent !== "undefined") {
175                                                 indices[index].parents[type] = mapping[type]._parent.type;
176                                         }
177                                 }
178                         }
179
180                         this.aliasesList = Object.keys(aliases);
181                         this.indicesList = Object.keys(indices);
182                         this.typesList = Object.keys(types);
183                         this.fieldsList = Object.keys(fields);
184                 }
185         });
186
187 })( this.app );