2 * Copyright 2010-2013 Ben Birch
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 (function( $, app, i18n ) {
18 var ui = app.ns("ui");
19 var data = app.ns("data");
20 var ut = app.ns("ut");
22 ui.FilterBrowser = ui.AbstractWidget.extend({
24 cluster: null, // (required) instanceof app.services.Cluster
25 index: "" // (required) name of the index to query
28 init: function(parent) {
30 this._cluster = this.config.cluster;
31 this.el = $(this._main_template());
32 this.filtersEl = this.el.find(".uiFilterBrowser-filters");
33 this.attach( parent );
34 new data.MetaDataFactory({ cluster: this._cluster, onReady: function(metadata, eventData) {
35 this.metadata = metadata;
36 this._createFilters_handler(eventData.originalData.metadata.indices);
40 _createFilters_handler: function(data) {
42 function scan_properties(path, obj) {
44 for (var prop in obj.properties) {
45 scan_properties(path.concat(prop), obj.properties[prop]);
50 for (var subField in obj.fields) {
51 filters.push({ path: (path[path.length - 1] !== subField) ? path.concat(subField) : path, type: obj.fields[subField].type, meta: obj.fields[subField] });
54 filters.push({ path: path, type: obj.type, meta: obj });
58 for(var type in data[this.config.index].mappings) {
59 scan_properties([type], data[this.config.index].mappings[type]);
62 filters.sort( function(a, b) {
63 var x = a.path.join(".");
64 var y = b.path.join(".");
65 return (x < y) ? -1 : (x > y) ? 1 : 0;
69 { path: ["match_all"], type: "match_all", meta: {} },
70 { path: ["_all"], type: "_all", meta: {}}
73 this._addFilterRow_handler();
76 _addFilterRow_handler: function() {
77 this.filtersEl.append(this._filter_template());
80 _removeFilterRow_handler: function(jEv) {
81 $(jEv.target).closest("DIV.uiFilterBrowser-row").remove();
82 if(this.filtersEl.children().length === 0) {
83 this._addFilterRow_handler();
87 _search_handler: function() {
88 var search = new data.BoolQuery();
89 search.setSize( this.el.find(".uiFilterBrowser-outputSize").val() )
90 this.fire("startingSearch");
91 this.filtersEl.find(".uiFilterBrowser-row").each(function(i, row) {
93 var bool = row.find(".bool").val();
94 var field = row.find(".field").val();
95 var op = row.find(".op").val();
97 if(field === "match_all") {
99 } else if(op === "range") {
100 var lowqual = row.find(".lowqual").val(),
101 highqual = row.find(".highqual").val();
103 value[row.find(".lowop").val()] = lowqual;
105 if(highqual.length) {
106 value[row.find(".highop").val()] = highqual;
108 } else if(op === "fuzzy") {
109 var qual = row.find(".qual").val(),
110 fuzzyqual = row.find(".fuzzyqual").val();
112 value["value"] = qual;
114 if(fuzzyqual.length) {
115 value[row.find(".fuzzyop").val()] = fuzzyqual;
118 value = row.find(".qual").val();
120 search.addClause(value, field, op, bool);
122 if(this.el.find(".uiFilterBrowser-showSrc").attr("checked")) {
123 this.fire("searchSource", search.search);
125 this._cluster.post( this.config.index + "/_search", search.getData(), this._results_handler );
128 _results_handler: function( data ) {
129 var type = this.el.find(".uiFilterBrowser-outputFormat").val();
130 this.fire("results", this, { type: type, data: data, metadata: this.metadata });
133 _changeQueryField_handler: function(jEv) {
134 var select = $(jEv.target);
135 var spec = select.children(":selected").data("spec");
136 select.siblings().remove(".op,.qual,.range,.fuzzy");
138 if(spec.type === 'match_all') {
139 } else if(spec.type === '_all') {
140 ops = ["query_string"];
141 } else if(spec.type === 'string' || spec.type === 'text' || spec.type === 'keyword') {
142 ops = ["term", "wildcard", "prefix", "fuzzy", "range", "query_string", "text", "missing"];
143 } else if(spec.type === 'long' || spec.type === 'integer' || spec.type === 'float' ||
144 spec.type === 'byte' || spec.type === 'short' || spec.type === 'double') {
145 ops = ["term", "range", "fuzzy", "query_string", "missing"];
146 } else if(spec.type === 'date') {
147 ops = ["term", "range", "fuzzy", "query_string", "missing"];
148 } else if(spec.type === 'geo_point') {
150 } else if(spec.type === 'ip') {
151 ops = ["term", "range", "fuzzy", "query_string", "missing"];
152 } else if(spec.type === 'boolean') {
155 select.after({ tag: "SELECT", cls: "op", onchange: this._changeQueryOp_handler, children: ops.map(ut.option_template) });
156 select.next().change();
159 _changeQueryOp_handler: function(jEv) {
160 var op = $(jEv.target), opv = op.val();
161 op.siblings().remove(".qual,.range,.fuzzy");
162 if(opv === 'term' || opv === 'wildcard' || opv === 'prefix' || opv === "query_string" || opv === 'text') {
163 op.after({ tag: "INPUT", cls: "qual", type: "text" });
164 } else if(opv === 'range') {
165 op.after(this._range_template());
166 } else if(opv === 'fuzzy') {
167 op.after(this._fuzzy_template());
171 _main_template: function() {
172 return { tag: "DIV", children: [
173 { tag: "DIV", cls: "uiFilterBrowser-filters" },
174 { tag: "BUTTON", type: "button", text: i18n.text("General.Search"), onclick: this._search_handler },
175 { tag: "LABEL", children:
176 i18n.complex("FilterBrowser.OutputType", { tag: "SELECT", cls: "uiFilterBrowser-outputFormat", children: [
177 { text: i18n.text("Output.Table"), value: "table" },
178 { text: i18n.text("Output.JSON"), value: "json" },
179 { text: i18n.text("Output.CSV"), value: "csv" }
180 ].map(function( o ) { return $.extend({ tag: "OPTION" }, o ); } ) } )
182 { tag: "LABEL", children:
183 i18n.complex("FilterBrowser.OutputSize", { tag: "SELECT", cls: "uiFilterBrowser-outputSize",
184 children: [ "10", "50", "250", "1000", "5000", "25000" ].map( ut.option_template )
187 { tag: "LABEL", children: [ { tag: "INPUT", type: "checkbox", cls: "uiFilterBrowser-showSrc" }, i18n.text("Output.ShowSource") ] }
191 _filter_template: function() {
192 return { tag: "DIV", cls: "uiFilterBrowser-row", children: [
193 { tag: "SELECT", cls: "bool", children: ["must", "must_not", "should"].map(ut.option_template) },
194 { tag: "SELECT", cls: "field", onchange: this._changeQueryField_handler, children: this.filters.map(function(f) {
195 return { tag: "OPTION", data: { spec: f }, value: f.path.join("."), text: f.path.join(".") };
197 { tag: "BUTTON", type: "button", text: "+", onclick: this._addFilterRow_handler },
198 { tag: "BUTTON", type: "button", text: "-", onclick: this._removeFilterRow_handler }
202 _range_template: function() {
203 return { tag: "SPAN", cls: "range", children: [
204 { tag: "SELECT", cls: "lowop", children: ["gt", "gte"].map(ut.option_template) },
205 { tag: "INPUT", type: "text", cls: "lowqual" },
206 { tag: "SELECT", cls: "highop", children: ["lt", "lte"].map(ut.option_template) },
207 { tag: "INPUT", type: "text", cls: "highqual" }
211 _fuzzy_template: function() {
212 return { tag: "SPAN", cls: "fuzzy", children: [
213 { tag: "INPUT", cls: "qual", type: "text" },
214 { tag: "SELECT", cls: "fuzzyop", children: ["max_expansions", "min_similarity"].map(ut.option_template) },
215 { tag: "INPUT", cls: "fuzzyqual", type: "text" }
220 })( this.jQuery, this.app, this.i18n );