es:
address: http://127.0.0.1:9200
username: elastic
password: test
index: elastic-test-20220402
package conn
import (
"github.com/olivere/elastic"
"github.com/spf13/viper"
"log"
"os"
"time"
)
func init() {
// 读取yaml文件
// config := viper.New() // 通过New加载配置则只能用其返回值获取配置
config := viper.GetViper() // 全局加载配置, 可在任意位置获取配置
config.AddConfigPath("./") //设置读取的文件路径
config.SetConfigName("app") //设置读取的文件名
config.SetConfigType("yaml")
if err := config.ReadInConfig(); err != nil {
panic(err)
}
}
var con *elastic.Client
func init() {
// 初始化es连接
options := []elastic.ClientOptionFunc{
elastic.SetURL(viper.GetString("es.address")),
elastic.SetSniff(viper.GetBool("es.sniff")),
elastic.SetHealthcheckInterval(10 * time.Second),
elastic.SetGzip(viper.GetBool("es.gzip")),
elastic.SetErrorLog(log.New(os.Stderr, "ELASTIC ", log.LstdFlags)),
elastic.SetInfoLog(log.New(os.Stdout, "", log.LstdFlags)),
elastic.SetBasicAuth(viper.GetString("es.username"), viper.GetString("es.password")),
}
var err error
con, err = elastic.NewClient(options...)
if err != nil {
panic(err)
}
}
func Es() *elastic.Client {
return con
}
package main
import (
"app/conn"
"github.com/goperate/es/basics"
"github.com/spf13/viper"
)
type TestForm struct {
Id int `json:"id"`
}
func main() {
obj := basics.NewStructToEsQuery()
req := conn.Es().Search().Index(viper.GetString("es.index"))
form := new(TestForm)
_, _ = obj.Search(req, form)
}
对应的es查询json如下
{
"query": {
"bool": {
"must": {
"term": {
"id": 0
}
}
}
}
}
我们发现在未对form赋值的情况下会出现一条id为0的查询, 那么我们如何避免这种情况呢
package main
type TestForm struct {
Id *int `json:"id"`
Id2 []int `json:"id2" field:"id"`
Id3 basics.ArrayInt `json:"id3" field:"id"`
}
我们使用上述几种类型则不会出现不赋值也被解析的情况
basics.ArrayInt的优点: 当你需要把json字符串解析为结构体时它可以解析, 字符串, 数字, 数组甚至逗号分割的字符串
例如: 1, “1”, [1, 2], [“1”, “2”], “1,2”
package main
func main() {
obj := basics.NewStructToEsQuery()
req := conn.Es().Search().Index(viper.GetString("es.index"))
form := new(TestForm)
jsonStr := "{\"id\": 1, \"id2\": [10, 20], \"id3\": 30}"
_ = json.Unmarshal([]byte(jsonStr), form)
_, _ = obj.Search(req, form)
}
{
"query": {
"bool": {
"must": [
{
"term": {
"id": 1
}
},
{
"terms": {
"id": [
10,
20
]
}
},
{
"term": {
"id": 30
}
}
]
}
}
}
tag: field 指定查询es中对应的字段, 取值优先级: fields > field > json > 字段名
package main
type TestForm struct {
Id *int `json:"id"`
Id2 []int `json:"id2" field:"id" es:"range"`
Id3 basics.ArrayInt `json:"id3" field:"id" es:"relational:range"`
}
{
"query": {
"bool": {
"must": [
{
"term": {
"id": 1
}
},
{
"range": {
"id": {
"from": 10,
"include_lower": true,
"include_upper": true,
"to": 20
}
}
},
{
"range": {
"id": {
"from": 30,
"include_lower": true,
"include_upper": true,
"to": null
}
}
}
]
}
}
}
package main
type TestForm struct {
Name *string `json:"name" es:"relational:match"`
Name2 basics.ArrayString `json:"name2" field:"name" es:"relational:matchAnd"`
}
{
"query": {
"bool": {
"must": [
{
"match": {
"name": {
"query": "测试"
}
}
},
{
"match": {
"name": {
"operator": "and",
"query": "测试"
}
}
}
]
}
}
}
package main
type TestForm struct {
Id *int `json:"id"`
Name *string `json:"name" es:"relational:match"`
}
// 同
type TestForm struct {
Id *int `json:"id" es:"must"`
Name *string `json:"name" es:"must;relational:match"`
}
// 同
type TestForm struct {
Id *int `json:"id" es:"logical:must"`
Name *string `json:"name" es:"logical:must;relational:match"`
}
{
"query": {
"bool": {
"must": [
{
"term": {
"id": 100
}
},
{
"match": {
"name": {
"query": "测试"
}
}
}
]
}
}
}
多个es tag使用 ; 分割, 只有must时可忽略不写
package main
type TestForm struct {
Id *int `json:"id" es:"not"`
Name *string `json:"name" es:"logical:not;relational:match"`
}
{
"query": {
"bool": {
"must_not": [
{
"term": {
"id": 100
}
},
{
"match": {
"name": {
"query": "测试"
}
}
}
]
}
}
}
package main
type TestForm struct {
Id *int `json:"id" es:"should"`
Name *string `json:"name" es:"logical:should;relational:match"`
}
{
"query": {
"bool": {
"should": [
{
"term": {
"id": 100
}
},
{
"match": {
"name": {
"query": "测试"
}
}
}
]
}
}
}
package main
type TestForm struct {
Id *int `json:"id" es:"filter"`
Name *string `json:"name" es:"logical:filter;relational:match"`
}
{
"query": {
"bool": {
"filter": [
{
"term": {
"id": 100
}
},
{
"match": {
"name": {
"query": "测试"
}
}
}
]
}
}
}
package main
type TestForm struct {
Id *int `json:"id" es:"must"`
Id2 *int `json:"id2" es:"filter"`
Id3 *int `json:"id3" es:"not"`
Name *string `json:"name" es:"should;relational:match"`
}
{
"query": {
"bool": {
"filter": {
"term": {
"id2": 200
}
},
"must": {
"term": {
"id": 100
}
},
"must_not": {
"term": {
"id3": 300
}
},
"should": {
"match": {
"name": {
"query": "测试"
}
}
}
}
}
}
package main
type TestForm struct {
Id *int `json:"id" es:"must"`
Id2 *int `json:"id2" es:"logical:must,should"`
Id3 *int `json:"id3" es:"logical:must,should"`
Id4 *int `json:"id4" es:"logical:must,not"`
Id5 *int `json:"id5" es:"logical:must,not"`
}
{
"query": {
"bool": {
"must": [
{
"term": {
"id": 100
}
},
{
"bool": {
"must_not": [
{
"term": {
"id4": 400
}
},
{
"term": {
"id5": 500
}
}
],
"should": [
{
"term": {
"id2": 200
}
},
{
"term": {
"id3": 300
}
}
]
}
}
]
}
}
}
嵌套时首层的must不能省略, logical不能省略
package main
type TestForm struct {
Id *int `json:"id" es:"must"`
Id2 *int `json:"id2" es:"logical:must@a,should"`
Id3 *int `json:"id3" es:"logical:must@a,should"`
Id4 *int `json:"id4" es:"logical:must@b,not"`
Id5 *int `json:"id5" es:"logical:must@b,not"`
}
{
"query": {
"bool": {
"must": [
{
"term": {
"id": 100
}
},
{
"bool": {
"should": [
{
"term": {
"id2": 200
}
},
{
"term": {
"id3": 300
}
}
]
}
},
{
"bool": {
"must_not": [
{
"term": {
"id5": 500
}
},
{
"term": {
"id4": 400
}
}
]
}
}
]
}
}
}
同一组的会做为一个整体
package main
type TestForm struct {
Id *int `json:"id" es:"must" field:"object.id"`
Id2 *int `json:"id2" es:"must"`
Object struct {
Id3 *int `json:"id3" es:"must"`
Id4 *int `json:"id4" es:"must"`
Id5 *int `json:"id5" es:"must"`
} `json:"object" es:"obj"`
}
// 或
type Object struct {
Id3 *int `json:"id3" es:"must"`
Id4 *int `json:"id4" es:"must"`
Id5 *int `json:"id5" es:"must"`
}
type TestForm struct {
Id *int `json:"id" es:"must" field:"object.id"`
Id2 *int `json:"id2" es:"must"`
Object *Object `json:"object" es:"obj"`
}
{
"query": {
"bool": {
"must": [
{
"term": {
"object.id": 100
}
},
{
"term": {
"id2": 200
}
},
{
"bool": {
"must": [
{
"term": {
"object.id3": 300
}
},
{
"term": {
"object.id4": 400
}
},
{
"term": {
"object.id5": 500
}
}
]
}
}
]
}
}
}
package main
type Nested struct {
Id3 *int `json:"id3" es:"must"`
Id4 *int `json:"id4" es:"must"`
Id5 *int `json:"id5" es:"must"`
}
type TestForm struct {
Id *int `json:"id" es:"logical:must,nested@nested_data,must" field:"id"`
Id2 *int `json:"id2" es:"must"`
NestedData *Nested `json:"nested_data" es:"nested"`
}
{
"query": {
"bool": {
"must": [
{
"nested": {
"path": "nested_data",
"query": {
"bool": {
"must": {
"term": {
"nested_data.id": 100
}
}
}
}
}
},
{
"term": {
"id2": 200
}
},
{
"nested": {
"path": "nested_data",
"query": {
"bool": {
"must": [
{
"term": {
"nested_data.id3": 300
}
},
{
"term": {
"nested_data.id4": 400
}
},
{
"term": {
"nested_data.id5": 500
}
}
]
}
}
}
}
]
}
}
}
当前版本logical中的nested无法跟nested结构体合并
package main
import (
"app/conn"
"encoding/json"
"github.com/goperate/es/basics"
"github.com/spf13/viper"
)
type Nested struct {
Id3 *int `json:"id3" es:"must"`
Id4 *int `json:"id4" es:"must"`
Id5 *int `json:"id5" es:"must"`
Inner *basics.EsSelect `json:"inner" es:"nesting:innerHits"`
}
type TestForm struct {
Id *int `json:"id" es:"logical:must,nested@nested_data2,must" field:"id"`
NestedData2Inner *basics.EsSelect `json:"nested_data2_inner" es:"logical:must,nested@nested_data2;nesting:innerHits"`
Id2 *int `json:"id2" es:"must"`
NestedData *Nested `json:"nested_data" es:"nested"`
}
func main() {
obj := basics.NewStructToEsQuery()
req := conn.Es().Search().Index(viper.GetString("es.index"))
form := new(TestForm)
jsonStr := "{\"id\": 100, \"nested_data2_inner\": {}, \"id2\": 200, \"nested_data\": {\"id3\": 300, \"id4\": 400, \"id5\": 500, \"inner\": {\"page\": 2, \"size\": 20, \"include\": [\"aaa\"], \"exclude\": [\"bbb\"]}}}"
_ = json.Unmarshal([]byte(jsonStr), form)
_, _ = obj.Search(req, form)
}
{
"query": {
"bool": {
"must": [
{
"nested": {
"inner_hits": {
"from": 0,
"size": 100
},
"path": "nested_data2",
"query": {
"bool": {
"must": {
"term": {
"nested_data2.id": 100
}
}
}
}
}
},
{
"term": {
"id2": 200
}
},
{
"nested": {
"inner_hits": {
"_source": {
"excludes": [
"bbb"
],
"includes": [
"aaa"
]
},
"from": 20,
"size": 20
},
"path": "nested_data",
"query": {
"bool": {
"must": [
{
"term": {
"nested_data.id3": 300
}
},
{
"term": {
"nested_data.id4": 400
}
},
{
"term": {
"nested_data.id5": 500
}
}
]
}
}
}
}
]
}
}
}
package main
type TestForm struct {
Id *int `json:"id" es:"should" fields:"id,id2"`
}
{
"query": {
"bool": {
"should": [
{
"term": {
"id": 100
}
},
{
"term": {
"id2": 100
}
}
]
}
}
}
package main
import (
"app/conn"
"encoding/json"
"github.com/goperate/es/basics"
"github.com/spf13/viper"
)
type TestForm struct {
Id *int `json:"id"`
IdSort *int `json:"id_sort" es:"sort" field:"id"`
}
func main() {
obj := basics.NewStructToEsQuery()
req := conn.Es().Search().Index(viper.GetString("es.index"))
form := new(TestForm)
jsonStr := "{\"id\": 100, \"id_sort\": 2}"
_ = json.Unmarshal([]byte(jsonStr), form)
_, _ = obj.Search(req, form)
}
{
"query": {
"bool": {
"must": {
"term": {
"id": 100
}
}
}
},
"sort": [
{
"id": {
"order": "asc"
}
}
]
}
sort字段的值为2时表示升序排序, 其它任何值为降序排序(建议使用1, 后续升级可能固定为1)
package main
import (
"app/conn"
"encoding/json"
"github.com/goperate/es/basics"
"github.com/spf13/viper"
)
type TestForm struct {
Id []int `json:"id"`
IdSort []int `json:"id_sort" es:"sort:val" field:"id"`
}
func main() {
obj := basics.NewStructToEsQuery()
req := conn.Es().Search().Index(viper.GetString("es.index"))
form := new(TestForm)
jsonStr := "{\"id\": [100, 200], \"id_sort\": [100, 200]}"
_ = json.Unmarshal([]byte(jsonStr), form)
_, _ = obj.Search(req, form)
}
{
"query": {
"bool": {
"must": {
"terms": {
"id": [
100,
200
]
}
}
}
},
"sort": [
{
"_script": {
"order": "asc",
"script": {
"params": {
"idMap": {
"100": 0,
"200": 1
}
},
"source": "params.idMap[String.valueOf(doc['id'].value)]"
},
"type": "string"
}
}
]
}
package main
import (
"app/conn"
"encoding/json"
"github.com/goperate/es/basics"
"github.com/spf13/viper"
)
type TestForm struct {
Id []int `json:"id"`
NestedSort struct {
StatusSort basics.ArrayInt `json:"status_sort" es:"sort;mode:min" field:"status"`
Status basics.ArrayInt `json:"status"`
} `json:"nested_sort" es:"sort:nested"`
}
func main() {
obj := basics.NewStructToEsQuery()
req := conn.Es().Search().Index(viper.GetString("es.index"))
form := new(TestForm)
jsonStr := "{\"id\": [100, 200], \"nested_sort\": {\"status\": 6, \"status_sort\": 2}}"
_ = json.Unmarshal([]byte(jsonStr), form)
_, _ = obj.Search(req, form)
}
{
"query": {
"bool": {
"must": {
"terms": {
"id": [
100,
200
]
}
}
}
},
"sort": [
{
"nested_sort.status": {
"mode": "min",
"nested": {
"filter": {
"bool": {
"must": {
"term": {
"nested_sort.status": 6
}
}
}
},
"path": "nested_sort"
},
"order": "asc"
}
}
]
}
package main
import (
"app/conn"
"encoding/json"
"github.com/goperate/es/basics"
"github.com/spf13/viper"
)
type TestForm struct {
Id *int `json:"id"`
IdSort []int `json:"id_sort" es:"sort;level:2" field:"id"`
StatusSort *int `json:"status_sort" es:"sort;level:1" field:"status"`
}
func main() {
obj := basics.NewStructToEsQuery()
req := conn.Es().Search().Index(viper.GetString("es.index"))
form := new(TestForm)
jsonStr := "{\"id\": 100, \"id_sort\": [2], \"status_sort\": 2}"
_ = json.Unmarshal([]byte(jsonStr), form)
_, _ = obj.Search(req, form)
}
{
"query": {
"bool": {
"must": {
"term": {
"id": 100
}
}
}
},
"sort": [
{
"status": {
"order": "asc"
}
},
{
"id": {
"order": "asc"
}
}
]
}
多字段排序时可使用level指定字段顺序
package main
import (
"app/conn"
"encoding/json"
"github.com/goperate/es/basics"
"github.com/spf13/viper"
)
type TestForm struct {
Id []int `json:"id"`
basics.EsSelect `es:"innerHits"`
}
func main() {
obj := basics.NewStructToEsQuery()
req := conn.Es().Search().Index(viper.GetString("es.index"))
form := new(TestForm)
jsonStr := "{\"id\": [100, 200], \"page\": 2, \"size\": 20, \"include\": [\"aaa\"], \"exclude\": [\"bbb\"]}"
_ = json.Unmarshal([]byte(jsonStr), form)
_, _ = obj.Search(req, form)
}
{
"_source": {
"excludes": [
"bbb"
],
"includes": [
"aaa"
]
},
"from": 20,
"query": {
"bool": {
"must": {
"terms": {
"id": [
100,
200
]
}
}
}
},
"size": 20
}
package main
import (
"app/conn"
"encoding/json"
"github.com/goperate/es/basics"
"github.com/spf13/viper"
)
type TestForm struct {
Id []int `json:"id"`
Id2 *int `json:"id2" es:"-"`
Other struct {
Id3 *int `json:"id3"`
} `json:"other" es:"block"`
}
// 查询效果同
type TestForm struct {
Id []int `json:"id"`
Id3 *int `json:"id3"`
}
func main() {
obj := basics.NewStructToEsQuery()
req := conn.Es().Search().Index(viper.GetString("es.index"))
form := new(TestForm)
jsonStr := "{\"id\": [100, 200], \"id2\": 200, \"other\": {\"id3\": 300}}"
_ = json.Unmarshal([]byte(jsonStr), form)
_, _ = obj.Search(req, form)
}
{
"query": {
"bool": {
"must": [
{
"terms": {
"id": [
100,
200
]
}
},
{
"term": {
"id3": 300
}
}
]
}
}
}
block 表示忽略当前层级, 如示例中的id3跟放在外层对应的查询是一样的
logical 依然生效
package main
import (
"app/conn"
"encoding/json"
"github.com/goperate/es/basics"
"github.com/olivere/elastic"
"github.com/spf13/viper"
)
type TestForm struct {
Id *int `json:"id" es:"custom;logical:not"`
Sort int `json:"sort" es:"custom;sort"`
}
func (t *TestForm) CustomSearch(field string) []elastic.Query {
switch field {
case "id":
return []elastic.Query{elastic.NewTermsQuery("id2", t.Id)}
}
return nil
}
func (t *TestForm) CustomSorter(field string) []elastic.Sorter {
switch field {
case "sort":
return []elastic.Sorter{elastic.SortInfo{Field: "id2", Ascending: t.Sort != 0}}
}
return nil
}
func main() {
form := new(TestForm)
jsonStr := "{\"id\": 100, \"sort\": 1}"
_ = json.Unmarshal([]byte(jsonStr), form)
obj := basics.NewStructToEsQueryAndCustom(form.CustomSearch, form.CustomSorter)
req := conn.Es().Search().Index(viper.GetString("es.index"))
_, _ = obj.Search(req, form)
}
{
"query": {
"bool": {
"must_not": {
"terms": {
"id2": [
100
]
}
}
}
},
"sort": [
{
"id2": {
"order": "asc"
}
}
]
}
package main
import (
"app/conn"
"encoding/json"
"github.com/goperate/es/basics"
"github.com/spf13/viper"
)
type TestForm struct {
Id *int `json:"id" es:"logical:not"`
}
func main() {
form := new(TestForm)
jsonStr := "{\"id\": 100}"
_ = json.Unmarshal([]byte(jsonStr), form)
obj := basics.NewStructToEsQuery()
body := obj.ToSearchBody(form)
body.Query.Must()
body.Source = nil
body.SetPage(1).SetSize(20).SetSorter()
req := conn.Es().Search().Index(viper.GetString("es.index"))
_, _ = body.Search(req)
}
我正在用Ruby编写一个简单的程序来检查域列表是否被占用。基本上它循环遍历列表,并使用以下函数进行检查。require'rubygems'require'whois'defcheck_domain(domain)c=Whois::Client.newc.query("google.com").available?end程序不断出错(即使我在google.com中进行硬编码),并打印以下消息。鉴于该程序非常简单,我已经没有什么想法了-有什么建议吗?/Library/Ruby/Gems/1.8/gems/whois-2.0.2/lib/whois/server/adapters/base.
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我知道我可以指定某些字段来使用pluck查询数据库。ids=Item.where('due_at但是我想知道,是否有一种方法可以指定我想避免从数据库查询的某些字段。某种反拔?posts=Post.where(published:true).do_not_lookup(:enormous_field) 最佳答案 Model#attribute_names应该返回列/属性数组。您可以排除其中一些并传递给pluck或select方法。像这样:posts=Post.where(published:true).select(Post.attr
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵
文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc
ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear
您将如何构建一个简单的Sinatra应用程序?我正在制作,我希望该应用具有以下功能:“应用程序”更像是一个包含所有信息的管理仪表板。然后另一个应用程序将通过REST访问信息。我还没有创建仪表板,只是从数据库中获取东西session和身份验证(尚未实现)您可以上传图片,其他应用可以显示这些图片我已经使用RSpec创建了一个测试文件通过Prawn生成报告目前的设置是这样的:app.rbtest_app.rb因为我实际上只有应用程序和测试文件。到目前为止,我已经将Datamapper用于ORM,将SQLite用于数据库。这是我的第一个Ruby/Sinatra项目,所以欢迎任何和所有建议-我应