分类 notes 下的文章

muhe师父语录(持续更新)

2018/10/14 : (邢老师牛逼)我现在也很迷茫,可是不管你在做啥,坚持做就好了吧。

2018/10/22 : (邢老师牛逼)人生总是如此,对吧?你要么知道自己想要什么,但是却得不到。要么得到了,可是却不知道自己想要什么了。

Elasticsearch_DSL_Search_Note

0x00 测试数据

{
  "took": 5,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 7,
    "max_score": 1,
    "hits": [
      {
        "_index": "test",
        "_type": "test",
        "_id": "123",
        "_score": 1,
        "_source": {
          "content": "1.2.3.12.23.34"
        }
      },
      {
        "_index": "test",
        "_type": "test",
        "_id": "21",
        "_score": 1,
        "_source": {
          "content": "beijing"
        }
      },
      {
        "_index": "test",
        "_type": "test",
        "_id": "2122",
        "_score": 1,
        "_source": {
          "content": "360.cn"
        }
      },
      {
        "_index": "test",
        "_type": "test",
        "_id": "21322",
        "_score": 1,
        "_source": {
          "content": "360"
        }
      },
      {
        "_index": "test",
        "_type": "test",
        "_id": "21222",
        "_score": 1,
        "_source": {
          "content": "ab.ab"
        }
      },
      {
        "_index": "test",
        "_type": "test",
        "_id": "213422",
        "_score": 1,
        "_source": {
          "content": "ab"
        }
      },
      {
        "_index": "test",
        "_type": "test",
        "_id": "212",
        "_score": 1,
        "_source": {
          "content": "360.cn/?1=1"
        }
      }
    ]
  }
}

0x01 Elasticsearch 基本结构

Elasticsearch 是一个分布式的搜索和分析引擎,可以用于全文检索、结构化检索和分析,并能将这三者结合起来。

Elasticsearch 存储储存的数据,相当于一个个文档。其中有三个必须的元数据元素:

_index(索引)
    文档位置
_type(类别)
    文档表示对象类别
_id
    文档唯一标识

一般插入数据时如果不指定_id将会分配一个随机字符串当作文档的_id.

一般我们想查看储存结构和数据元素可通过_mapping方式,可以查看完整映射。

λ curl -XGET "http://192.168.188.133:9200/test/_mapping/test?pretty"
{
  "test" : {
    "mappings" : {
      "test" : {
        "properties" : {
          "content" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          }
        }
      }
    }
  }
}

从映射中可以看到index=test,type=test中有个properties。

而我们插入数据中的 field = content的内容首先有个type为text.

那么现在我们来解释一下Elasticsearch中文档类型。

具体可查看elasticsearch5.5 Document

这边简单的介绍几种常用的:

Numeric datatypes
    long, integer, short, byte, double, float, half_float, scaled_float
Date datatype
    date
Boolean datatype
    boolean
Binary datatype
    binary
Range datatypes
    integer_range, float_range, long_range, double_range, date_range
string
    text and keyword

着重说明一下string类型。

string类型在elasticsearch5.0版本后拆分成text和keyword两种类型。在查询时,根据选择text或者keyword的不同,查询的处理方式也不同。如果将数据当作text进行查找的话,es内置分词器会将text按照分词器规则进行分词后进行匹配,但是如果使用keyword的话则会当成字符串进行匹配。使用text还是keyword需要看需求进行选择,如果一个field的数据量过大,使用keyword的话将会极大的降低效率。

0x02 Elasticsearch DSL 查询

如果你只是需要应用,那么这里将会有大部分问题的解决办法。

match 查询

GET /_search
{
    "query": {
        "match" : {
            "content" : "360.cn"
        }
    }
}

查询结果:

{
  "took": 11,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 3,
    "max_score": 0.52354836,
    "hits": [
      {
        "_index": "test",
        "_type": "test",
        "_id": "21322",
        "_score": 0.52354836,
        "_source": {
          "content": "360"
        }
      },
      {
        "_index": "test",
        "_type": "test",
        "_id": "2122",
        "_score": 0.34148216,
        "_source": {
          "content": "360.cn"
        }
      },
      {
        "_index": "test",
        "_type": "test",
        "_id": "212",
        "_score": 0.2876821,
        "_source": {
          "content": "360.cn/?1=1"
        }
      }
    ]
  }
}

match查询会将你所输入的查询字符按照既定的分词器拆分后在和指定的field进行匹配,如果field的类型是text,则会将filed内的内容也拆分,匹配后返回结果。match查询实质上是一种boolean型查询,它支持 and 或者 or 操作符,但是一般默认是or操作符。即,若查询为:

GET /_search
{
    "query": {
        "match" : {
            "content" : "test search"
        }
    }
}

match返回结果中会包含匹配的test或者searc的field。如展示的查询,就是将360.cn按照默认分词分为360 和 cn 两个部分,进行查询。

GET /_search
{
    "query": {
        "match" : {
            "content" : {
                "query" : "test search"
                "operator" : "and"
            }
        }
    }
}

这样查询的话则会返回同时包含test和search的部分。

前面说到match查询是将所输入和查询目标分别分词后查询,这样查询以后会返回一个score即匹配度。匹配度越高也就是在text中这些词汇出现率越高。

cutoff_frequency这个参数即可以根据出现频率高低进行划,绝对输出内容

match_phrase 短语查询

match_phrase 查询会依旧会将查询语句进行分词,但是他会根据位置关系进行匹配,如:

GET /_search
{
    "query": {
        "match_phrase" : {
            "message" : "360.cn"
        }
    }
}

查询结果:

{
  "took": 2,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 1.0541058,
    "hits": [
      {
        "_index": "test",
        "_type": "test",
        "_id": "2122",
        "_score": 1.0541058,
        "_source": {
          "content": "360.cn"
        }
      },
      {
        "_index": "test",
        "_type": "test",
        "_id": "212",
        "_score": 0.5753642,
        "_source": {
          "content": "360.cn/?1=1"
        }
      }
    ]
  }
}

虽然他查询也是将查询内容和查询目标(text)进行分词以后查询,但是他会根据位置是否相连判断是否是短语进行匹配,然后输出结果。

match_phrase有个参数是 analyzer (分词规则),使用这个可以指定指定分词器。如果想了解分词器可以继续往下看,会有详细说明,如果只是想满足需求,可以看keyword不需要看分词器

match_phrase_prefix 前缀查询

match_phrase_prefix 和 match_phrase 一样,唯一的区别就是从分词后的字符前缀开始比较

multi_match 多数查询

multi_match 可以同时匹配多个 field,如:

GET /_search
{
  "query": {
    "multi_match" : {
      "query":    "Will Smith",
      "fields": [ "title", "*_name" ]
    }
  }
}

同时有个小技巧,如果想在两个field内匹配不同内容,因为match实质上是boolean查询,故可以这样添加条件:

{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "content": "360"
          }
        },
        {
          "match": {
            "content": "cn"
          }
        }
      ]
    }
  }
}

这样可以同时匹配不同field,不同内容的文档。

query_string

URL Search中q参数查询也就是使用这个方法,在此不做赘述。附链接,可查看语法:

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html

term

term查询是精确的查询特定的词汇在倒排索引中。

首先解释一下倒排索引,es在查询时会将查询的field通过分词器处理后加入倒排索引。

查询方式如下:

GET _search
{
  "query": {
    "term" : {
        "content" : "360.cn"
     }
  }
}

看到这里的应该很好奇既然这样term和match的区别在哪里,官方已经给出解释很明确了:

String fields can be of type text (treated as full text, like the body of an email), or keyword (treated as exact values, like an email address or a zip code). Exact values (like numbers, dates, and keywords) have the exact value specified in the field added to the inverted index in order to make them searchable.

However, text fields are analyzed. This means that their values are first passed through an analyzer to produce a list of terms, which are then added to the inverted index.

There are many ways to analyze text: the default standard analyzer drops most punctuation, breaks up text into individual words, and lower cases them. For instance, the standard analyzer would turn the string “Quick Brown Fox!” into the terms [quick, brown, fox].

This analysis process makes it possible to search for individual words within a big block of full text.

The term query looks for the exact term in the field’s inverted index — it doesn’t know anything about the field’s analyzer. This makes it useful for looking up values in keyword fields, or in numeric or date fields. When querying full text

其中最重要的是:

主要说的是一个string类型的field可能是一个text也可能是一个keyword。当text被处理的时候,text会被分词器分析,这就意味着他们的数据第一次被处理不是查询而是分词器,它产生一系列的小的词汇加入倒叙索引,如“Quick Brown Fox!”在standard分词器处理会会变成[quick, brown, fox],那么在差欻性能的时候将不会匹配整个文本,而是在倒序索引中匹配这些小的词汇。
term不会将查询词汇进行分词进行匹配,而match则会将查询词汇分词后进行匹配。

官方还给了几个例子:

PUT my_index
{
  "mappings": {
    "my_type": {
      "properties": {
        "full_text": {
          "type":  "text"    //1
        },
        "exact_value": {
          "type":  "keyword"    //2
        }
      }
    }
  }
}

PUT my_index/my_type/1
{
  "full_text":   "Quick Foxes!",    //3
  "exact_value": "Quick Foxes!"   //4
}

1The full_text field is of type text and will be analyzed.

2.The exact_value field is of type keyword and will NOT be analyzed.

3.The full_text inverted index will contain the terms: [quick, foxes].

4.The exact_value inverted index will contain the exact term: [Quick Foxes!].

term和match的比较

GET my_index/my_type/_search
{
  "query": {
    "term": {
      "exact_value": "Quick Foxes!" 
    }
  }
}
//1

GET my_index/my_type/_search
{
  "query": {
    "term": {
      "full_text": "Quick Foxes!" 
    }
  }
}
//2

GET my_index/my_type/_search
{
  "query": {
    "term": {
      "full_text": "foxes" 
    }
  }
}
//3

GET my_index/my_type/_search
{
  "query": {
    "match": {
      "full_text": "Quick Foxes!" 
    }
  }
}
//4

1.This query matches because the exact_value field contains the exact term Quick Foxes!.

2.This query does not match, because the full_text field only contains the terms quick and foxes. It does not contain the exact term Quick Foxes!.

3.A term query for the term foxes matches the full_text field.

4.This match query on the full_text field first analyzes the query string, then looks for documents containing quick or foxes or both.

range 范围查询

参数如下:

gt: > 大于
lt: < 小于
gte: >= 大于或等于
lte: <= 小于或等于

示例:

GET _search
{
    "query": {
        "range" : {
            "age" : {
                "gte" : 10,
                "lte" : 20,
            }
        }
    }
}

正则查询wildcard 和 regexp

使用范例:

GET /_search
{
    "query": {
        "wildcard" : {
            "user" : "ki*y"
        }
    }
}

GET /_search
{
    "query": {
        "regexp":{
            "name.first": "s.*y"
        }
    }
}

特别注意:wildcard和regexp在查询时若field的类型为text,则是对倒叙索引中的内容进行匹配。如:

GET /_search
{
    "query": {
        "regexp":{
            "content": "3[0-9]0\.cn"
        }
    }
}

而field为content的内容为:

360.cn 666

则倒叙索引中只会有['360','cn','666'],而正则使用也只会对['360','cn','666']中的每一个分别进行匹配。

wildcard 使用标准的 shell 通配符查询: ? 匹配任意字符, * 匹配 0 或多个字符。

regexp 可以使用我们平时使用的正则表达式,其中regexp有个flags参数,他是继承至Lucene。

可用的有:

ALL (default), ANYSTRING, COMPLEMENT, EMPTY, INTERSECTION, INTERVAL, or NONE.

意义可通过 http://lucene.apache.org/core/4_9_0/core/org/apache/lucene/util/automaton/RegExp.html 查询.

keyword 类型

keyword 类型是当这个field被查询时,会当成一个字符串而不是一个text进行分词,如果查询field的内容大小没有很大并且想通过正则进行精确查询,可以通过将text转换为keyword进行查询,方式如下:

{
  "query": {
    "match" : {
      "content.keyword":"360"
    }
  }
}

即在原field后加个.keyword即可。

特别说明

当查找类型转换为keyword以后,term或者match等这中需要完全匹配的处理结果将会和把field当作text时候处理的结果差距特别大。因为当field变为keyword进行查询后keyword不分词,则match将查找内容分词后不能和整个field完全匹配,就不会返回结果,term同理。

0x02分词器介绍

如果只需要应用,那么前面所说的应该满足基本需求了,如果想更优雅的使用可以接着了解下分词器。

官方文档5.5介绍如下:

https://www.elastic.co/guide/en/elasticsearch/reference/current/analyzer-anatomy.html

默认就有的分词器有:

Standard
Simple
Whitespace
Stop
keyword
pattern
language
Fingerprint
Custom

每个分词器的特性官方都有写,链接如下:

https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-analyzers.html

当想对每种分词器进行测试时可以使用如下方法:

POST _analyze
{
  "analyzer": "whitespace",
  "text":     "The quick brown fox."
}

analyzer 分词器名称
text 进行测试内容

如我使用 whitespace 分词器(按照空格将text内的内容分为一个个term加入倒叙索引),对'360.cn 666'进行测试:

POST _analyze
{
  "analyzer": "whitespace",
  "text":     "360.cn 666"
}

返回内容为:

{
  "tokens" : [
    {
      "token" : "360.cn",
      "start_offset" : 0,
      "end_offset" : 6,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "666",
      "start_offset" : 7,
      "end_offset" : 10,
      "type" : "word",
      "position" : 1
    }
  ]
}

将会分为两部分['360.cn','666']并表明开始和借宿点在text的下标。

挑选好分词器后,可以通过如下命令创建指定index的分词器:

curl -XPUT 'localhost:9200/bac?pretty' -H 'Content-Type: application/json' -d'
{
  "mappings": {
    "abc": {
      "properties": {
        "content": {
          "type":     "text",
          "analyzer": "standard",
          "fields": {
            "keyword": {
              "type":     "keyword",
              "ignore_above":"256"
            }
          }
        }
      }
    }
  }
}

创建完成后即可看到mapping中已经有分词器了:

{
  "abc" : {
    "mappings" : {
      "abc" : {
        "properties" : {
          "content" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            },
            "analyzer" : "standard"
          }
        }
      }
    }
  }
}

如果想使用停用词之类的自己建立一个新规则的分词器可以使用setting:

PUT my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "std_english": {
          "type":      "standard",
          "stopwords": "_english_"
        }
      }
    }
  }
}

这样会建立一个名为 std_englist 的分词器。在通过上述方式设定即可。

然而我并没有找到可以修改已经建立index的分词器的方法,如果各位师父们有知识请教我一下qwq

0x03 一些tips

First

在查询时可以先使用_mapping查看整个index的映射关系,field的类型,这样更好确定怎么查,查哪些。如:

{
  "test" : {
    "mappings" : {
      "tesst" : {
        "properties" : {
          "content" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          }
        }
      },
      "test" : {
        "properties" : {
          "content" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          }
        }
      }
    }
  }
}

在这里就可以看到,没有指定分词器,则使用的是默认分词器。只有一个field,名字是content。类型是文本,则查询时根据需求选择用text查询还是keyword查询。这样可以更好确定查询语句怎么写。

Second

由于match,match_phrase,term,regexp等都是boolean型查询,则可以通过must,should,boolean等组合进行联合查询,更方便快捷高效。

如:

{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "_index": "360"
          }
        },
        {
          "match_all": {}
        },
        {
          "regexp": {
            "content.keyword": ".*360.cn.*"
          }
        }
      ]
    }
  }
}

final

推荐一个chrome插件,如果连看都不想看就直接用这个生成查询就可以了:mirage

2016末

又是深夜

转眼间又到了年末,而我也已经大三啦,毕业已经触手可及,然而还是感觉自己很菜啊qwq

一月份准备期末考试,结果还是可以,并没有给我已经很窘迫的学业增加负面影响。对此还是很满意的。

寒假一个月在家基本上就是看别人日站自己日站。

三月份开始大二的下半学期,期间基本也就是没事看看书看看剧这样,并没有很努力。半年在我看来是碌碌无为吧。期间学校组织了CCTF,然而并没有什么很好的思路可以出题目。想要弄出很精彩的东西可是总感觉自己的积累不够,想着如果出的题目很没有意思会给学校丢脸吧。最后因为机缘巧合打了CCTF的决赛,和一个学弟还有会长一起。这也算是我自己真正打的一次决赛吧。成绩自己感觉不是很理想,自己还是太弱了啊。

四月底到五月这样菜鸡有了女朋友,感觉自己失恋了。

七月份暑假在家基本没有做事和学习,整体闲着,和混吃等死基本没有区别。八月份去北京ISCC,也是和学弟一起。可能也是因为只有我的原因,成绩也不是很理想。但是和学弟一起商量第二天怎么打比赛到半夜真的是很美好的回忆啊。这也是我第一次来帝都。感觉就是帝都真的很好啊,想过来生活一段时间感受一下。帝都的时候喝涛哥吃汉拿山看到了pandada,然而害羞并不敢说话。学弟真的超屌,在北京同居的两三天里学到了很多。然后见到了霖儿和big,有时候想想认识这些学长真的是很走运啊。很感激。

七月去了ISC。

九月份开学,开始了和子卓同居的日子。真的是接触到大佬的生活才知道自己到底有多不努力啊qwq。期间去了菜鸡女票也就是容妈的公司面试。这也是第一次我觉的打CTF是有用的。因为CCTF有名次所以直接录用了我....然后事实证明,面试比较水所以工作也比较水。实习半年基本上没有学习到什么,基本上都是用自己所学做事而已。但是还是很感激公司的。

九月份中旬就开始和菜鸡还有榕妈子卓一起吃饭,突然觉的菜鸡有女票并没有很大影响啊。

九月底去西电打华山杯,基本上没做什么,划水一天。(我还是觉的我那个脚本没有写错,因为人测也是那个结果!哼!)

十一放假莫名难过,在家宅了七天。

十一月份想找个女朋友跨年。

十二月初去厦门打比赛。第二次看到大海,心情还是很激动。菜鸡哥哥带我们玩了两天还看到了菜鸡妈妈。果然我还是吃不惯海鲜,榕妈感觉打了鸡血一样嗨了三天。比赛的时候感觉自己有点贡献吧。也认识到自己的不足,只会WEB的确很惨。更何况你WEB还不是超强。所以也决定要开始学re。(很认真)

十二月底,年末。

所有人都有人陪,我依然是一个人。

如果可以,请让我明年有个女朋友,会害羞的那种。