使用ElasticSearchsearch文件名

我想使用ElasticSearchsearch文件名(而不是文件的内容)。 所以我需要find文件名的一部分(完全匹配,没有模糊search)。

例:
我有以下名称的文件:

My_first_file_created_at_2012.01.13.doc My_second_file_created_at_2012.01.13.pdf Another file.txt And_again_another_file.docx foo.bar.txt 

现在我想search2012.01.13获取前两个文件。
searchfilefile应该返回除最后一个以外的所有文件名。

我如何使用ElasticSearch来完成?

这是我testing过的,但总是返回零结果:

 curl -X DELETE localhost:9200/files curl -X PUT localhost:9200/files -d ' { "settings" : { "index" : { "analysis" : { "analyzer" : { "filename_analyzer" : { "type" : "custom", "tokenizer" : "lowercase", "filter" : ["filename_stop", "filename_ngram"] } }, "filter" : { "filename_stop" : { "type" : "stop", "stopwords" : ["doc", "pdf", "docx"] }, "filename_ngram" : { "type" : "nGram", "min_gram" : 3, "max_gram" : 255 } } } } }, "mappings": { "files": { "properties": { "filename": { "type": "string", "analyzer": "filename_analyzer" } } } } } ' curl -X POST "http://localhost:9200/files/file" -d '{ "filename" : "My_first_file_created_at_2012.01.13.doc" }' curl -X POST "http://localhost:9200/files/file" -d '{ "filename" : "My_second_file_created_at_2012.01.13.pdf" }' curl -X POST "http://localhost:9200/files/file" -d '{ "filename" : "Another file.txt" }' curl -X POST "http://localhost:9200/files/file" -d '{ "filename" : "And_again_another_file.docx" }' curl -X POST "http://localhost:9200/files/file" -d '{ "filename" : "foo.bar.txt" }' curl -X POST "http://localhost:9200/files/_refresh" FILES=' http://localhost:9200/files/_search?q=filename:2012.01.13 ' for file in ${FILES} do echo; echo; echo ">>> ${file}" curl "${file}&pretty=true" done 

您粘贴的内容存在各种问题:

1)不正确的映射

创build索引时,指定:

 "mappings": { "files": { 

但是你的types实际上是file ,而不是files 。 如果你检查了映射,你会立即看到:

 curl -XGET 'http://127.0.0.1:9200/files/_mapping?pretty=1' # { # "files" : { # "files" : { # "properties" : { # "filename" : { # "type" : "string", # "analyzer" : "filename_analyzer" # } # } # }, # "file" : { # "properties" : { # "filename" : { # "type" : "string" # } # } # } # } # } 

2)错误的分析器定义

你已经指定了lowercase标记器,但删除了任何不是字母的东西(参见文档 ),所以你的数字被完全删除。

你可以用分析API检查这个:

 curl -XGET 'http://127.0.0.1:9200/_analyze?pretty=1&text=My_file_2012.01.13.doc&tokenizer=lowercase' # { # "tokens" : [ # { # "end_offset" : 2, # "position" : 1, # "start_offset" : 0, # "type" : "word", # "token" : "my" # }, # { # "end_offset" : 7, # "position" : 2, # "start_offset" : 3, # "type" : "word", # "token" : "file" # }, # { # "end_offset" : 22, # "position" : 3, # "start_offset" : 19, # "type" : "word", # "token" : "doc" # } # ] # } 

3)Ngrams在search

您可以在索引分析器和search分析器中包含您的ngram标记filter。 这对于索引分析器来说很好,因为你想把这个ngrams编入索引。 但是当你search时,你想search完整的string,而不是每个ngram。

例如,如果您使用长度为1到4的ngram为"abcd"build立索引,您将以这些标记结束:

 abcd ab bc cd abc bcd 

但是,如果你search"dcba" (不应该匹配),你也用ngram分析你的search条件,那么你实际上search:

 dcba dc cb ba dbc cba 

所以abcd会匹配!

首先,你需要select合适的分析仪。 您的用户可能会search文字,数字或date,但他们可能不会希望file匹配。 相反,使用边ngram可能会更有用,它会将ngram定位到每个单词的开头(或结尾)。

另外,为什么排除docx等? 当然,用户可能想要search的文件types?

因此,我们通过删除任何不是字母或数字的东西(使用模式标记器 ),将每个文件名拆分为更小的标记:

 My_first_file_2012.01.13.doc => my first file 2012 01 13 doc 

那么对于索引分析器,我们也会在这些令牌的每一个上使用边ngram:

 my => m my first => f fi fir firs first file => f fi fil file 2012 => 2 20 201 201 01 => 0 01 13 => 1 13 doc => d do doc 

我们创build索引如下:

 curl -XPUT 'http://127.0.0.1:9200/files/?pretty=1' -d ' { "settings" : { "analysis" : { "analyzer" : { "filename_search" : { "tokenizer" : "filename", "filter" : ["lowercase"] }, "filename_index" : { "tokenizer" : "filename", "filter" : ["lowercase","edge_ngram"] } }, "tokenizer" : { "filename" : { "pattern" : "[^\\p{L}\\d]+", "type" : "pattern" } }, "filter" : { "edge_ngram" : { "side" : "front", "max_gram" : 20, "min_gram" : 1, "type" : "edgeNGram" } } } }, "mappings" : { "file" : { "properties" : { "filename" : { "type" : "string", "search_analyzer" : "filename_search", "index_analyzer" : "filename_index" } } } } } ' 

现在,testing我们的分析仪是否正常工作:

filename_search:

 curl -XGET 'http://127.0.0.1:9200/files/_analyze?pretty=1&text=My_first_file_2012.01.13.doc&analyzer=filename_search' [results snipped] "token" : "my" "token" : "first" "token" : "file" "token" : "2012" "token" : "01" "token" : "13" "token" : "doc" 

filename_index:

 curl -XGET 'http://127.0.0.1:9200/files/_analyze?pretty=1&text=My_first_file_2012.01.13.doc&analyzer=filename_index' "token" : "m" "token" : "my" "token" : "f" "token" : "fi" "token" : "fir" "token" : "firs" "token" : "first" "token" : "f" "token" : "fi" "token" : "fil" "token" : "file" "token" : "2" "token" : "20" "token" : "201" "token" : "2012" "token" : "0" "token" : "01" "token" : "1" "token" : "13" "token" : "d" "token" : "do" "token" : "doc" 

好的 – 似乎工作正常。 所以让我们添加一些文档:

 curl -X POST "http://localhost:9200/files/file" -d '{ "filename" : "My_first_file_created_at_2012.01.13.doc" }' curl -X POST "http://localhost:9200/files/file" -d '{ "filename" : "My_second_file_created_at_2012.01.13.pdf" }' curl -X POST "http://localhost:9200/files/file" -d '{ "filename" : "Another file.txt" }' curl -X POST "http://localhost:9200/files/file" -d '{ "filename" : "And_again_another_file.docx" }' curl -X POST "http://localhost:9200/files/file" -d '{ "filename" : "foo.bar.txt" }' curl -X POST "http://localhost:9200/files/_refresh" 

并尝试search:

 curl -XGET 'http://127.0.0.1:9200/files/file/_search?pretty=1' -d ' { "query" : { "text" : { "filename" : "2012.01" } } } ' # { # "hits" : { # "hits" : [ # { # "_source" : { # "filename" : "My_second_file_created_at_2012.01.13.pdf" # }, # "_score" : 0.06780553, # "_index" : "files", # "_id" : "PsDvfFCkT4yvJnlguxJrrQ", # "_type" : "file" # }, # { # "_source" : { # "filename" : "My_first_file_created_at_2012.01.13.doc" # }, # "_score" : 0.06780553, # "_index" : "files", # "_id" : "ER5RmyhATg-Eu92XNGRu-w", # "_type" : "file" # } # ], # "max_score" : 0.06780553, # "total" : 2 # }, # "timed_out" : false, # "_shards" : { # "failed" : 0, # "successful" : 5, # "total" : 5 # }, # "took" : 4 # } 

成功!

#### UPDATE ####

我意识到2012.01的search将会匹配2012.01.122012.12.01所以我尝试改变查询来使用文本短语查询。 但是,这不起作用。 事实certificate,边缘ngramfilter增加每个ngram的位置计数(而我会认为每个ngram的位置将与该词的开始相同)。

上面第(3)点中提到的问题仅在使用试图匹配ANY标记的query_stringfieldtext query时才是问题。 但是,对于text_phrase查询,它会尝试匹配所有的标记,并按正确的顺序。

为了演示这个问题,用另一个date索引另一个文档:

 curl -X POST "http://localhost:9200/files/file" -d '{ "filename" : "My_third_file_created_at_2012.12.01.doc" }' curl -X POST "http://localhost:9200/files/_refresh" 

并执行与上面相同的search:

 curl -XGET 'http://127.0.0.1:9200/files/file/_search?pretty=1' -d ' { "query" : { "text" : { "filename" : { "query" : "2012.01" } } } } ' # { # "hits" : { # "hits" : [ # { # "_source" : { # "filename" : "My_third_file_created_at_2012.12.01.doc" # }, # "_score" : 0.22097087, # "_index" : "files", # "_id" : "xmC51lIhTnWplOHADWJzaQ", # "_type" : "file" # }, # { # "_source" : { # "filename" : "My_first_file_created_at_2012.01.13.doc" # }, # "_score" : 0.13137488, # "_index" : "files", # "_id" : "ZUezxDgQTsuAaCTVL9IJgg", # "_type" : "file" # }, # { # "_source" : { # "filename" : "My_second_file_created_at_2012.01.13.pdf" # }, # "_score" : 0.13137488, # "_index" : "files", # "_id" : "XwLNnSlwSeyYtA2y64WuVw", # "_type" : "file" # } # ], # "max_score" : 0.22097087, # "total" : 3 # }, # "timed_out" : false, # "_shards" : { # "failed" : 0, # "successful" : 5, # "total" : 5 # }, # "took" : 5 # } 

第一个结果是2012.12.01这个date不是2012.12.01的最佳匹配。 所以要匹配那个确切的短语,我们可以这样做:

 curl -XGET 'http://127.0.0.1:9200/files/file/_search?pretty=1' -d ' { "query" : { "text_phrase" : { "filename" : { "query" : "2012.01", "analyzer" : "filename_index" } } } } ' # { # "hits" : { # "hits" : [ # { # "_source" : { # "filename" : "My_first_file_created_at_2012.01.13.doc" # }, # "_score" : 0.55737644, # "_index" : "files", # "_id" : "ZUezxDgQTsuAaCTVL9IJgg", # "_type" : "file" # }, # { # "_source" : { # "filename" : "My_second_file_created_at_2012.01.13.pdf" # }, # "_score" : 0.55737644, # "_index" : "files", # "_id" : "XwLNnSlwSeyYtA2y64WuVw", # "_type" : "file" # } # ], # "max_score" : 0.55737644, # "total" : 2 # }, # "timed_out" : false, # "_shards" : { # "failed" : 0, # "successful" : 5, # "total" : 5 # }, # "took" : 7 # } 

或者,如果您仍想匹配所有3个文件(因为用户可能会记住文件名中的一些单词,但顺序不对),则可以同时运行这两个查询,但会增加正确顺序的文件名的重要性:

 curl -XGET 'http://127.0.0.1:9200/files/file/_search?pretty=1' -d ' { "query" : { "bool" : { "should" : [ { "text_phrase" : { "filename" : { "boost" : 2, "query" : "2012.01", "analyzer" : "filename_index" } } }, { "text" : { "filename" : "2012.01" } } ] } } } ' # [Fri Feb 24 16:31:02 2012] Response: # { # "hits" : { # "hits" : [ # { # "_source" : { # "filename" : "My_first_file_created_at_2012.01.13.doc" # }, # "_score" : 0.56892186, # "_index" : "files", # "_id" : "ZUezxDgQTsuAaCTVL9IJgg", # "_type" : "file" # }, # { # "_source" : { # "filename" : "My_second_file_created_at_2012.01.13.pdf" # }, # "_score" : 0.56892186, # "_index" : "files", # "_id" : "XwLNnSlwSeyYtA2y64WuVw", # "_type" : "file" # }, # { # "_source" : { # "filename" : "My_third_file_created_at_2012.12.01.doc" # }, # "_score" : 0.012931341, # "_index" : "files", # "_id" : "xmC51lIhTnWplOHADWJzaQ", # "_type" : "file" # } # ], # "max_score" : 0.56892186, # "total" : 3 # }, # "timed_out" : false, # "_shards" : { # "failed" : 0, # "successful" : 5, # "total" : 5 # }, # "took" : 4 # } 

我相信这是因为令牌被使用..

http://www.elasticsearch.org/guide/reference/index-modules/analysis/lowercase-tokenizer.html

小写标记符在字边界上分开,所以2012.01.13将被索引为“2012”,“01”和“13”。 searchstring“2012.01.13”显然不匹配。

一种select是在search中添加标记。 因此,search“2012.01.13”将被标记为与索引中相同的标记,并且将匹配。 这也很方便,因为你不需要总是在代码中小写你的search。

第二种select是使用n-gram标记器而不是filter。 这将意味着它会忽略单词界限(你也会得到“_”),但是你可能会遇到大小写不一致的问题,这大概是你在第一个地方添加小写的tokenizer的原因。

我对ES没有经验,但在Solr中,您需要将字段types指定为文本。 您的字段是stringtypes而不是文本 。 string字段,不会被分析,但会逐字存储和索引。 给一个镜头,看看它是否有效。

 properties": { "filename": { "type": "string", "analyzer": "filename_analyzer" }