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