add global cluster select and init rebuild

This commit is contained in:
silenceqi 2021-01-02 23:22:35 +08:00
parent 9fd1d490ac
commit e44729fcb8
21 changed files with 770 additions and 164 deletions

View File

@ -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

View File

@ -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
}

View File

@ -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)
}

View File

@ -70,6 +70,7 @@ func main() {
}, func() {
orm.RegisterSchema(model.Dict{})
orm.RegisterSchema(model.InfiniReindex{})
})
}

24
model/reindex.go Normal file
View File

@ -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
View File

@ -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()))

View File

@ -68,7 +68,7 @@ export default {
// },
// },
proxy: {
'/api/': {
'/_search-center/': {
target: 'http://localhost:2900',
changeOrigin: true,
// pathRewrite: { '^/server': '' },

View File

@ -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
}
}
}

View File

@ -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"],
});
}
}

View File

@ -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",

View File

@ -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;

View File

@ -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;
}

View File

@ -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>
);

View File

@ -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;

View File

@ -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/>&nbsp;&nbsp;"bool":{<br/>
&nbsp;&nbsp;&nbsp;&nbsp;"must": {<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"match":{ <br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"FIELD": "VALUE"<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br/>
&nbsp;&nbsp;&nbsp;&nbsp;}<br/>
&nbsp;&nbsp;}<br/>}` }}>
</code>
<pre className="language-json">{JSON.stringify({
must: {
match: {
FIELD: "VALUE"
}
}
}, null, 2)}</pre>
</div>
</div>
</Col>

View File

@ -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)}

View File

@ -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: {
}
}

View File

@ -0,0 +1 @@
export const pathPrefix = '/_search-center';

View File

@ -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'
});
}

View File

@ -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,
});
}

View File

@ -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,