2012-02-23 8 views
26

ElasticSearch를 사용하여 파일의 내용이 아닌 파일 이름을 검색하고 싶습니다. 따라서 파일 이름의 일부 (정확한 일치, 퍼지 검색 없음)를 찾아야합니다.ElasticSearch로 파일명 검색

예 :
나는 다음과 같은 이름의 파일이 있습니다

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 

지금 내가 처음 두 개의 파일을 얻을 수 2012.01.13 검색 할를.
file 또는 ile을 검색하면 마지막 파일을 제외한 모든 파일 이름을 반환해야합니다.

어떻게 ElasticSearch를 사용하여이를 수행 할 수 있습니까?

내가 테스트 한 것입니다,하지만 항상 0 결과를 반환

나는이 때문에 사용되는 토크 나이의 판단
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 

답변

128

당신은 붙여 넣은 것과 여러 가지 문제가 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 토크 나이를 지정하지만이 편지가 아닌 것은 제거

정의 (docs 참조), 귀하의 전화 번호는 완전히 삭제됩니다.

당신은 analyze 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을

검색에 당신은 인덱스 분석 및 검색 분석기 모두에 N- 그램 토큰 필터를 포함한다. Ngram을 색인화하기를 원하기 때문에 색인 분석기에서는 문제가 없습니다. 그러나 검색 할 때 각 ngram이 아닌 전체 문자열을 검색하려고합니다. 예를 들어

, 당신은 4 길이 1의 ngrams와 인덱스 "abcd", 당신은이 토큰으로 종료됩니다 경우 :

a b c d ab bc cd abc bcd 

그러나 당신은 또한 당신을 "dcba" 검색 (일치하지해야하는) 및 경우 ngrams와 검색어를 분석, 당신은 실제로에서 검색 :

d c b a dc cb ba dbc cba 

그래서 a, b, cd이 일치합니다!

솔루션

먼저, 당신은 바로 분석기를 선택해야합니다. 사용자는 단어, 숫자 또는 날짜를 검색하지만 ilefile과 일치하지 않을 것입니다. 대신, edge ngrams을 사용하면 Ngram을 각 단어의 시작 (또는 끝)에 고정시키는 것이 더 유용 할 것입니다.

또한 docx 등을 제외해야하는 이유는 무엇입니까? 확실히 사용자가 파일 형식을 검색하려고 할 것입니다.

그래서 문자 나 (사용 pattern tokenizer) 숫자가 아닌 것은 제거하여 작은 토큰으로 각 파일 이름을 깰 수 있습니다 :

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

그런 다음 인덱스 분석기, 우리는 또한 가장자리를 사용합니다 그 토큰의 각 ngrams 다음과 같이

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 

우리는 인덱스를 만들 :

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" 
      } 
     } 
     } 
    } 
} 
' 
,

지금, 우리의 분석기가 제대로 작동하는지 테스트 :

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" 

을 그리고 검색하려고 : 그럼 몇 가지 문서를 추가 할 수

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에 대한 검색이 모두 2012.01.122012.12.01 그래서 내가 대신 text phrase 쿼리를 사용하는 쿼리를 변경 시도와 일치하는 것을 깨달았다. 그러나 이것은 효과가 없었습니다. 가장자리 ngram 필터는 각 ngram의 위치 수를 증가시킵니다 (각 ngram의 위치는 단어의 시작과 같을 것입니다).

위에서 언급 한 문제는 어떤 토큰과도 일치하려고 시도하는 query_string, field 또는 text 쿼리를 사용할 때만 문제가됩니다. 그러나 text_phrase 쿼리의 경우 모든 토큰을 올바른 순서로 일치 시키려고 시도합니다.

다른 날짜로 다른 문서 문제, 인덱스를 설명하기 위해 :

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" 

그리고 상기와 동일한 검색을 수행 외설하는

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있다 가장 일치하는 2012.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 
# } 
+16

와우, 이건 단지 해결책이 아닙니다. 그것은 제가 찾고 있었던 튜토리얼입니다 : D THX – Biggie

+0

정말 고마워요. 정말 고마워요! – Robin

+5

멋진 답변입니다. 그럼 SO20에 +20 버튼이 있습니까? – Ben

0

..

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

분할 토크 나이 소문자를 단어 경계를 벗어날 정도로 2012.01.13은 "2012", "01"및 "13"으로 색인이 생성됩니다. 문자열 "2012.01.13"을 검색하면 분명히 일치하지 않습니다.

하나의 옵션은 검색에 토큰 화를 추가하는 것입니다. 따라서 "2012.01.13"에 대한 검색은 색인에서와 동일한 토큰으로 토큰 화되며 일치 할 것입니다. 또한 코드에서 항상 검색을 소문자로 지정할 필요가 없으므로 편리합니다.

두 번째 옵션은 필터 대신 n-gram 토큰 화 프로그램을 사용하는 것입니다. 이것은 단어 경계를 무시한다는 것을 의미 할 것이고 (또한 "_"을 얻을 것입니다), 대소 문자가 일치하지 않는 문제가있을 수 있습니다. 이는 아마도 소문자 토크 나이저를 처음에 추가 한 이유 일 수 있습니다.

+0

1] 옵션을 : 내 filename_analyzer가 이미 때 인덱싱 및 검색에 이용 될 것이라고 생각, 내가 명시 적으로하지 않았기 때문에 index_analyzer/search_analyzer를 사용하십시오. 제 2의 옵션에 : 나는이 방법으로 그것을 시험해 보았다. 그러나 키워드를''* "'로 둘러 쌓으면 검색 결과는 오직''* 2012 * ''와 같이 나타납니다. 게다가''* doc * ''는 두 문서 파일을 모두 찾았지만''* .doc * ''만이 docx-file만을 찾는다. 어떤 아이디어? – Biggie

-1

저는 ES에 대한 경험이 없지만 Solr에서는 필드 유형을 텍스트로 지정해야합니다. 귀하의 필드의 유형은 대신입니다. 문자열 필드는 분석되지 않지만 그대로 저장되고 색인됩니다. 그 슛을주고 그것이 효과가 있는지보십시오.

1) 잘못된 매핑

인덱스를 만들 때 지정 :

"mappings": { 
    "files": { 

하지만 당신의 타입이 실제로

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

ES는 단지'string' 유형을 사용하며 이것들은 기본적으로 분석됩니다. 그것들을 축 어적으로 저장하고 싶다면, 매핑에'{ "index": "not_analyzed"}'를 추가해야합니다. – DrTech