相关文章推荐
机灵的铁链  ·  我对python中的wget模块有问题。-腾 ...·  1 年前    · 
爱运动的圣诞树  ·  Capturing Terraform ...·  1 年前    · 
成熟的枇杷  ·  javascript中宿主对象和原生对象的区 ...·  1 年前    · 
火星上的冲锋衣  ·  dependencies.dependenc ...·  1 年前    · 
踢足球的刺猬  ·  Hibernate:深入HQL学习 | 李大辉·  1 年前    · 
小百科  ›  Kibana源码剖析 —— savedSearch从读取开去链接-腾讯云开发者社区-腾讯云
源码 kibana js获取当前url js获取url参数
稳重的啤酒
1 年前
用户1154259
0 篇文章

Kibana源码剖析 —— savedSearch从读取到跳转

前往专栏
腾讯云
开发者社区
文档 意见反馈 控制台
首页
学习
活动
专区
工具
TVP
最新优惠活动
文章/答案/技术大牛
发布
首页
学习
活动
专区
工具
TVP 最新优惠活动
返回腾讯云官网
用户1154259
首页
学习
活动
专区
工具
TVP 最新优惠活动
返回腾讯云官网
社区首页 > 专栏 > xingoo, 一个梦想做发明家的程序员 > Kibana源码剖析 —— savedSearch从读取到跳转

Kibana源码剖析 —— savedSearch从读取到跳转

作者头像
用户1154259
发布 于 2018-01-17 16:14:03
1.4K 1
发布 于 2018-01-17 16:14:03
举报

持久化对象

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;
    };
这样基本上完成了对象的查询,然后看看页面是如何定义的把!
 <!-- 对象列表 遍历services数组创建对应的service列表-->
      <div ng-repeat="service in services" ng-class="{ active: state.tab === service.title }" class="tab-pane">
        <ul class="list-unstyled">
          <li class="item" ng-repeat="item in service.data | orderBy:'title'">
            <div class="actions pull-right">
              <button
                ng-click="edit(service, item)"
                class="btn btn-default"
                aria-label="Edit">
                <span class="sr-only" translate="edit">Edit</span>
                <i aria-hidden="true" class="fa fa-pencil"></i>
              </button><!-- 该按钮对应了页面上的编辑按钮 -->
              <button
                ng-click="open(item)"
                class="btn btn-info"
                aria-label="Hide">
                <span class="sr-only" translate="hide">Hide</span>
                <i aria-hidden="true" class="fa fa-eye"></i>
              </button><!-- 该按钮对应了页面上的跳转按钮,小眼睛的那个 -->
            <div class="pull-left">
              <input
                ng-click="toggleItem(item)"
                ng-checked="selectedItems.indexOf(item) >= 0"
                type="checkbox" ><!-- 该按钮对应了页面上的单选框 -->
            <div class="item-title">
              <a ng-click="edit(service, item)">{{ item.title }}</a>
            </div><!-- 该部分的内容为页面上的对象名称,点击可以直接进行修改 -->
          <li ng-if="!service.data.length" class="empty">
            <span translate="no_found_1">No </span>"{{service.title}}" <span translate="no_found_2">found.</span></li>
    </div>

当点击跳转按钮时,会触发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 () {
 
推荐文章
机灵的铁链  ·  我对python中的wget模块有问题。-腾讯云开发者社区-腾讯云
1 年前
爱运动的圣诞树  ·  Capturing Terraform Azure CLI Traffic with Fiddler - samcogan.com
1 年前
成熟的枇杷  ·  javascript中宿主对象和原生对象的区别是什么
1 年前
火星上的冲锋衣  ·  dependencies.dependency.version missing问题解决 - 司马他
1 年前
踢足球的刺猬  ·  Hibernate:深入HQL学习 | 李大辉
1 年前
今天看啥   ·   Py中国   ·   codingpro   ·   小百科   ·   link之家   ·   卧龙AI搜索
删除内容请联系邮箱 2879853325@qq.com
小百科 - 百科知识指南
© 2024 ~ 沪ICP备11025650号