博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Kibana源码剖析 —— savedSearch从读取到跳转
阅读量:5009 次
发布时间:2019-06-12

本文共 9039 字,大约阅读时间需要 30 分钟。

持久化对象

Kibana中可以查询到很多保存的对象,他们都存储在es中一个叫做.kibana的索引中。

  • 搜索 存储在type为search中;
  • 图表 存储在type为visualization中;
  • 仪表板 存储在type为dashboard中;

每个plugins下的tab页都有一个对应的savedObject对象,比如

  • 检索页对应的是savedSearch对象(discover/saved_searches/_saved_search.js)
  • 图表页对应的是savedVisualization对象(visualize/saved_visualizations/saved_visualizations.js)
  • 仪表板对应的是savedDashboard对象(dashboard/services/saved_dashboard.js)

这些JS都有一个特点,就是会在加载的时候注册到一个saved_object_registry的对象中去

require('plugins/settings/saved_object_registry').register({    service: 'savedSearches',    title: 'searches'  });

通过这个注册对象,可以快速的拿到对应的服务。

savedSearch

以savedSearch为例,说明如何在settings页面获取到该对象

首先代码的入口在settings/objects/index.js,它加载了settings/objects/_object.js

//第二步,由于脏检查触发了getData,因此会去执行services的查询var getData = function (filter) {          //获取保存对象services,这里拿到存储几个tab页对应的services的服务数组,然后遍历。          var services = registry.all().map(function (obj) {            var service = $injector.get(obj.service);//获取对应的服务            return service.find(filter).then(function (data) {//执行service对应的find()方法              return {                service: service,                serviceName: obj.service,                title: obj.title,                type: service.type,                data: data.hits,                total: data.total              };            });          });          $q.all(services).then(function (data) {            $scope.services = _.sortBy(data, 'title');            var tab = $scope.services[0];            if ($state.tab) $scope.currentTab = tab = _.find($scope.services, {title: $state.tab});            $scope.$watch('state.tab', function (tab) {              if (!tab) $scope.changeTab($scope.services[0]);            });          });        };//第一步,...... 页面刚加载时,由于页面绑定了advancedFilter,此时的值为undefined,因此会触发脏检查,从而触发getData()方法。$scope.$watch('advancedFilter', function (filter) {          getData(filter);        });

由上面的代码可以看到,在页面初始化时,会挨个service(检索、图表、仪表板)的服务执行find。

那么看看find()方法的内容就行了,以searchServices为例:

this.find = function (searchString, size) {      var self = this;      size = (size == null) ? 100 : size;      var body;//封装请求体      if (searchString) {        body = {          query: {            simple_query_string: {              query: searchString + '*',              fields: ['title^3', 'description'],              default_operator: 'AND'            }          }        };      } else {        body = { query: {match_all: {}}};      }//执行查询      return es.search({        index: configFile.kibana_index,        type: 'search',        body: body,        size: size      })      .then(function (resp) {//返回的数据进行改造,主要是增加source的id和url,id就是保存对象的名称;url则主要用于后期的页面跳转        return {          total: resp.hits.total,          hits: resp.hits.hits.map(function (hit) {            var source = hit._source;            source.id = hit._id;            source.url = self.urlFor(hit._id);            return source;          })        };      });    };

这样基本上完成了对象的查询,然后看看页面是如何定义的把!

当点击跳转按钮时,会触发open(item)方法,item即每个对象保存的内容。就拿searchSource来说,除了之前保存的内容,还多了id和url.

$scope.open = function (item) {    kbnUrl.change(item.url.substr(1));};

通过kbnUrl实现页面的跳转。

页面的跳转

继上篇,存储的对象会通过kbnUrl服务改变url地址:

$scope.open = function (item) {    kbnUrl.change(item.url.substr(1));};

kbnUrl在components/url/url.js中声明:

self.change = function (url, paramObj) {    self._changeLocation('url', url, paramObj);};

_changeLocation()

其中,url会改变为:"/discover/id名称"

self._changeLocation = function (type, url, paramObj, replace) {     //改变地址前,记录历史信息,用于回退      var prev = {        path: $location.path(),        search: $location.search()      };      url = self.eval(url, paramObj);      $location[type](url);//改变url地址,等待脏值检查,进行刷新      if (replace) $location.replace();      var next = {        path: $location.path(),        search: $location.search()      };      ...    };

当触发脏值检查后,会跳转到http://localhost:5601/#/discover/id名称

页面的初始化

初始化前的准备,ip和savedSearch

在页面进入到discover的时候,会进行两个操作:

  • ip:获取索引列表
  • savedSearch:通过id查询保存在.kibana索引中的信息

这两个初始化的操作是通过路由的resolve参数绑定的。resolve有个特性,就是如果传入的是一个Promise对象,就会等到这个Promise执行结束,再加载controller。

因此,只要加载了discover对应的controller,就说明上面的两个对象已经准备好了。并且都可以使用了...

使用的方法都是下面的格式:

$route.current.locals.savedSearch;$route.current.locals.ip;

discover.js的路由配置:

require('routes')  .when('/discover/:id?', {    template: require('text!plugins/discover/index.html'),    reloadOnSearch: false,    resolve: {      ip: function (Promise, courier, config, $location) {        return courier.indexPatterns.getIds()        .then(function (list) {          var stateRison = $location.search()._a;          var state;          try { state = rison.decode(stateRison); } catch (e) {}          state = state || {};          var specified = !!state.index;          var exists = _.contains(list, state.index);          var id = exists ? state.index : config.get('defaultIndex');          return Promise.props({            list: list,            loaded: courier.indexPatterns.get(id),            stateVal: state.index,            stateValFound: specified && exists          });        });      },      savedSearch: function (courier, savedSearches, $route) {        return savedSearches.get($route.current.params.id)        .catch(courier.redirectWhenMissing({          'search': '/discover',          'index-pattern': '/settings/objects/savedSearches/' + $route.current.params.id        }));      }    }  });

触发查询

由于时间空间绑定了一个变量,在discover页,对着变量进行了$watch监视,因此当第一次创建该值时,就会触发$watch

$scope.$watch('state.interval', function (interval, oldInterval) {          if (interval !== oldInterval && interval === 'auto') {            $scope.showInterval = false;          }          $scope.fetch();//fetch就是触发查询的方法        });
//初始化创建Interval    var $state = $scope.state = new AppState(getStateDefaults());    function getStateDefaults() {      return {        ...        interval: 'auto',        ...      };    }

此时就会触发$watch

IP

这个方法比较简单,主要是为了获取当前的索引列表,然后返回:

ip: function (Promise, courier, config, $location) {        return courier.indexPatterns.getIds()        .then(function (list) {          var stateRison = $location.search()._a;          var state;          try { state = rison.decode(stateRison); } catch (e) {}          state = state || {};          var specified = !!state.index;          var exists = _.contains(list, state.index);          var id = exists ? state.index : config.get('defaultIndex');          return Promise.props({            list: list,            loaded: courier.indexPatterns.get(id),            stateVal: state.index,            stateValFound: specified && exists          });        });      },

其中courier.indexPatterns.getIds()的会返回所有的索引列表,而且这个indexPatterns.getIds()的方法仅会执行一次:

在_get_ids.js中,执行下面的查询,然后进行缓存

es.search({        index: configFile.kibana_index,//索引名称为".kibana"        type: 'index-pattern',        fields: [],        body: {          query: { match_all: {} },          size: 2147483647        }      })

savedSearch

这个方法比较复杂,它首先去saved_searches工厂中,通过id获取savedSearch对象。但是其实并没有对savedSearch进行缓存,而是直接创建新的savedSearch:

this.get = function (id) {    return (new SavedSearch(id)).init();};

SavedSearch继承于savedObject

_(SavedSearch).inherits(courier.SavedObject);function SavedSearch(id) {      courier.SavedObject.call(this, {        type: SavedSearch.type,        mapping: SavedSearch.mapping,        searchSource: SavedSearch.searchSource,        id: id,        defaults: {          title: 'New Saved Search',          description: '',          columns: [],          hits: 0,          sort: [],          version: 1        }      });    }

然后调用savedObject中对应的init()方法

self.init = _.once(function () {        ...//创建docSource对应的信息        docSource        .index(configFile.kibana_index)        .type(type)        .id(self.id);//指定查询的文档        //检查是否定义过"search"类型,如果没有定义过则需要在es中的.kibana中创建相关的类型。这是因为默认的es中并没有任何kibana初始化的信息,如果第一次登陆kibana,es中的.kibana索引是没有任何内容的                return mappingSetup.isDefined(type)        .then(function (defined) {          if (defined) return true;          mapping.kibanaSavedObjectMeta = {            properties: {              // setup the searchSource mapping, even if it is not used but this type yet              searchSourceJSON: {                type: 'string'              }            }          };          return mappingSetup.setup(type, mapping);        })        .then(function () {        ...        //执行查询          return docSource.fetch()          .then(self.applyESResp);        })        .then(function () {          return customInit.call(self);        })        .then(function () {          return self;        });      });

转载于:https://www.cnblogs.com/xing901022/p/5158425.html

你可能感兴趣的文章
url模块和querystring模块
查看>>
.net core入门 部署到Linux实践
查看>>
WPF Bitmap转Imagesource
查看>>
Java compiler level does not match the version of the installed Java project facet.解决方法
查看>>
笔记_小结
查看>>
Linux lsof命令 umount U盘
查看>>
自定义Font
查看>>
Matlab绘制透明平面(二元函数)
查看>>
基于二部图的推荐
查看>>
POJ 1543
查看>>
linux svn 服务端搭建
查看>>
maven用途、核心概念、用法、常用参数和命令、扩展
查看>>
linux时间同步ntp服务的安装与配置
查看>>
django.core.exceptions.ImproperlyConfigured: Requested setting DEFAULT_INDEX_TABLESPACE的解决办法...
查看>>
网络编程-socket并发-粘包问题
查看>>
Mitmproxy(Mitmdump) 二次代理使用方式启动python
查看>>
JSP 技术(二)--详解
查看>>
第二冲刺阶段——站立会议第十四天6月7日
查看>>
HDUOJ----Super Jumping! Jumping! Jumping!
查看>>
CronTrigger
查看>>