add global cluster select and init rebuild
This commit is contained in:
parent
9fd1d490ac
commit
e44729fcb8
2
Makefile
2
Makefile
|
@ -36,7 +36,7 @@ INFINI_BASE_FOLDER := $(OLDGOPATH)/src/infini.sh/
|
|||
FRAMEWORK_FOLDER := $(INFINI_BASE_FOLDER)framework/
|
||||
FRAMEWORK_REPO := ssh://git@git.infini.ltd:64221/infini/framework.git
|
||||
FRAMEWORK_BRANCH := master
|
||||
FRAMEWORK_VENDOR_FOLDER := $(CURDIR)/../vendor/
|
||||
FRAMEWORK_VENDOR_FOLDER := $(CURDIR)/vendor/
|
||||
FRAMEWORK_VENDOR_REPO := ssh://git@git.infini.ltd:64221/infini/framework-vendor.git
|
||||
FRAMEWORK_VENDOR_BRANCH := master
|
||||
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
package index_management
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
httprouter "infini.sh/framework/core/api/router"
|
||||
"infini.sh/framework/core/elastic"
|
||||
"infini.sh/framework/core/util"
|
||||
"infini.sh/search-center/model"
|
||||
)
|
||||
|
||||
func (handler APIHandler) ReindexAction(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
|
||||
reindexItem := &model.InfiniReindex{}
|
||||
resResult := map[string]interface{}{
|
||||
"errno": "0",
|
||||
"errmsg": "",
|
||||
"payload": nil,
|
||||
}
|
||||
|
||||
err := handler.DecodeJSON(req, reindexItem)
|
||||
if err != nil {
|
||||
resResult["errno"] = "E20001"
|
||||
resResult["errmsg"] = err.Error()
|
||||
handler.WriteJSON(w, resResult, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(reindexItem)
|
||||
|
||||
taskID, err := reindex(handler.Config.Elasticsearch, reindexItem)
|
||||
if err != nil {
|
||||
resResult["errno"] = "E20002"
|
||||
resResult["errmsg"] = err.Error()
|
||||
handler.WriteJSON(w, resResult, http.StatusOK)
|
||||
return
|
||||
}
|
||||
resResult["payload"] = taskID
|
||||
handler.WriteJSON(w, resResult, http.StatusOK)
|
||||
}
|
||||
|
||||
func reindex(esName string, body *model.InfiniReindex) (string, error) {
|
||||
client := elastic.GetClient(esName)
|
||||
esConfig := elastic.GetConfig(esName)
|
||||
url := fmt.Sprintf("%s/_reindex?wait_for_completion=false", esConfig.Endpoint)
|
||||
source := map[string]interface{}{
|
||||
"index": body.Source.Index,
|
||||
}
|
||||
if body.Source.MaxDocs > 0 {
|
||||
source["max_docs"] = body.Source.MaxDocs
|
||||
}
|
||||
if body.Source.Query != nil {
|
||||
source["query"] = body.Source.Query
|
||||
}
|
||||
if body.Source.Sort != "" {
|
||||
source["sort"] = body.Source.Sort
|
||||
}
|
||||
if body.Source.Source != "" {
|
||||
source["_source"] = body.Source.Source
|
||||
}
|
||||
dest := map[string]string{
|
||||
"index": body.Dest.Index,
|
||||
}
|
||||
if body.Dest.Pipeline != "" {
|
||||
dest["pipeline"] = body.Dest.Pipeline
|
||||
}
|
||||
esBody := map[string]interface{}{
|
||||
"source": source,
|
||||
"dest": dest,
|
||||
}
|
||||
buf, _ := json.Marshal(esBody)
|
||||
fmt.Println(string(buf))
|
||||
reindexRes, err := client.Request("POST", url, buf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
resBody := struct {
|
||||
Task string `json:"task"`
|
||||
}{}
|
||||
err = json.Unmarshal(reindexRes.Body, &resBody)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
body.ID = util.GetUUID()
|
||||
body.TaskId = resBody.Task
|
||||
body.CreatedAt = time.Now()
|
||||
|
||||
_, err = client.Index("infinireindex", body.ID, body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return body.ID, nil
|
||||
}
|
15
api/init.go
15
api/init.go
|
@ -11,13 +11,16 @@ func Init(cfg *config.AppConfig) {
|
|||
handler := index_management.APIHandler{
|
||||
Config: cfg,
|
||||
}
|
||||
var pathPrefix = "/_search-center/"
|
||||
//ui.HandleUIMethod(api.POST, "/api/get_indices",index_management.API1)
|
||||
ui.HandleUIMethod(api.GET, "/api/dict/_search", handler.GetDictListAction)
|
||||
ui.HandleUIMethod(api.POST, "/api/dict/_create", handler.CreateDictItemAction)
|
||||
ui.HandleUIMethod(api.GET, pathPrefix+"dict/_search", handler.GetDictListAction)
|
||||
ui.HandleUIMethod(api.POST, pathPrefix+"dict/_create", handler.CreateDictItemAction)
|
||||
//ui.HandleUIMethod(api.GET, "/api/dict/:id",handler.GetDictItemAction)
|
||||
ui.HandleUIMethod(api.DELETE, "/api/dict/:id", handler.DeleteDictItemAction)
|
||||
ui.HandleUIMethod(api.DELETE, pathPrefix+"dict/:id", handler.DeleteDictItemAction)
|
||||
//ui.HandleUIMethod(api.DELETE, "/api/dict/", handler.DeleteDictItemAction2)
|
||||
ui.HandleUIMethod(api.POST, "/api/dict/_update", handler.UpdateDictItemAction)
|
||||
ui.HandleUIMethod(api.POST, "/api/doc/:index", handler.HandleDocumentAction)
|
||||
ui.HandleUIMethod(api.GET, "/api/indices/_cat", handler.HandleGetIndicesAction)
|
||||
ui.HandleUIMethod(api.POST, pathPrefix+"dict/_update", handler.UpdateDictItemAction)
|
||||
ui.HandleUIMethod(api.POST, pathPrefix+"doc/:index", handler.HandleDocumentAction)
|
||||
ui.HandleUIMethod(api.GET, pathPrefix+"indices/_cat", handler.HandleGetIndicesAction)
|
||||
|
||||
ui.HandleUIMethod(api.POST, pathPrefix+"rebuild/_create", handler.ReindexAction)
|
||||
}
|
||||
|
|
1
main.go
1
main.go
|
@ -70,6 +70,7 @@ func main() {
|
|||
|
||||
}, func() {
|
||||
orm.RegisterSchema(model.Dict{})
|
||||
orm.RegisterSchema(model.InfiniReindex{})
|
||||
})
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type InfiniReindex struct {
|
||||
ID string `json:"id" elastic_meta:"_id"`
|
||||
Name string `json:"name" elastic_mapping:"name:{type:text}"`
|
||||
Desc string `json:"desc" elastic_mapping:"desc:{type:text}"`
|
||||
TaskId string `json:"task_id" elastic_mapping:"task_id:{type:keyword}"`
|
||||
Source struct {
|
||||
Index string `json:"index"`
|
||||
MaxDocs int `json:"max_docs"`
|
||||
Query map[string]interface{} `json:"query"`
|
||||
Sort string `json:"sort"`
|
||||
Source string `json:"_source"`
|
||||
} `json:"source" elastic_mapping:"source:{type:object}"`
|
||||
Dest struct {
|
||||
Index string `json:"index"`
|
||||
Pipeline string `json:"pipeline"`
|
||||
} `json:"dest" elastic_mapping:"dest:{type:object}"`
|
||||
|
||||
CreatedAt time.Time `json:"created_at" elastic_mapping:"created_at:{type:date}"`
|
||||
Status string `json:"status" elastic_mapping:"status:{type:keyword}"`
|
||||
}
|
5
ui.go
5
ui.go
|
@ -2,9 +2,10 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
public "infini.sh/search-center/.public"
|
||||
"net/http"
|
||||
|
||||
public "infini.sh/search-center/.public"
|
||||
|
||||
log "github.com/cihub/seelog"
|
||||
"infini.sh/framework/core/api"
|
||||
"infini.sh/framework/core/ui"
|
||||
|
@ -21,7 +22,7 @@ type UI struct {
|
|||
|
||||
func (h UI) InitUI() {
|
||||
|
||||
vfs.RegisterFS(public.StaticFS{StaticFolder: h.Config.UILocalPath, TrimLeftPath: h.Config.UILocalPath , CheckLocalFirst: h.Config.UILocalEnabled, SkipVFS: !h.Config.UIVFSEnabled})
|
||||
vfs.RegisterFS(public.StaticFS{StaticFolder: h.Config.UILocalPath, TrimLeftPath: h.Config.UILocalPath, CheckLocalFirst: h.Config.UILocalEnabled, SkipVFS: !h.Config.UIVFSEnabled})
|
||||
|
||||
ui.HandleUI("/", vfs.FileServer(vfs.VFS()))
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ export default {
|
|||
// },
|
||||
// },
|
||||
proxy: {
|
||||
'/api/': {
|
||||
'/_search-center/': {
|
||||
target: 'http://localhost:2900',
|
||||
changeOrigin: true,
|
||||
// pathRewrite: { '^/server': '' },
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
export const queryData = {
|
||||
"errmsg": "",
|
||||
"errno": "0",
|
||||
"payload": {
|
||||
"data": [
|
||||
{
|
||||
"_index": "infini-test",
|
||||
"address": "hunan changsha",
|
||||
"created_at": "2020-12-23T03:57:57.620Z",
|
||||
"email": "liugq@qq.com",
|
||||
"hobbies": "[\"basketball\",\"pingpan\"]",
|
||||
"id": "jc6_jXYBKoaaPbVfj_8W",
|
||||
"name": "liugq国家"
|
||||
},
|
||||
{
|
||||
"_index": "infini-test",
|
||||
"address": "hunan changsha",
|
||||
"created_at": "2020-12-23T03:57:57.620Z",
|
||||
"email": "786027438@qq.com",
|
||||
"hobbies": "[\"test5\"]",
|
||||
"id": "bvhm18dath2d6oa9046g",
|
||||
"name": "hello4"
|
||||
},
|
||||
{
|
||||
"_index": "infini-test",
|
||||
"address": "hunan changsha",
|
||||
"created_at": "2020-12-23T03:57:57.620Z",
|
||||
"email": "786027438@qq.com",
|
||||
"hobbies": "[\"test2\"]",
|
||||
"id": "bvhlv6dath2d6oa9045g",
|
||||
"name": "test 词典"
|
||||
},
|
||||
{
|
||||
"_index": "infini-test",
|
||||
"address": "hunan changsha",
|
||||
"created_at": "2020-12-23T03:57:57.620Z",
|
||||
"email": "786027438@qq.com",
|
||||
"hobbies": "[\"test1\"]",
|
||||
"id": "bvhltpdath2d6oa90450",
|
||||
"name": "liugqy"
|
||||
},
|
||||
{
|
||||
"_index": "infini-test",
|
||||
"address": "hunan zhuzhou",
|
||||
"created_at": "2020-12-23T03:59:57.620Z",
|
||||
"email": "cincky@qq.com",
|
||||
"hobbies": "[\"basketball\",\"badminton1\"]",
|
||||
"id": "js7EjXYBKoaaPbVfvf-c",
|
||||
"name": "cincky"
|
||||
},
|
||||
{
|
||||
"_index": "infini-test",
|
||||
"address": "湖北武汉2",
|
||||
"created_at": "2020-12-23T03:57:57.620Z",
|
||||
"email": "786027438@qq.com",
|
||||
"hobbies": [
|
||||
"test3"
|
||||
],
|
||||
"id": "bvi5ellath2e0ukbq5e0",
|
||||
"name": "武汉test"
|
||||
},
|
||||
{
|
||||
"_index": "infini-test",
|
||||
"address": "hunan changsha",
|
||||
"created_at": "2020-12-23T03:57:57.620Z",
|
||||
"email": "786027438@qq.com",
|
||||
"hobbies": [
|
||||
"test3"
|
||||
],
|
||||
"id": "bvia41lath2eneoeeij0",
|
||||
"name": "铁路测试词典"
|
||||
},
|
||||
{
|
||||
"_index": "infini-test",
|
||||
"address": "beijing",
|
||||
"created_at": "2020-12-23T03:57:57.620Z",
|
||||
"email": "786027438@qq.com",
|
||||
"hobbies": [
|
||||
"basketball1",
|
||||
"badminton"
|
||||
],
|
||||
"id": "bvia4ctath2eneoeeijg",
|
||||
"name": "北京"
|
||||
},
|
||||
{
|
||||
"_index": "infini-test",
|
||||
"address": "湖北武汉",
|
||||
"created_at": "2020-12-23T03:57:57.620Z",
|
||||
"email": "786027438@qq.com",
|
||||
"hobbies": [
|
||||
"test4"
|
||||
],
|
||||
"id": "bvi5omtath2e0ukbq5eg",
|
||||
"name": "武汉2"
|
||||
},
|
||||
{
|
||||
"_index": "infini-test",
|
||||
"address": "hunan changsha1",
|
||||
"created_at": "2020-12-23T03:57:57.620Z",
|
||||
"email": "786027438@qq.com",
|
||||
"hobbies": [
|
||||
"test3"
|
||||
],
|
||||
"id": "bvhm0d5ath2d6oa90460",
|
||||
"name": "hello2"
|
||||
}
|
||||
],
|
||||
"mappings": {
|
||||
"infini-test": {
|
||||
"mappings": {
|
||||
"dynamic_templates": [
|
||||
{
|
||||
"strings": {
|
||||
"mapping": {
|
||||
"ignore_above": 256,
|
||||
"type": "keyword"
|
||||
},
|
||||
"match_mapping_type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"address": {
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"ignore_above": 256,
|
||||
"type": "keyword"
|
||||
}
|
||||
},
|
||||
"type": "text"
|
||||
},
|
||||
"age": {
|
||||
"type": "integer"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "date"
|
||||
},
|
||||
"email": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"hobbies": {
|
||||
"type": "text"
|
||||
},
|
||||
"id": {
|
||||
"ignore_above": 256,
|
||||
"type": "keyword"
|
||||
},
|
||||
"name": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"total": {
|
||||
"relation": "eq",
|
||||
"value": 11
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
import {queryData} from './data/doc';
|
||||
|
||||
function getUUID(len){
|
||||
len = len || 20;
|
||||
let chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
var maxPos = chars.length;
|
||||
var uuid = '';
|
||||
for (let i = 0; i < len; i++) {
|
||||
uuid += chars.charAt(Math.floor(Math.random() * maxPos));
|
||||
}
|
||||
return uuid;
|
||||
}
|
||||
|
||||
export default {
|
||||
'post /_search-center/doc/:index': function(req, res){
|
||||
switch(req.body.action){
|
||||
case 'SAVE':
|
||||
res.send({
|
||||
errno: "0",
|
||||
errmsg: ""
|
||||
});
|
||||
break;
|
||||
case 'ADD':
|
||||
res.send({
|
||||
errno: "0",
|
||||
errmsg: "",
|
||||
payload: {
|
||||
...req.body.payload,
|
||||
id: getUUID(),
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'DELETE':
|
||||
res.send({
|
||||
errno: "0"
|
||||
});
|
||||
break;
|
||||
default:
|
||||
res.send(queryData)
|
||||
}
|
||||
},
|
||||
'get /_search-center/indices/_cat': function(req, res){
|
||||
res.send({
|
||||
errno: "0",
|
||||
payload: ["infini-test"],
|
||||
});
|
||||
}
|
||||
}
|
|
@ -42,6 +42,7 @@
|
|||
"react-dom": "^16.5.1",
|
||||
"react-fittext": "^1.0.0",
|
||||
"react-highlight-words": "^0.16.0",
|
||||
"react-infinite-scroller": "^1.2.4",
|
||||
"react-json-prettify": "^0.2.0",
|
||||
"react-json-view": "^1.19.1",
|
||||
"react-router-dom": "^4.3.1",
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
import { Button, Dropdown, List, Spin, message, Icon } from 'antd';
|
||||
import React from 'react';
|
||||
import InfiniteScroll from 'react-infinite-scroller';
|
||||
import styles from './DropdownSelect.less';
|
||||
|
||||
class DropdownSelect extends React.Component{
|
||||
state={
|
||||
value: this.props.defaultValue,
|
||||
data: this.props.data || [],
|
||||
loading: false,
|
||||
hasMore: true,
|
||||
}
|
||||
|
||||
handleItemClick = (item)=>{
|
||||
let preValue = this.state.value;
|
||||
this.setState({
|
||||
value: item,
|
||||
},()=>{
|
||||
let onChange = this.props.onChange;
|
||||
if(preValue != item && onChange && typeof onChange == 'function'){
|
||||
onChange(item)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
let data = [];
|
||||
for(let i = 1; i<=28; i++){
|
||||
data.push('cluster'+i)
|
||||
}
|
||||
this.setState({
|
||||
data: data,
|
||||
})
|
||||
}
|
||||
fetchData = ()=>{
|
||||
let me = this;
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
let start = me.state.data.length;
|
||||
let data =[]
|
||||
for(let i = start + 1; i<start+11; i++){
|
||||
data.push('cluster'+i)
|
||||
}
|
||||
resolve(data)
|
||||
}, 2000)
|
||||
});
|
||||
}
|
||||
|
||||
handleInfiniteOnLoad = (page) => {
|
||||
let { data } = this.state;
|
||||
this.setState({
|
||||
loading: true,
|
||||
})
|
||||
if (data.length > 50) {
|
||||
message.warning('No more data');
|
||||
this.setState({
|
||||
hasMore: false,
|
||||
loading: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.fetchData().then((newdata)=>{
|
||||
data = data.concat(newdata);
|
||||
this.setState({
|
||||
data,
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
render(){
|
||||
let me = this;
|
||||
const menu = (<div className={styles.dropmenu}>
|
||||
<div className={styles.infiniteContainer}>
|
||||
<InfiniteScroll
|
||||
initialLoad={false}
|
||||
loadMore={this.handleInfiniteOnLoad}
|
||||
hasMore={!this.state.loading && this.state.hasMore}
|
||||
useWindow={false}
|
||||
>
|
||||
<List
|
||||
grid={{
|
||||
gutter: 8,
|
||||
column: 4,
|
||||
}}
|
||||
dataSource={this.state.data}
|
||||
renderItem={item => (
|
||||
<List.Item key={item}>
|
||||
<Button onClick={()=>{this.handleItemClick(item)}} className={styles.btnitem}>{item}</Button>
|
||||
</List.Item>
|
||||
)}
|
||||
>
|
||||
{this.state.loading && this.state.hasMore && (
|
||||
<div className={styles.loadingContainer}>
|
||||
<Spin />
|
||||
</div>
|
||||
)}
|
||||
</List>
|
||||
</InfiniteScroll>
|
||||
</div>
|
||||
{!this.state.loading && this.state.hasMore && (
|
||||
<div style={{textAlign:'center', marginTop: 10, color:'#ccc'}}>
|
||||
pull load more
|
||||
</div>
|
||||
)}
|
||||
</div>);
|
||||
return(
|
||||
<Dropdown overlay={menu} placement="bottomLeft">
|
||||
<Button className={styles['btn-ds']}>{this.state.value} <Icon style={{float:'right', marginTop:3}} type="caret-down"/></Button>
|
||||
</Dropdown>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default DropdownSelect;
|
|
@ -0,0 +1,42 @@
|
|||
.btn-ds, .btn-ds:hover{
|
||||
min-width: 150px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-bottom: 1px solid #333;
|
||||
text-align: left;
|
||||
transition: all .3s cubic-bezier(.645,.045,.355,1);
|
||||
border-radius: 0;
|
||||
margin-left: 15px;
|
||||
position: relative;
|
||||
bottom: 8px;
|
||||
span{
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.dropmenu{
|
||||
box-shadow: 0 0 15px 0 rgba(0, 0, 0, .15);
|
||||
padding: 20px;
|
||||
width: 500px;
|
||||
background: #fff;
|
||||
.item{
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
.btnitem{
|
||||
border-radius: 0px;
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
.infiniteContainer {
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
height: 300px;
|
||||
}
|
||||
.loadingContainer {
|
||||
position: absolute;
|
||||
bottom: 40px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
|
@ -4,6 +4,7 @@ import Link from 'umi/link';
|
|||
import Debounce from 'lodash-decorators/debounce';
|
||||
import styles from './index.less';
|
||||
import RightContent from './RightContent';
|
||||
import DropdownSelect from './DropdownSelect'
|
||||
|
||||
export default class GlobalHeader extends PureComponent {
|
||||
componentWillUnmount() {
|
||||
|
@ -36,6 +37,9 @@ export default class GlobalHeader extends PureComponent {
|
|||
type={collapsed ? 'menu-unfold' : 'menu-fold'}
|
||||
onClick={this.toggle}
|
||||
/>
|
||||
<DropdownSelect defaultValue="Select cluster"
|
||||
onChange={(item)=>{}}
|
||||
data={['cluster1', 'cluster2','cluster3', 'cluster4','cluster5', 'cluster6']}/>
|
||||
<RightContent {...this.props} />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
import React from 'react';
|
||||
import {Input, Menu, Dropdown} from 'antd';
|
||||
|
||||
class InputSelect extends React.Component{
|
||||
componentDidMount(){
|
||||
}
|
||||
constructor(props){
|
||||
// console.log('new: '+ props.id)
|
||||
super(props);
|
||||
this.state = {
|
||||
value: props.defaultValue || props.value,
|
||||
originData: props.data || [],
|
||||
}
|
||||
}
|
||||
|
||||
onClick = ({ key }) => {
|
||||
this.setState({
|
||||
value: key,
|
||||
})
|
||||
this.triggerChange(key)
|
||||
}
|
||||
triggerChange = (val)=>{
|
||||
let {onChange} = this.props;
|
||||
if(onChange && typeof onChange == 'function'){
|
||||
onChange(val)
|
||||
}
|
||||
}
|
||||
handleChange = (ev) => {
|
||||
let val = ev.target.value;
|
||||
let filterData = this.state.originData.slice();
|
||||
if(val != ""){
|
||||
filterData = filterData.filter(v=>v.value.includes(val))
|
||||
}
|
||||
this.setState({
|
||||
value: val,
|
||||
data: filterData
|
||||
})
|
||||
this.triggerChange(val);
|
||||
}
|
||||
render(){
|
||||
let {id, data, onChange, value, ...restProps} = this.props;
|
||||
let filterData = this.state.data || data || [];
|
||||
return (
|
||||
<Dropdown overlay={
|
||||
<Menu onClick={this.onClick} style={{maxHeight:'350px', overflow:"scroll"}}>
|
||||
{filterData.map(op =>(
|
||||
<Menu.Item key={op.value}>{op.label}</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
} trigger={['focus']}>
|
||||
<Input id={id} {...restProps} value={this.state.value} autoComplete="off" onChange={this.handleChange}/>
|
||||
</Dropdown>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default InputSelect;
|
|
@ -3,10 +3,11 @@ import { formatMessage, FormattedMessage } from 'umi/locale';
|
|||
import router from 'umi/router';
|
||||
import { connect } from 'dva';
|
||||
import { Col, Form, Row,Select, Input, Card,Icon, Table, InputNumber, Popconfirm,
|
||||
Divider,Button,Tooltip, Cascader, Modal, DatePicker, Dropdown,Menu, message } from 'antd';
|
||||
Divider,Button,Tooltip, Modal, DatePicker, message } from 'antd';
|
||||
import Editor, {monaco} from '@monaco-editor/react';
|
||||
import moment from 'moment';
|
||||
import {createDependencyProposals} from './autocomplete';
|
||||
import InputSelect from '@/components/infini/InputSelect';
|
||||
|
||||
function findParentIdentifier(textUntilPosition){
|
||||
let chars = textUntilPosition;
|
||||
|
@ -364,54 +365,6 @@ class EditableCell extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
class InputSelect extends React.Component{
|
||||
state = {
|
||||
value: this.props.defaultValue
|
||||
}
|
||||
onClick = ({ key }) => {
|
||||
this.inputEl.setState({
|
||||
value: key
|
||||
})
|
||||
this.setState({
|
||||
value: key,
|
||||
})
|
||||
this.triggerChange(key)
|
||||
}
|
||||
triggerChange = (val)=>{
|
||||
let {onChange} = this.props;
|
||||
if(onChange && typeof onChange == 'function'){
|
||||
onChange(val)
|
||||
}
|
||||
}
|
||||
handleChange = (ev) => {
|
||||
let val = ev.target.value;
|
||||
let filterData = this.props.data.slice();
|
||||
if(val != ""){
|
||||
filterData = filterData.filter(v=>v.value.includes(val))
|
||||
}
|
||||
this.setState({
|
||||
value: val,
|
||||
data: filterData
|
||||
})
|
||||
this.triggerChange(val);
|
||||
}
|
||||
render(){
|
||||
let {data, onChange, ...restProps} = this.props;
|
||||
let filterData = this.state.data || data;
|
||||
return (
|
||||
<Dropdown overlay={
|
||||
<Menu onClick={this.onClick} style={{maxHeight:'350px', overflow:"scroll"}}>
|
||||
{filterData.map(op =>(
|
||||
<Menu.Item key={op.value}>{op.label}</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
} trigger={['focus']}>
|
||||
<Input ref={el=>{this.inputEl=el}} {...restProps} onChange={this.handleChange}/>
|
||||
</Dropdown>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@connect(({document})=>({
|
||||
document
|
||||
}))
|
||||
|
@ -440,14 +393,20 @@ class Doucment extends React.Component {
|
|||
componentDidMount(){
|
||||
const {location, dispatch } = this.props;
|
||||
//console.log(match, location);
|
||||
let index = location.query.index || 'infini-test';
|
||||
let index = location.query.index;
|
||||
let cluster = location.query.cluster || 'single-es';
|
||||
if(!cluster){
|
||||
return
|
||||
}
|
||||
dispatch({
|
||||
type: 'document/fetchIndices',
|
||||
payload: {
|
||||
cluster,
|
||||
}
|
||||
}).then(()=>{
|
||||
if(!index){
|
||||
return
|
||||
}
|
||||
this.fetchData({
|
||||
pageSize: 10,
|
||||
pageIndex: 1,
|
||||
|
@ -559,7 +518,7 @@ class Doucment extends React.Component {
|
|||
})
|
||||
const clusters = ["single-es"];
|
||||
let {cluster, index}= this.props.document;
|
||||
cluster = cluster || this.props.location.query.cluster;
|
||||
cluster = cluster || this.props.location.query.cluster || 'single-es';
|
||||
index = index || this.props.location.query.index;
|
||||
return (
|
||||
<div>
|
||||
|
@ -620,14 +579,13 @@ class Doucment extends React.Component {
|
|||
<div style={{fontSize: 12, paddingLeft: 20}}>
|
||||
<div style={{fontSize: 16, paddingBottom: 10, color: '#1890FF'}}>query example:</div>
|
||||
<div style={{background:'rgb(245, 247, 250)', padding: 10}}>
|
||||
<code dangerouslySetInnerHTML={{ __html: `{<br/> "bool":{<br/>
|
||||
"must": {<br/>
|
||||
"match":{ <br/>
|
||||
"FIELD": "VALUE"<br/>
|
||||
}<br/>
|
||||
}<br/>
|
||||
}<br/>}` }}>
|
||||
</code>
|
||||
<pre className="language-json">{JSON.stringify({
|
||||
must: {
|
||||
match: {
|
||||
FIELD: "VALUE"
|
||||
}
|
||||
}
|
||||
}, null, 2)}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
|
|
|
@ -1,20 +1,43 @@
|
|||
import React, { Component } from 'react';
|
||||
import { connect } from 'dva';
|
||||
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
|
||||
import {Steps, Card, Form, Select, Input,Button, Divider,message,Spin, Row, Col,Result} from 'antd';
|
||||
import {Steps, Card, Form, Select, Input,Button, Divider,message, InputNumber} from 'antd';
|
||||
import InputSelect from '@/components/infini/InputSelect';
|
||||
|
||||
const {Step} = Steps;
|
||||
const {Option} = Select;
|
||||
const {TextArea} = Input;
|
||||
|
||||
@Form.create()
|
||||
@connect()
|
||||
@connect(({document}) => ({
|
||||
document
|
||||
}))
|
||||
class Rebuild extends Component {
|
||||
state = {
|
||||
currentStep: 0,
|
||||
configData: {},
|
||||
configData: {
|
||||
source:{},
|
||||
dest:{},
|
||||
},
|
||||
}
|
||||
renderSteps(currentStep){
|
||||
componentDidMount(){
|
||||
const {dispatch} = this.props;
|
||||
dispatch({
|
||||
type:'document/fetchIndices',
|
||||
payload:{
|
||||
cluster: 'sinlge-es'
|
||||
}
|
||||
})
|
||||
}
|
||||
renderSteps = (currentStep) => {
|
||||
let {clusterIndices} = this.props.document;
|
||||
clusterIndices = clusterIndices || [];
|
||||
let indices = clusterIndices.map((item)=>{
|
||||
return {
|
||||
label: item,
|
||||
value: item,
|
||||
}
|
||||
});
|
||||
var stepDom = '';
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
const formItemLayout = {
|
||||
|
@ -28,6 +51,9 @@ class Rebuild extends Component {
|
|||
sm: { span: 16 },
|
||||
md:{span:20},
|
||||
},
|
||||
style: {
|
||||
marginBottom: 10,
|
||||
}
|
||||
};
|
||||
const tailFormItemLayout = {
|
||||
wrapperCol: {
|
||||
|
@ -49,35 +75,23 @@ class Rebuild extends Component {
|
|||
case 0:
|
||||
stepDom = (
|
||||
<div style={{marginTop:20}}>
|
||||
<Form.Item {...formItemLayout} label="选择源索引">
|
||||
{getFieldDecorator('source_index', {
|
||||
initialValue: this.state.configData.source_index,
|
||||
rules: [{ required: true, message: '请选择要重建的索引' }],
|
||||
<Form.Item {...formItemLayout} label="Task Name">
|
||||
{getFieldDecorator('name', {
|
||||
initialValue: this.state.configData.name,
|
||||
rules: [{ required: true, message: 'please input a task name' }],
|
||||
})(
|
||||
<Select
|
||||
showSearch
|
||||
style={{ width: 200 }}
|
||||
optionFilterProp="children"
|
||||
filterOption={(input, option) =>
|
||||
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
>
|
||||
<Option value="logs">logs</Option>
|
||||
<Option value="blogs">blogs</Option>
|
||||
<Option value="filebeat">filebeat</Option>
|
||||
</Select>,
|
||||
<Input autoComplete="off" style={{width: 200}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item {...formItemLayout} label="过滤条件">
|
||||
{getFieldDecorator('creterial', {
|
||||
<Form.Item {...formItemLayout} label="Task Description">
|
||||
{getFieldDecorator('desc', {
|
||||
initialValue: this.state.configData.creterial,
|
||||
rules: [
|
||||
{required: true, },
|
||||
],
|
||||
})(
|
||||
<TextArea
|
||||
style={{ width: '80%' }}
|
||||
rows={8}
|
||||
rows={6}
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
|
@ -87,28 +101,82 @@ class Rebuild extends Component {
|
|||
</div>
|
||||
)
|
||||
break;
|
||||
case 1:
|
||||
stepDom = (
|
||||
<div style={{marginTop:20}}>
|
||||
<Form.Item {...formItemLayout} label="目标索引名">
|
||||
{getFieldDecorator('target_index', {
|
||||
initialValue: this.state.configData.target_index,
|
||||
rules: [{ required: true, message: '请输入目标索引名称' }],
|
||||
case 1:
|
||||
stepDom = (
|
||||
<div style={{marginTop:20}}>
|
||||
<Form.Item {...formItemLayout} label="选择源索引">
|
||||
{getFieldDecorator('source_index', {
|
||||
initialValue: this.state.configData.source.index,
|
||||
rules: [{ required: true, message: '请选择要重建的索引' }],
|
||||
})(
|
||||
<InputSelect data={indices} style={{width: 200}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item {...formItemLayout} label="Query">
|
||||
{getFieldDecorator('source_query', {
|
||||
initialValue: this.state.configData.source.query,
|
||||
rules: [
|
||||
{required: true, },
|
||||
],
|
||||
})(
|
||||
<Input style={{width:200}} />
|
||||
<TextArea
|
||||
style={{ width: '80%' }}
|
||||
rows={6}
|
||||
/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item {...formItemLayout} label="目标索引设置">
|
||||
{getFieldDecorator('target_setting', {
|
||||
initialValue: this.state.configData.target_setting,
|
||||
<Form.Item {...formItemLayout} label="max_docs">
|
||||
{getFieldDecorator('source_max_docs', {
|
||||
initialValue: this.state.configData.source_max_docs,
|
||||
rules: [
|
||||
],
|
||||
})(
|
||||
<InputNumber min={1} style={{width:200}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item {...formItemLayout} label="_source">
|
||||
{getFieldDecorator('source__source', {
|
||||
initialValue: this.state.configData.source__source,
|
||||
rules: [
|
||||
],
|
||||
})(
|
||||
<Input style={{width:'50%'}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item {...formItemLayout} label="sort">
|
||||
{getFieldDecorator('source_sort', {
|
||||
initialValue: this.state.configData.source_sort,
|
||||
rules: [
|
||||
],
|
||||
})(
|
||||
<Input style={{width:'50%'}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item {...tailFormItemLayout}>
|
||||
{this.renderFooter(currentStep)}
|
||||
</Form.Item>
|
||||
</div>
|
||||
)
|
||||
break;
|
||||
case 2:
|
||||
stepDom = (
|
||||
<div style={{marginTop:20}}>
|
||||
<span style={{height:1}}/>
|
||||
<Form.Item {...formItemLayout} label="目标索引名">
|
||||
{getFieldDecorator('dest_index', {
|
||||
initialValue: this.state.configData.dest.index || '',
|
||||
rules: [{ required: true, message: '请输入目标索引名称' }],
|
||||
})(
|
||||
<InputSelect data={indices} style={{width: 200}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item {...formItemLayout} label="Pipeline">
|
||||
{getFieldDecorator('dest_pipeline', {
|
||||
initialValue: this.state.configData.dest.source,
|
||||
rules: [
|
||||
{required: true, },
|
||||
],
|
||||
})(
|
||||
<TextArea
|
||||
style={{ width: '80%' }}
|
||||
rows={8}
|
||||
/>
|
||||
<Input style={{width:200}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item {...tailFormItemLayout}>
|
||||
|
@ -117,58 +185,52 @@ class Rebuild extends Component {
|
|||
</div>
|
||||
)
|
||||
break;
|
||||
case 2:
|
||||
stepDom = (
|
||||
<div>
|
||||
<Spin tip="数据正在重建,请稍等...">
|
||||
<div style={{width:'100%', height: 200}}></div>
|
||||
</Spin>
|
||||
<div style={{textAlign:'center',}}>
|
||||
{this.renderFooter(currentStep)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
break;
|
||||
case 3:
|
||||
stepDom = (<Result
|
||||
status="success"
|
||||
title="数据重建成功"
|
||||
subTitle=""
|
||||
extra={[
|
||||
// <Button type="primary" key="console">
|
||||
// </Button>,
|
||||
<Button key="continue">继续重建</Button>,
|
||||
]}
|
||||
/>)
|
||||
break;
|
||||
}
|
||||
return stepDom;
|
||||
}
|
||||
|
||||
handleNext(currentStep){
|
||||
const { form } = this.props;
|
||||
const { form, dispatch } = this.props;
|
||||
const { configData: oldValue } = this.state;
|
||||
var formValues = {};
|
||||
form.validateFields((err, fieldsValue) => {
|
||||
if (err) return;
|
||||
switch(currentStep){
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
|
||||
break;
|
||||
let newValue = {};
|
||||
if(fieldsValue['source_query']){
|
||||
fieldsValue['source_query'] = JSON.parse(fieldsValue['source_query'])
|
||||
}
|
||||
formValues = fieldsValue;
|
||||
});
|
||||
this.setState({
|
||||
configData: {
|
||||
...oldValue,
|
||||
...formValues,
|
||||
},
|
||||
currentStep: currentStep+1
|
||||
},()=>{
|
||||
message.info(JSON.stringify(this.state));
|
||||
for(let key in fieldsValue){
|
||||
if(key.startsWith('source_')){
|
||||
!newValue.source && (newValue.source ={})
|
||||
newValue.source[key.slice(7)] = fieldsValue[key]
|
||||
}else if(key.startsWith('dest_')){
|
||||
!newValue.dest && (newValue.dest ={})
|
||||
newValue.dest[key.slice(5)] = fieldsValue[key]
|
||||
}else{
|
||||
newValue[key] = fieldsValue[key];
|
||||
}
|
||||
}
|
||||
if(currentStep == 2){
|
||||
currentStep = 1;
|
||||
dispatch({
|
||||
type: 'rebuild/addTask',
|
||||
payload: {
|
||||
...oldValue,
|
||||
...newValue,
|
||||
}
|
||||
})
|
||||
}
|
||||
this.setState({
|
||||
configData: {
|
||||
...oldValue,
|
||||
...newValue,
|
||||
},
|
||||
currentStep: currentStep+1
|
||||
},()=>{
|
||||
message.info(JSON.stringify(this.state));
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
backward(currentStep){
|
||||
if(currentStep > 0){
|
||||
|
@ -179,7 +241,7 @@ class Rebuild extends Component {
|
|||
});
|
||||
}
|
||||
renderFooter = currentStep => {
|
||||
if (currentStep === 1 || currentStep ==2) {
|
||||
if (currentStep === 1) {
|
||||
return [
|
||||
<Button key="back" onClick={()=>this.backward(currentStep)}>
|
||||
上一步
|
||||
|
@ -192,7 +254,7 @@ class Rebuild extends Component {
|
|||
</Button>,
|
||||
];
|
||||
}
|
||||
if (currentStep === 3) {
|
||||
if (currentStep === 2) {
|
||||
return [
|
||||
<Button key="back" style={{ float: 'left' }} onClick={()=>this.backward(currentStep)}>
|
||||
上一步
|
||||
|
@ -200,7 +262,7 @@ class Rebuild extends Component {
|
|||
<Button key="cancel" style={{margin:'auto 2em'}} onClick={() => {}}>
|
||||
取消
|
||||
</Button>,
|
||||
<Button key="submit" type="primary" onClick={() =>{}}>
|
||||
<Button key="submit" type="primary" onClick={() =>this.handleNext(currentStep)}>
|
||||
完成
|
||||
</Button>,
|
||||
];
|
||||
|
@ -219,10 +281,9 @@ class Rebuild extends Component {
|
|||
<PageHeaderWrapper >
|
||||
<Card>
|
||||
<Steps current={this.state.currentStep}>
|
||||
<Step title="基本信息" />
|
||||
<Step title="源索引信息" />
|
||||
<Step title="目标索引信息" />
|
||||
<Step title="数据重建中" subTitle="剩余 00:00:08" />
|
||||
<Step title="重建结果" />
|
||||
</Steps>
|
||||
<Divider/>
|
||||
{this.renderSteps(this.state.currentStep)}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import {reindex} from '@/services/rebuild';
|
||||
import { message } from 'antd';
|
||||
|
||||
export default {
|
||||
namespace: 'rebuild',
|
||||
state: {},
|
||||
effects:{
|
||||
*addTask({payload}, {call, put}){
|
||||
let resp = yield call(reindex, payload);
|
||||
console.log(resp);
|
||||
if(resp.errno != "0"){
|
||||
message.warn("rebuild failed")
|
||||
return
|
||||
}
|
||||
}
|
||||
},
|
||||
reducers: {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export const pathPrefix = '/_search-center';
|
|
@ -1,7 +1,8 @@
|
|||
import request from '@/utils/request';
|
||||
import {pathPrefix} from './common';
|
||||
|
||||
export async function getDocList(params) {
|
||||
return request(`/api/doc/${params.index}`, {
|
||||
return request(`${pathPrefix}/doc/${params.index}`, {
|
||||
method: 'POST',
|
||||
body: {
|
||||
action: 'SEARCH',
|
||||
|
@ -11,7 +12,7 @@ export async function getDocList(params) {
|
|||
}
|
||||
|
||||
export async function saveDoc(params) {
|
||||
return request(`/api/doc/${params.index}`, {
|
||||
return request(`${pathPrefix}/doc/${params.index}`, {
|
||||
method: 'POST',
|
||||
body: {
|
||||
payload: params.data,
|
||||
|
@ -22,7 +23,7 @@ export async function saveDoc(params) {
|
|||
}
|
||||
|
||||
export async function deleteDoc(params) {
|
||||
return request(`/api/doc/${params.index}`, {
|
||||
return request(`${pathPrefix}/doc/${params.index}`, {
|
||||
method: 'POST',
|
||||
body: {
|
||||
index: params.index,
|
||||
|
@ -33,7 +34,7 @@ export async function deleteDoc(params) {
|
|||
}
|
||||
|
||||
export async function addDoc(params) {
|
||||
return request(`/api/doc/${params.index}`, {
|
||||
return request(`${pathPrefix}/doc/${params.index}`, {
|
||||
method: 'POST',
|
||||
body: {
|
||||
payload: params.data,
|
||||
|
@ -44,7 +45,7 @@ export async function addDoc(params) {
|
|||
}
|
||||
|
||||
export async function getIndices(params) {
|
||||
return request(`/api/indices/_cat`, {
|
||||
return request(`${pathPrefix}/indices/_cat`, {
|
||||
method: 'GET'
|
||||
});
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import request from '@/utils/request';
|
||||
import {pathPrefix} from './common';
|
||||
|
||||
export async function reindex(payload){
|
||||
let url = `${pathPrefix}/rebuild/_create`;
|
||||
return request(url,{
|
||||
method: 'POST',
|
||||
body: payload,
|
||||
expirys: 0,
|
||||
});
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
import request from '@/utils/request';
|
||||
import {pathPrefix} from './common';
|
||||
|
||||
export async function getDictList(payload){
|
||||
let url = `/api/dict/_search?from=${payload.from}&size=${payload.size}`;
|
||||
let url = `${pathPrefix}/dict/_search?from=${payload.from}&size=${payload.size}`;
|
||||
payload.name && (url+= `&name=${payload.name}`);
|
||||
payload.tags && (url+=`&tags=${payload.tags}`);
|
||||
return request(url,{
|
||||
|
@ -12,7 +13,7 @@ export async function getDictList(payload){
|
|||
}
|
||||
|
||||
export async function addDict(payload){
|
||||
return request('/api/dict/_create',{
|
||||
return request(`${pathPrefix}/dict/_create`,{
|
||||
method: 'POST',
|
||||
body: payload,
|
||||
expirys: 0,
|
||||
|
@ -20,14 +21,14 @@ export async function addDict(payload){
|
|||
}
|
||||
|
||||
export async function deleteDict(payload){
|
||||
return request(`/api/dict/${payload.id}`,{
|
||||
return request(`${pathPrefix}/dict/${payload.id}`,{
|
||||
method: 'DELETE',
|
||||
expirys: 0,
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateDict(payload){
|
||||
return request('/api/dict/_update',{
|
||||
return request(`${pathPrefix}/dict/_update`,{
|
||||
method: 'POST',
|
||||
body: payload,
|
||||
expirys: 0,
|
||||
|
|
Loading…
Reference in New Issue