Elasticsearch - поисковая система

nano ~/elasticsearch-install.sh

#!/usr/bin/sh
 
VERSION=2.4.0
 
apt-get install openjdk-8-jdk
rm /etc/alternatives/java
ln -s /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java /etc/alternatives/java
 
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH
 
if [ ! -f elasticsearch-$VERSION.deb ]; then
  wget https://download.elastic.co/elasticsearch/release/org/elasticsearch/distribution/deb/elasticsearch/$VERSION/elasticsearch-$VERSION.deb
fi
 
sudo dpkg -i elasticsearch-$VERSION.deb
 
# be sure you add "action.disable_delete_all_indices" : true to the config!!
 
# start script
sudo /etc/init.d/elasticsearch restart
 
if [ -f /usr/bin/plugin ]; then
  rm /usr/bin/plugin
fi
 
sudo ln -s /usr/share/elasticsearch/bin/plugin /usr/bin/plugin
 
# ------------------------------------------------------------------------------------
 
# if you want to remove it:
#sudo dpkg -r elasticsearch
 
# binaries & plugin
#/usr/share/elasticsearch/bin
 
# log dir
#/var/log/elasticsearch
 
# data dir
#/var/lib/elasticsearch
 
# config dir
#/etc/elasticsearch
 
# prepare ElasticSearch UI
#sudo apt-get install apache2
#sudo mkdir /var/www/ui
#sudo chown -R ubuntu.www-data /var/www
# now copy ES-HEAD to /var/www/ui

Установка и настройка Elasticsearch

  • Установите максимальный размер открытых дескрипторов файла для пользователя от 32k до 64k

nano /etc/sysctl.conf

vm.max_map_count = 262144
  • Если возможно, отключите свопинг памяти для процессов Elasticsearch. Обратите внимание, что в виртуальных средах это может привести к неожиданным результатам.
  • Установите значение -Xms равным -Xmx (то же самое что установить значение переменной среды ES_HEAP_SIZE).
  • Оставьте некоторое количество памяти, чтобы кеш операционной системы мог использовать его для Lucene.
Elasticsearch JVM не должен занимать больше половины всего объема памяти.

Подробнее...

$ curl -XGET http://localhost:9200/_stats/?pretty
$ curl -XGET http://localhost:9200/_nodes/?pretty
$ curl --silent -XGET "http://localhost:9200/_snapshot/_all"
$ curl --silent -XGET  "http://localhost:9200/_cat/nodes?pretty"
mbp-mirocow 192.168.1.147 11 78 6.88 d * Mirocow
Добавляем путь до сохранения снапшотов в конфигурационный файл

nano /usr/local/etc/elasticsearch/elasticsearch.yml

path.repo: ["/usr/local/var/elasticsearch/snapshot"]

Регистрация

$ curl -XPUT 'http://localhost:9200/_snapshot/backup' -d '{
    "type": "fs",
    "settings": {
        "location": "/usr/local/var/elasticsearch/snapshot",
        "compress": true
    }
}'
  • где:
    • backup - название репозитория
    • /usr/local/var/elasticsearch/snapshot - папка с содержанием самого репозитория

Проверка регистрации

$ curl -XGET "http://localhost:9200/_snapshot/_all?pretty"

ответ от elasticsearch

{
  "backup" : {
    "type" : "fs",
    "settings" : {
      "compress" : "true",
      "location" : "/usr/local/var/elasticsearch/snapshot/backup"
    }
  }
}

Проверка целостности

$ curl -XPOST "http://localhost:9200/_snapshot/backup/_verify

ответ от elasticsearch

{"nodes":{"zKsDO_G3SlCdbLlhVmgLKA":{"name":"Vapor"}}}

Создание snapshot / Backup

$ curl -XPUT "http://localhost:9200/_snapshot/backup/my_index/?wait_for_completion=true"

Где my_index является существующим индексом.

Проверить наличие инднеса можно командой

$ curl --silent -XGET  "http://localhost:9200/_stats?pretty"|grep my_index

Удаление регистрации репозитория

$ curl -XDELETE 'http://localhost:9200/_snapshot/backup'

Настройка

Если ранее небыл зарегистрирован.

$ curl -XPUT 'http://localhost:9200/_snapshot/_restore?wait_for_completion=true' -d '{
    "type": "fs",
    "settings": {
        "location": "/usr/local/var/elasticsearch/snapshot",
        "compress": true
    }
}'
  • где:
    • backup - название репозитория
    • /usr/local/var/elasticsearch/snapshot - папка с содержанием самого репозитория

Проверка

$ curl -XPOST http://localhost:9200/_snapshot/backup/_verify?pretty

Восстановление

$ curl -XPOST http://localhost:9200/_snapshot/backup/_restore?pretty

Способы запроса

GET

Вывод всех документов из индекса

$ curl -XGET 'localhost:9200/test/_search?q=*&pretty'

REST

Вывод всех документов из индекса

$ curl -XPOST 'localhost:9200/1vse/_search?pretty' -d '
{
  "query": { "match_all": {} }
}'
Доступные параметры для REST запросов
$ curl -XPOST 'localhost:9200/1vse/_search?pretty' -d '
{
  "query": { "match_all": {} },
  "size": 1
}'
$ curl -XPOST 'localhost:9200/1vse/_search?pretty' -d '
{
  "query": { "match": { "title": "Thomson T50E10DHU-01B" } },
  "_source": ["title", "category", "brand"]
}'
 
$ curl -XPOST 'localhost:9200/1vse/_search?pretty' -d '
{
  "query": { "match_phrase": { "category": "телевизоры" } },
  "_source": ["title", "category", "brand"]
}'
 
$ curl -XPOST 'localhost:9200/bank/_search?pretty' -d '
{
  "query": {
    "bool": {
      "must": [
        { "match": { "age": "40" } }
      ],
      "must_not": [
        { "match": { "state": "ID" } }
      ]
    }
  }
}'

Поиск по регулярным выражениям

$ curl -XGET 'http://localhost:9200/_search' -d '{
    "filter": {
        "bool": {
            "should": [
                {
                    "regexp": {
                        "word": "мульт.*"
                    }
                },
                {
                    "regexp": {
                        "word": "фильм.*"
                    }
                }
            ],
            "must_not": {
                "terms": {
                    "word": ["видео", "скачать"]
                }
            }
        }
    }
}'

Подробнее...

Примеры запросов / Query DSL

$ curl -XGET http://localhost:9200/1vse/product/_search/ -d '{
  "size": 10,
  "query": {
    "bool": {
      "should": [
        {
          "function_score": {
            "weight": 100,
            "query": {
              "match": {
                "product.title": "samsung"
              }
            }
          }
        },
        {
          "function_score": {
            "weight": 80,
            "query": {
              "match_phrase_prefix": {
                "product.title": "samsung"
              }
            }
          }
        }
      ]
    }
  }
}'
  • где:
    • 1vse - название БД
    • product.title - название таблицы

Данная операция создаст инжекс test

$ curl -XPUT "http://localhost:9200/test?pretty"
{
  "acknowledged" : true
}

Получить список созданых индексов

$ curl --silent -XGET  "http://localhost:9200/_cat/indices?pretty"

ответ от elasticsearch

yellow open 1vse      5 1 147785 0 197.3mb 197.3mb
yellow open 1vse_logs 5 1 439914 0   1.6gb   1.6gb
$ curl --silent -XGET  "http://localhost:9200/_cat/indices?v"

ответ от elasticsearch

health status index     pri rep docs.count docs.deleted store.size pri.store.size
yellow open   1vse        5   1     147785            0    197.3mb        197.3mb
yellow open   1vse_logs   5   1     439914            0      1.6gb          1.6gb

Фильтры

Фильтр worddelimiter

Фильтр worddelimiter разбивает слова на несколько частей. Небольшой пример: представьте, что вы допустили опечатку в предложении “To be or not to be.That is the question”. Если вы обратите внимание, то заметите отсутствие пробела после точки. Без этого фильтра Elasticsearch проиндексирует “be.That” как одно слово - “bethat”. При помощи этого фильтра мы указываем ему индексировать эти слова по отдельности - “be” и “that”.

Примеры:

 

Фильтр stopwords

Фильтр stopwords состоит из списка слов запрещенных к индексированию. Например он исключает такие слова, как “and”, “a”, “the”, “to” и т.д. Конечно этот список уникален для каждого языка, но существует довольно много заготовок, которые вы можете использовать.

Примеры:

 

Фильтр snowball

Фильтр snowball используется для группировки слов по их основе. Фильтр применяет набор правил для правильного определения основы слова. Это означает, что разные настройки могут выдавать разные результаты. Например слова “indexing”, “indexable”, “indexes”, “indexation” и т.д. получать основу “index”. Хотелось бы отметить, что вы получите результат “Make my string indexable”, запросив “Indexing string”.

Примеры:

 

Фильтр elison

Фильтр elison имеет большее значения для некоторых языков (например французский) и не так важен для других (например английский). Он исключает маловажные слова перед индексированием, например “j’attends que tu m’appelles” (Я жду вашего звонка) проиндексируется как “attends que tu appelles” (наконце слова “que” и “tu” будут исключены фильтром stopwords). Как вы видите слова “j’” и “m’” (Я) были удалены из-за настройки фильтра elison.

Примеры:

 

Фильтр lowercase

Фильтр asciifolding

Заменяет все Unicode символы, не входящие в латинский алфавит на их ascii эквивалент.

Лексеры

nGram

Роль лексера nGram очень высока. Например, наш поиск “funny pony” был разбит на несколько частей. Вот два примера:

Пример 1
"min_gram" : "2",
"max_gram" : "3"
Результат:
fu, fun, un, unn, nn, nny, po, pon, on, ony, ny
2-ой пример с лучшей настройкой:
Пример 2
"min_gram" : "3",
"max_gram" : "20"

Результат:
fun, funn, funny, unn, unny, nny, pon, pony, ony

Анализаторы

По умолчанию в Eldsticseach включен регистрозависимый анализатор поиска. Для его отключения используем ниже следующие настройки в конфигурационом файле elasticsearch.yml

            settings:
                index:
                    analysis:
                        analyzer:
                            string_lowercase:
                                tokenizer: keyword
                                filter: lowercase

Мапинг

$ curl -XGET "http://localhost:9200/1vse_logs/_mapping?pretty=true"

Примеры индексов

            settings:
                index:
                    analysis:
                        analyzer:
                            app_analyzer:
                                type: custom
                                tokenizer: nGram
                                filter   : [stopwords, app_ngram, asciifolding, lowercase, snowball, worddelimiter]
                            app_search_analyzer:
                                type: custom
                                tokenizer: standard
                                filter   : [stopwords, app_ngram, asciifolding, lowercase, snowball, worddelimiter]
                        tokenizer:
                            nGram:
                                type:     "nGram"
                                min_gram: 2
                                max_gram: 20
                        filter:
                            snowball:
                                type:     snowball
                                language: English
                            app_ngram:
                                type: "nGram"
                                min_gram: 2
                                max_gram: 20
                            worddelimiter :
                                 type: word_delimiter
                            stopwords:
                                 type:      stop
                                 stopwords: [_french_]
                                 ignore_case : true

Подокументно

Получение документа

$ curl -XGET "http://localhost:9200/test/external/key1?pretty"

ответ от elasticsearch

{
  "_index" : "test",
  "_type" : "external",
  "_id" : "key1",
  "_version" : 4,
  "found" : true,
  "_source":
{
  "body": {
      "name": "test test",
      "description": "Description"
   }
}
}

Добавление документа / Заполенение индекса

$ curl -XPUT "http://localhost:9200/test/external/key1?pretty" -d '
{
  "body": {"name": "test"}
}'

ответ от elasticsearch

{
  "_index" : "test",
  "_type" : "external",
  "_id" : "1",
  "_version" : 1,
  "created" : true
}

Обновление документа

$ curl -XPOST "http://localhost:9200/test/external/key1?pretty" -d '
{
  "body": {
      "pice_id": 3,
      "name": "test test",
      "description": "Description"
   }
}'

ответ от elasticsearch

{
  "_index" : "test",
  "_type" : "external",
  "_id" : "1",
  "_version" : 1,
  "created" : true
}

Частичное обновление документа

$ curl -XPUT "http://localhost:9200/test/external/key1?pretty" -d '
{
  "body": {
     "doc": {
       "name": "test test"
      }
  }
}'

ответ от elasticsearch

{
  "_index" : "test",
  "_type" : "external",
  "_id" : "1",
  "_version" : 1,
  "created" : true
}

Обновление документа с учетом параметров

> v 2.3
$ curl -XPOST 'http://localhost:9200/test/external/_bulk' -d '
{
  "update": {
      "_index": "myindex",
      "_type": "type",
      "_id": "myid"
  }
}{
  "doc": {
      "field": "new value"
  }
}'
v 2.3 >
$ curl -XPOST 'http://localhost:9200/test/external/_update_by_query' -d '
{
  "query": {
    "filtered": {
      "filter": {
        "bool": {
          "must": [
            {
              "term": {
                "url": "stackoverflow.com"
              }
            },
            {
              "missing": {
                "field": "category"
              }
            }
          ]
        }
      }
    }
  },
  "script" : "ctx._source.category = \"10\";"
}'

Получение документа из индекса по ID

$ curl -XGET "http://localhost:9200/test/external/1?pretty"

ответ от elasticsearch

{
  "_index" : "test",
  "_type" : "external",
  "_id" : "1",
  "_version" : 3,
  "found" : true,
  "_source":
{
  "name": "test"
}
}

Удаление документа из индекса

Удаление по индексу

$ curl -XDELETE 'localhost:9200/customer/external/2?pretty'

Мы также имеем возможность удалить несколько документов совпавших с условием запроса. Данный пример показывает как удалить все документы из индекса customers, содержащие «John»:

$ curl -XDELETE 'localhost:9200/customer/external/_query?pretty' -d '
{
  "query": { "match": { "name": "John" } }
}'

Работа с частичными данными документа

Настройки elasticsearch.yml

script.update: on
script.mapping: on
script.engine.groovy.file.aggs: on
script.engine.groovy.file.mapping: on
script.engine.groovy.file.search: on
script.engine.groovy.file.update: on
script.engine.groovy.file.plugin: on
script.engine.groovy.indexed.aggs: on
script.engine.groovy.indexed.mapping: on
script.engine.groovy.indexed.search: on
script.engine.groovy.indexed.update: on
script.engine.groovy.indexed.plugin: on
script.engine.groovy.inline.aggs: on
script.engine.groovy.inline.mapping: on
script.engine.groovy.inline.search: on
script.engine.groovy.inline.update: on
script.engine.groovy.inline.plugin: on

Создание массива

$ curl -s -XPOST 'localhost:9200/1vse/product_for_mapping/1/_update' -d '{
    "script" : "if(ctx._source.aliases.size() == 0){ctx._source.aliases = [];}"
}' 

Добавление в массив

$ curl -XPOST 'localhost:9200/1vse/product_for_mapping/1/_update' -d '{
    "script" : "ctx._source.aliases += alias",
    "params" : {
        "alias" : "Manfrotto MV-M500A"
    }
}' 

Удаление из массива

$ curl -s -XPOST 'localhost:9200/1vse/product_for_mapping/1/_update' -d '{
    "script" : "for (int i = 0; i < ctx._source.aliases.size(); i++){if(ctx._source.aliases[i] == value){ctx._source.aliases.remove(i);}}",
    "params" : {
        "value" : "Manfrotto MVM500A XXX"
    }
}'

Пакетная обработка документов

$ curl -XPOST 'localhost:9200/customer/external/_bulk?pretty' -d '
{"index":{"_id":"1"}}
{"name": "John Doe" }
{"index":{"_id":"2"}}
{"name": "Jane Doe" }'

Данный пример обновляет первый документ (с ID равным 1) и затем удаляет второй документ (с ID равным 2) в одной массовой операции:

$ curl -XPOST 'localhost:9200/customer/external/_bulk?pretty' -d '
{"update":{"_id":"1"}}
{"doc": { "name": "John Doe becomes Jane Doe" } }
{"delete":{"_id":"2"}}'

Параметры выдачи

  • _source -
  • _score - Оценка - это числовое значение, которое является относительной мерой того, насколько документ совпадает с поисковым запросом, который мы создали. Большие значения оценки показывают более релевантные (более полно совпадающие) документы, меньшие значение - менее релевантные.

Агрегация

Плагины / Plugins

  • snapshot_missing_exception:
    {"type": "no_such_file_exception","reason/usr/local/var/elasticsearch/snapshot/snap-snapshot.dat"}

  • SnapshotMissingException:

    {"error":"SnapshotMissingException[[backup:snapshot] is missing]; nested: FileNotFoundException[/usr/local/var/elasticsearch/snapshot/snapshot-snapshot (No such file or directory)]; ","status":404}

    snapshot не найден, так как дамп создавался с отличным названием снапшота от snapshot

  • Data too large, data for [@timestamp] would be larger than limit

    $ curl -XPOST 'http://localhost:9200/_cache/clear'
{  
   "size":0,
   "aggs":{  
      "top-terms-aggregation":{  
         "terms":{  
            "field":"field_name",
            "size":10
         }
      }
   }
}
{
    "query": {
        "match_phrase": {
            "content": "brown dog"
        }
    }
}
{
    "query": {
        "match_phrase": {
            "content": {
                "query": "brown dog",
                "slop": 3
            }
        }
    }
}
{
    "query": {
        "match_phrase": {
            "content": {
                "query": "brown dog",
                "slop": 100
            }
        }
    }
}
{
    "properties": {
        "content": {
            "type": "string",
            "position_offset_gap": 100
        }
    }
}
{
    "query": {
        "match_phrase": {
            "content": {
                "query": "brown dog",
                "slop": 4
            }
        }
    }
}
{
  "query": {
    "match": {
      "FIELD": "TEXT"
    }
  }
}
{
  "query": {
    "match": {
      "FIELD": {
        "query": "TEXT",
        "OPTION": "VALUE"
      }
    }
  }
}
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "smith"
          }
        }
      ],
      "must_not": [
        {
          "match_phrase": {
            "title": "granny smith"
          }
        }
      ],
      "filter": [
        {
          "exists": {
            "field": "title"
          }
        }
      ]
    }
  },
  "aggs": {
    "my_agg": {
      "terms": {
        "field": "user",
        "size": 10
      }
    }
  },
  "highlight": {
    "pre_tags": [
      "<em>"
    ],
    "post_tags": [
      "</em>"
    ],
    "fields": {
      "body": {
        "number_of_fragments": 1,
        "fragment_size": 20
      },
      "title": {}
    }
  },
  "size": 20,
  "from": 100,
  "_source": [
    "title",
    "id"
  ],
  "sort": [
    {
      "_id": {
        "order": "desc"
      }
    }
  ]
}
"multi_match": {
  "query": "Elastic",
  "fields": ["user.*", "title^3"],
  "type": "best_fields"
}
"bool": {
  "must": [],
  "must_not": [],
  "filter": [],
  "should": [],
  "minimum_should_match" : 1
}
"range": {
  "age": {
    "gte": 10,
    "lte": 20,
    "boost": 2
  }
}
{
  "query": {
    "query_string": {
      "default_field": "content",
      "query": "elastic AND (title:lucene OR title:solr)"
    }
  }
}

Yii2

Дискусии/Форумы

Обсуждения

Документация/Статьи

Autocomplete / Shingles

Видео

Презентации