Basic setup


In [60]:
from sqlalchemy import create_engine, MetaData, Table
from sqlalchemy.orm import mapper, sessionmaker
import requests
import json
from elasticsearch import Elasticsearch
import re


my_index = 'zadolbali'

In [25]:
print(requests.get('http://localhost:9200').text)
es = Elasticsearch([{'host': 'localhost', 'port': 9200}])


{
  "name" : "-L9u61j",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "7yPgKxlWRwmSpPqu9E9w2A",
  "version" : {
    "number" : "5.6.3",
    "build_hash" : "1a2f265",
    "build_date" : "2017-10-06T20:33:39.012Z",
    "build_snapshot" : false,
    "lucene_version" : "6.6.1"
  },
  "tagline" : "You Know, for Search"
}


In [26]:
def get_es_stats():
    print(requests.get('http://localhost:9200/_cat/health?v').text)
    print(requests.get('http://localhost:9200/_cat/nodes?v').text)
    print(requests.get('http://localhost:9200/_cat/shards?v').text)
    print(requests.get('http://localhost:9200/_cat/indices?v').text)

get_es_stats()


epoch      timestamp cluster       status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1509974899 16:28:19  elasticsearch yellow          1         1      6   6    0    0        6             0                  -                 50.0%

ip        heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
127.0.0.1           20          72  45    0.93    0.76     0.56 mdi       *      -L9u61j

index     shard prirep state      docs  store ip        node
zadolbali 4     p      STARTED    4752 18.2mb 127.0.0.1 -L9u61j
zadolbali 4     r      UNASSIGNED                       
zadolbali 2     p      STARTED    4746 18.9mb 127.0.0.1 -L9u61j
zadolbali 2     r      UNASSIGNED                       
zadolbali 3     p      STARTED    4729 18.5mb 127.0.0.1 -L9u61j
zadolbali 3     r      UNASSIGNED                       
zadolbali 1     p      STARTED    4686 18.5mb 127.0.0.1 -L9u61j
zadolbali 1     r      UNASSIGNED                       
zadolbali 0     p      STARTED    4645 18.2mb 127.0.0.1 -L9u61j
zadolbali 0     r      UNASSIGNED                       
.kibana   0     p      STARTED       2 10.8kb 127.0.0.1 -L9u61j
.kibana   0     r      UNASSIGNED                       

health status index     uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   .kibana   L5hEXnBERGe6czr2OJchfg   1   1          2            1     10.8kb         10.8kb
yellow open   zadolbali o6c21JfcRimc3IUs4LUJ0g   5   1      23558            0     92.5mb         92.5mb

Delete zadolbali index


In [4]:
def delete_index(index):
    return requests.delete('http://localhost:9200/{0}?pretty'.format(index)).text
print(delete_index(my_index))


{
  "acknowledged" : true
}


In [5]:
get_es_stats()


epoch      timestamp cluster       status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1509890898 17:08:18  elasticsearch yellow          1         1      1   1    0    0        1             0                  -                 50.0%

ip        heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
127.0.0.1           14          69  22    1.29    1.50     1.26 mdi       *      -L9u61j

index   shard prirep state      docs  store ip        node
.kibana 0     p      STARTED       2 10.8kb 127.0.0.1 -L9u61j
.kibana 0     r      UNASSIGNED                       

health status index   uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   .kibana L5hEXnBERGe6czr2OJchfg   1   1          2            1     10.8kb         10.8kb

Create index


In [6]:
def create_index(index, settings):
    return requests.put('http://localhost:9200/zadolbali', data=json.dumps(settings)).text

def setup_index_mapping(index, settings):
    headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
    return requests.put('http://localhost:9200/zadolbali/_mappings/story?pretty', headers=headers, data=json.dumps(settings)).text

def get_index_state(index):
    print(requests.get('http://localhost:9200/{0}/_settings?pretty'.format(index)).text)
    print(requests.get('http://localhost:9200/{0}/_mapping?pretty'.format(index)).text)

In [7]:
create_settings = {
    'settings' : {
        'index' : {
            'number_of_shards' : 5,
            'number_of_replicas' : 1
        }
    }
}

create_index(my_index, create_settings)


Out[7]:
'{"acknowledged":true,"shards_acknowledged":true,"index":"zadolbali"}'

In [8]:
mapping_settings = {
    'properties': {
        'id': { 'type': 'integer' },
        'title':  { 
            'type': 'text',
            'analyzer': 'russian'
        },
        'text': { 
            'type': 'text',
            'analyzer': 'russian'
        },
        'published': {
            'type': 'date',
            'format': 'yyyyMMdd'
            },
        'likes': { 'type': 'integer' },
        'tags': { 
            'type': 'keyword'
        },
        'url': { 'type': 'text' }
    }
}

print(setup_index_mapping(my_index, mapping_settings))


{
  "acknowledged" : true
}


In [9]:
get_index_state(my_index)


{
  "zadolbali" : {
    "settings" : {
      "index" : {
        "creation_date" : "1509890899241",
        "number_of_shards" : "5",
        "number_of_replicas" : "1",
        "uuid" : "f2SHcUU0Rcmn9ONHxhHQBg",
        "version" : {
          "created" : "5060399"
        },
        "provided_name" : "zadolbali"
      }
    }
  }
}

{
  "zadolbali" : {
    "mappings" : {
      "story" : {
        "properties" : {
          "id" : {
            "type" : "integer"
          },
          "likes" : {
            "type" : "integer"
          },
          "published" : {
            "type" : "date",
            "format" : "yyyyMMdd"
          },
          "tags" : {
            "type" : "keyword"
          },
          "text" : {
            "type" : "text",
            "analyzer" : "russian"
          },
          "title" : {
            "type" : "text",
            "analyzer" : "russian"
          },
          "url" : {
            "type" : "text"
          }
        }
      }
    }
  }
}

Get data


In [10]:
class Story(object):
    pass
 
def loadSession():
    dbPath = '../corpus/stories.sqlite'
    engine = create_engine('sqlite:///%s' % dbPath, echo=True)
 
    bookmarks = Table('stories', MetaData(engine), autoload=True)
    mapper(Story, bookmarks)
 
    Session = sessionmaker(bind=engine)
    session = Session()
    return session

session = loadSession()


2017-11-05 17:08:21,208 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
2017-11-05 17:08:21,209 INFO sqlalchemy.engine.base.Engine ()
2017-11-05 17:08:21,210 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
2017-11-05 17:08:21,211 INFO sqlalchemy.engine.base.Engine ()
2017-11-05 17:08:21,212 INFO sqlalchemy.engine.base.Engine PRAGMA table_info("stories")
2017-11-05 17:08:21,213 INFO sqlalchemy.engine.base.Engine ()
2017-11-05 17:08:21,216 INFO sqlalchemy.engine.base.Engine SELECT sql FROM  (SELECT * FROM sqlite_master UNION ALL   SELECT * FROM sqlite_temp_master) WHERE name = 'stories' AND type = 'table'
2017-11-05 17:08:21,217 INFO sqlalchemy.engine.base.Engine ()
2017-11-05 17:08:21,222 INFO sqlalchemy.engine.base.Engine PRAGMA foreign_key_list("stories")
2017-11-05 17:08:21,223 INFO sqlalchemy.engine.base.Engine ()
2017-11-05 17:08:21,224 INFO sqlalchemy.engine.base.Engine SELECT sql FROM  (SELECT * FROM sqlite_master UNION ALL   SELECT * FROM sqlite_temp_master) WHERE name = 'stories' AND type = 'table'
2017-11-05 17:08:21,225 INFO sqlalchemy.engine.base.Engine ()
2017-11-05 17:08:21,229 INFO sqlalchemy.engine.base.Engine PRAGMA index_list("stories")
2017-11-05 17:08:21,230 INFO sqlalchemy.engine.base.Engine ()
2017-11-05 17:08:21,231 INFO sqlalchemy.engine.base.Engine PRAGMA index_list("stories")
2017-11-05 17:08:21,232 INFO sqlalchemy.engine.base.Engine ()
2017-11-05 17:08:21,233 INFO sqlalchemy.engine.base.Engine SELECT sql FROM  (SELECT * FROM sqlite_master UNION ALL   SELECT * FROM sqlite_temp_master) WHERE name = 'stories' AND type = 'table'
2017-11-05 17:08:21,234 INFO sqlalchemy.engine.base.Engine ()

In [56]:
stories = session.query(Story).all()
print(len(stories))
print(dir(stories[0]))


2017-11-06 17:06:43,336 INFO sqlalchemy.engine.base.Engine SELECT stories.id AS stories_id, stories.title AS stories_title, stories.published AS stories_published, stories.tags AS stories_tags, stories.text AS stories_text, stories.likes AS stories_likes, stories.hrefs AS stories_hrefs, stories.url AS stories_url 
FROM stories
2017-11-06 17:06:43,338 INFO sqlalchemy.engine.base.Engine ()
23558
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_sa_class_manager', '_sa_instance_state', 'hrefs', 'id', 'likes', 'published', 'tags', 'text', 'title', 'url']

Index it!


In [12]:
# 'hrefs', 'id', 'likes', 'published', 'tags', 'text', 'title', 'url'
def index_data(index):
    for story in stories:
        body = {
            'id': story.id,
            'title': story.title,
            'text': story.text,
            'published': story.published,
            'likes': story.likes,
            'tags': story.tags.split(' '),
            'url': story.url
        }
        es.index(index=index, doc_type='story', id=story.id, body=body)
index_data(my_index)

Search it!


In [13]:
def run_easy_tests(index):
    print(es.get(index=index, id=2))
    print(es.search(index=index, doc_type='story', body={'query': {'match': {'tags': 'mail'}}}))

In [14]:
print(run_easy_tests(my_index))


{'_index': 'zadolbali', '_type': 'story', '_id': '2', '_version': 1, 'found': True, '_source': {'id': 2, 'title': 'Это был не металл!', 'text': 'Работаю в провинциальном городе в магазине отделочных материалов и сантехники.Заходит к нам на днях надменная пергидролевая дева, покрытая слоем штукатурки толщиной в палец. Собирается с мыслями, напускает на себя важный вид и обращается ко мне:Дева, медленно и с видом опытного сантехника: Молодой человек, у вас ванны железные есть?Я: Нет, у нас только акрил. Металлических нет.Дева: Молодой человек, я не спрашиваю металлические, я спрашиваю железные!Я: Извините, железных тоже нет.Дева презрительно смотрит на меня, бурчит что-то себе под нос, и, виляя бедрами, уходит. Смотрим в окно. Выходит. Подходит к побитой жизнью шестерке, деловито садится на переднее сиденье, подзывает торопливо курящего поодаль водителя.Дева, возмущенно: Понабрали крестьян, металлические ванны от железных не отличают!Водитель, тяжело вздохнув, затаптывает окурок, занимает свое место, и экипаж отправляется дальше, на поиски волшебной неметаллической ванны из железа.', 'published': 20090910, 'likes': 0, 'tags': ['women', 'sellers'], 'url': 'http://zadolba.li/story/2'}}
{'took': 1, 'timed_out': False, '_shards': {'total': 5, 'successful': 5, 'skipped': 0, 'failed': 0}, 'hits': {'total': 3, 'max_score': 2.4849067, 'hits': [{'_index': 'zadolbali', '_type': 'story', '_id': '6', '_score': 2.4849067, '_source': {'id': 6, 'title': 'Мы его почти победили!', 'text': 'Переписка с клиентом:Я: Здравствуйте, а у вас есть рабочий номер?Клиент: тел. хх-хх-ххЯ: Скажите тогда, почему там срабатывает факс?Клиент: Да, есть такая проблема. Мы боремся с ней!Что делать, за факсом нужен глаз да глаз.', 'published': 20091006, 'likes': 828, 'tags': ['mail'], 'url': 'http://zadolba.li/story/6'}}, {'_index': 'zadolbali', '_type': 'story', '_id': '5', '_score': 2.2192035, '_source': {'id': 5, 'title': 'Атака одушевленного обьекта', 'text': 'Работаю в страховой компании. Любимые клиенты пишут:«На крышу мерседеса упала с балкона кошка. Cуществуют риски "падение предмета" и "атака животного". Что писать в страховом акте?»Претензионщики мрачно шутят, что если бы она упала и померла в полете — то было бы проще. Записали бы предметом.', 'published': 20090915, 'likes': 0, 'tags': ['insurers', 'mail'], 'url': 'http://zadolba.li/story/5'}}, {'_index': 'zadolbali', '_type': 'story', '_id': '104', '_score': 2.2192035, '_source': {'id': 104, 'title': 'Страшно, аж жуть!', 'text': 'Руководил филиалом компании продающей грузовики, по долгу службы общался с директором одной компании, которой была нужна наша продукция. За время беседы стало понятно, что деньги у них есть, машины нужны, так как в кризис производство не упало. Но клиент почему-то мялся и машины не брал.Я: Для начала вы можете приобрести один автомобиль!Клиент: Вы знаете, кризис в стране, мы сейчас не готовы брать машины...Я: Но ведь вашему производству они необходимы?Клиент: Да. Очень нужны, как воздух.Я: Вас не устраивают наши машины?Клиент: Они идеально нам подходят, но кризис...Я: Вы считаете их дорогими?Клиент: Нет, цена лучшая в сегменте.Я: Наша компания готова в случае необходимости сама, без банков, кредитовать вас!Клиент: Нет, спасибо, деньги у нас есть. Просто кризис.Я: Но ведь ваше производство может встать!Клиент: Может. Но мы боимся, кризис...Так и не смог убедить этого клиента. Дело происходил в ноябре 2008 г. Этим летом, уже не работая в компании, я встретился с директором той конторы случайно. Решил узнать, как у них дела.Бывший клиент: Да туго, машины так и не купили, не справляемся.Я: А почему не купили?Бывший клиент: Кризис... Боимся...', 'published': 20091019, 'likes': 1517, 'tags': ['mail'], 'url': 'http://zadolba.li/story/104'}}]}}
None

Поищем что-то посложнее..


In [15]:
def run_hard_tests(index):
    print(es.search(index=index, doc_type='story', body={'query': {'match': {'text': 'страховая'}}}))
    print(es.search(index=index, doc_type='story', body={'query': {'match': {'text': 'страхов'}}}))

In [16]:
print(run_hard_tests(my_index))


{'took': 2, 'timed_out': False, '_shards': {'total': 5, 'successful': 5, 'skipped': 0, 'failed': 0}, 'hits': {'total': 3, 'max_score': 4.0308046, 'hits': [{'_index': 'zadolbali', '_type': 'story', '_id': '5', '_score': 4.0308046, '_source': {'id': 5, 'title': 'Атака одушевленного обьекта', 'text': 'Работаю в страховой компании. Любимые клиенты пишут:«На крышу мерседеса упала с балкона кошка. Cуществуют риски "падение предмета" и "атака животного". Что писать в страховом акте?»Претензионщики мрачно шутят, что если бы она упала и померла в полете — то было бы проще. Записали бы предметом.', 'published': 20090915, 'likes': 0, 'tags': ['insurers', 'mail'], 'url': 'http://zadolba.li/story/5'}}, {'_index': 'zadolbali', '_type': 'story', '_id': '85', '_score': 2.2460778, '_source': {'id': 85, 'title': 'Готовил сани летом', 'text': 'Работаю в страховой компании. В правилах обслуживания чётко, русским по белому написано, что заявить о страховом случае в компанию необходимо в течении трёх рабочих дней. Когда я заключаю договор с клиентами, я заостряю внимание на этом моменте и даю визитку — если что-то случится, а вы не будете знать порядок действий, звоните.Ага-ага. Вчера звонит возмущенный мужчина: «Ах, такие сякие, вы мне отказали в выплате!» Начинаю разбираться, в чём суть дела, звоню коллегам, занимающимися выплатами и наконец понимаю причину отказа.Заявлении о происшествии, написанное 15 августа: «Я счищал оледенелый снег с капота своего автомобиля вместе с лакокрасочным покрытием...»Звоню ему и спрашиваю: «А где же, друг мой любезный, ты в середине августа оледенелый снег нашел?» Он только смеётся: «Ты чё, не понимаешь что ли, это было в феврале! Какой ещё снег в августе?»Эх, как же вы мне все дороги...', 'published': 20091016, 'likes': 1188, 'tags': ['insurers'], 'url': 'http://zadolba.li/story/85'}}, {'_index': 'zadolbali', '_type': 'story', '_id': '55', '_score': 1.4884255, '_source': {'id': 55, 'title': 'Кошмары и ужасы страхования', 'text': 'Работаю в страховой компании. Каждый день происходят совершенно неземные диалоги с клиентами:— Девушка, я вам вчера звонил, соедените меня с тем же самым.— С кем вы разговаривали?— С парнем.— Как его зовут?— Не знаю.— Про что вы у него узнавали?— Про страхование!— Страхование чего? (злость начинает подступать к горлу)— Машины!— Соединяю с отделом КАСКО. (через минуту) У них телефон занят. Что передать?— Передайте, что я звонил! *вешает трубку*— Девушка!— Да, я вас слушаю.— А это точно %компания%?— Да, это %компания%.— Вы меня не обманываете?— Нет! Конечно же, вы попали именно в %компанию%. Чем вам помочь?— Да? А, ну ладно, спасибо. *короткие гудки*— Дэвущка! Это страховка?— %Компания%, я вас слушаю.— Дэнэг нет, ехал из Казан, попал аварий, что дэлать?— Вы повредили автомобиль или (чуть не сказала «испортились») что-то произошло с вами?— И машын, и я!— У вас что застраховано?— Всё!— И жизнь, и авто?— Да!Соединяю с мальчшками из авто отдела. Оказывается, что мужчина въехал в столб, застрахован только по ОСАГО, да ещё и в другой компании. А нам он позвонил, потому что «ваш телефон на рекламе увидел».', 'published': 20091014, 'likes': 1070, 'tags': ['insurers'], 'url': 'http://zadolba.li/story/55'}}]}}
{'took': 2, 'timed_out': False, '_shards': {'total': 5, 'successful': 5, 'skipped': 0, 'failed': 0}, 'hits': {'total': 0, 'max_score': None, 'hits': []}}
None

Ага! У нас не работает стеммер!


In [17]:
print(delete_index(my_index))
print(create_index(my_index, create_settings))
print(requests.post('http://localhost:9200/zadolbali/_close').text)


{
  "acknowledged" : true
}

{"acknowledged":true,"shards_acknowledged":true,"index":"zadolbali"}
{"acknowledged":true}

In [18]:
def setup_index_settings(index, settings):
    headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
    return requests.put('http://localhost:9200/zadolbali/_settings?pretty', headers=headers, data=json.dumps(settings)).text

In [19]:
# stolen from https://gist.github.com/svartalf/4465752

index_settings = {
    'analysis': {
        'analyzer': {
            'ru': {
                'type': 'custom',
                'tokenizer': 'standard',
                'filter': ['lowercase', 'russian_morphology', 'english_morphology', 'ru_stopwords'],
            },
        },
        'filter': {
            'ru_stopwords': {
                'type': 'stop',
                'stopwords': u'а,без,более,бы,был,была,были,было,быть,в,вам,вас,весь,во,вот,все,всего,всех,вы,где,да,даже,для,до,его,ее,если,есть,еще,же,за,здесь,и,из,или,им,их,к,как,ко,когда,кто,ли,либо,мне,может,мы,на,надо,наш,не,него,нее,нет,ни,них,но,ну,о,об,однако,он,она,они,оно,от,очень,по,под,при,с,со,так,также,такой,там,те,тем,то,того,тоже,той,только,том,ты,у,уже,хотя,чего,чей,чем,что,чтобы,чье,чья,эта,эти,это,я,a,an,and,are,as,at,be,but,by,for,if,in,into,is,it,no,not,of,on,or,such,that,the,their,then,there,these,they,this,to,was,will,with',
            },
            'ru_stemming': {
                'type': 'snowball',
                'language': 'Russian',
            }
        },

    }
}


print(setup_index_settings(my_index, index_settings))


{
  "acknowledged" : true
}


In [20]:
print(requests.post('http://localhost:9200/zadolbali/_open').text)

mapping_settings = {
    'properties': {
        'id': { 'type': 'integer' },
        'title':  { 
            'type': 'text',
            'analyzer': 'ru'
        },
        'text': { 
            'type': 'text',
            'analyzer': 'ru'
        },
        'published': {
            'type': 'date',
            'format': 'yyyyMMdd'
            },
        'likes': { 'type': 'integer' },
        'tags': { 
            'type': 'keyword'
        },
        'url': { 'type': 'text' }
    }
}

print(setup_index_mapping(my_index, mapping_settings))


{"acknowledged":true}
{
  "acknowledged" : true
}


In [21]:
index_data(my_index)

In [22]:
get_index_state(my_index)


{
  "zadolbali" : {
    "settings" : {
      "index" : {
        "number_of_shards" : "5",
        "provided_name" : "zadolbali",
        "creation_date" : "1509890910315",
        "analysis" : {
          "filter" : {
            "ru_stemming" : {
              "type" : "snowball",
              "language" : "Russian"
            },
            "ru_stopwords" : {
              "type" : "stop",
              "stopwords" : "а,без,более,бы,был,была,были,было,быть,в,вам,вас,весь,во,вот,все,всего,всех,вы,где,да,даже,для,до,его,ее,если,есть,еще,же,за,здесь,и,из,или,им,их,к,как,ко,когда,кто,ли,либо,мне,может,мы,на,надо,наш,не,него,нее,нет,ни,них,но,ну,о,об,однако,он,она,они,оно,от,очень,по,под,при,с,со,так,также,такой,там,те,тем,то,того,тоже,той,только,том,ты,у,уже,хотя,чего,чей,чем,что,чтобы,чье,чья,эта,эти,это,я,a,an,and,are,as,at,be,but,by,for,if,in,into,is,it,no,not,of,on,or,such,that,the,their,then,there,these,they,this,to,was,will,with"
            }
          },
          "analyzer" : {
            "ru" : {
              "filter" : [
                "lowercase",
                "russian_morphology",
                "english_morphology",
                "ru_stopwords"
              ],
              "type" : "custom",
              "tokenizer" : "standard"
            }
          }
        },
        "number_of_replicas" : "1",
        "uuid" : "fLOPKwa0QC6oXijkEjrP2A",
        "version" : {
          "created" : "5060399"
        }
      }
    }
  }
}

{
  "zadolbali" : {
    "mappings" : {
      "story" : {
        "properties" : {
          "id" : {
            "type" : "integer"
          },
          "likes" : {
            "type" : "integer"
          },
          "published" : {
            "type" : "date",
            "format" : "yyyyMMdd"
          },
          "tags" : {
            "type" : "keyword"
          },
          "text" : {
            "type" : "text",
            "analyzer" : "ru"
          },
          "title" : {
            "type" : "text",
            "analyzer" : "ru"
          },
          "url" : {
            "type" : "text"
          }
        }
      }
    }
  }
}


In [23]:
print(run_hard_tests(my_index))


{'took': 4, 'timed_out': False, '_shards': {'total': 5, 'successful': 5, 'skipped': 0, 'failed': 0}, 'hits': {'total': 3, 'max_score': 4.2066126, 'hits': [{'_index': 'zadolbali', '_type': 'story', '_id': '5', '_score': 4.2066126, '_source': {'id': 5, 'title': 'Атака одушевленного обьекта', 'text': 'Работаю в страховой компании. Любимые клиенты пишут:«На крышу мерседеса упала с балкона кошка. Cуществуют риски "падение предмета" и "атака животного". Что писать в страховом акте?»Претензионщики мрачно шутят, что если бы она упала и померла в полете — то было бы проще. Записали бы предметом.', 'published': 20090915, 'likes': 0, 'tags': ['insurers', 'mail'], 'url': 'http://zadolba.li/story/5'}}, {'_index': 'zadolbali', '_type': 'story', '_id': '85', '_score': 2.3874907, '_source': {'id': 85, 'title': 'Готовил сани летом', 'text': 'Работаю в страховой компании. В правилах обслуживания чётко, русским по белому написано, что заявить о страховом случае в компанию необходимо в течении трёх рабочих дней. Когда я заключаю договор с клиентами, я заостряю внимание на этом моменте и даю визитку — если что-то случится, а вы не будете знать порядок действий, звоните.Ага-ага. Вчера звонит возмущенный мужчина: «Ах, такие сякие, вы мне отказали в выплате!» Начинаю разбираться, в чём суть дела, звоню коллегам, занимающимися выплатами и наконец понимаю причину отказа.Заявлении о происшествии, написанное 15 августа: «Я счищал оледенелый снег с капота своего автомобиля вместе с лакокрасочным покрытием...»Звоню ему и спрашиваю: «А где же, друг мой любезный, ты в середине августа оледенелый снег нашел?» Он только смеётся: «Ты чё, не понимаешь что ли, это было в феврале! Какой ещё снег в августе?»Эх, как же вы мне все дороги...', 'published': 20091016, 'likes': 1188, 'tags': ['insurers'], 'url': 'http://zadolba.li/story/85'}}, {'_index': 'zadolbali', '_type': 'story', '_id': '55', '_score': 1.6297764, '_source': {'id': 55, 'title': 'Кошмары и ужасы страхования', 'text': 'Работаю в страховой компании. Каждый день происходят совершенно неземные диалоги с клиентами:— Девушка, я вам вчера звонил, соедените меня с тем же самым.— С кем вы разговаривали?— С парнем.— Как его зовут?— Не знаю.— Про что вы у него узнавали?— Про страхование!— Страхование чего? (злость начинает подступать к горлу)— Машины!— Соединяю с отделом КАСКО. (через минуту) У них телефон занят. Что передать?— Передайте, что я звонил! *вешает трубку*— Девушка!— Да, я вас слушаю.— А это точно %компания%?— Да, это %компания%.— Вы меня не обманываете?— Нет! Конечно же, вы попали именно в %компанию%. Чем вам помочь?— Да? А, ну ладно, спасибо. *короткие гудки*— Дэвущка! Это страховка?— %Компания%, я вас слушаю.— Дэнэг нет, ехал из Казан, попал аварий, что дэлать?— Вы повредили автомобиль или (чуть не сказала «испортились») что-то произошло с вами?— И машын, и я!— У вас что застраховано?— Всё!— И жизнь, и авто?— Да!Соединяю с мальчшками из авто отдела. Оказывается, что мужчина въехал в столб, застрахован только по ОСАГО, да ещё и в другой компании. А нам он позвонил, потому что «ваш телефон на рекламе увидел».', 'published': 20091014, 'likes': 1070, 'tags': ['insurers'], 'url': 'http://zadolba.li/story/55'}}]}}
{'took': 6, 'timed_out': False, '_shards': {'total': 5, 'successful': 5, 'skipped': 0, 'failed': 0}, 'hits': {'total': 3, 'max_score': 3.21028, 'hits': [{'_index': 'zadolbali', '_type': 'story', '_id': '5', '_score': 3.21028, '_source': {'id': 5, 'title': 'Атака одушевленного обьекта', 'text': 'Работаю в страховой компании. Любимые клиенты пишут:«На крышу мерседеса упала с балкона кошка. Cуществуют риски "падение предмета" и "атака животного". Что писать в страховом акте?»Претензионщики мрачно шутят, что если бы она упала и померла в полете — то было бы проще. Записали бы предметом.', 'published': 20090915, 'likes': 0, 'tags': ['insurers', 'mail'], 'url': 'http://zadolba.li/story/5'}}, {'_index': 'zadolbali', '_type': 'story', '_id': '55', '_score': 1.6297764, '_source': {'id': 55, 'title': 'Кошмары и ужасы страхования', 'text': 'Работаю в страховой компании. Каждый день происходят совершенно неземные диалоги с клиентами:— Девушка, я вам вчера звонил, соедените меня с тем же самым.— С кем вы разговаривали?— С парнем.— Как его зовут?— Не знаю.— Про что вы у него узнавали?— Про страхование!— Страхование чего? (злость начинает подступать к горлу)— Машины!— Соединяю с отделом КАСКО. (через минуту) У них телефон занят. Что передать?— Передайте, что я звонил! *вешает трубку*— Девушка!— Да, я вас слушаю.— А это точно %компания%?— Да, это %компания%.— Вы меня не обманываете?— Нет! Конечно же, вы попали именно в %компанию%. Чем вам помочь?— Да? А, ну ладно, спасибо. *короткие гудки*— Дэвущка! Это страховка?— %Компания%, я вас слушаю.— Дэнэг нет, ехал из Казан, попал аварий, что дэлать?— Вы повредили автомобиль или (чуть не сказала «испортились») что-то произошло с вами?— И машын, и я!— У вас что застраховано?— Всё!— И жизнь, и авто?— Да!Соединяю с мальчшками из авто отдела. Оказывается, что мужчина въехал в столб, застрахован только по ОСАГО, да ещё и в другой компании. А нам он позвонил, потому что «ваш телефон на рекламе увидел».', 'published': 20091014, 'likes': 1070, 'tags': ['insurers'], 'url': 'http://zadolba.li/story/55'}}, {'_index': 'zadolbali', '_type': 'story', '_id': '85', '_score': 1.6297764, '_source': {'id': 85, 'title': 'Готовил сани летом', 'text': 'Работаю в страховой компании. В правилах обслуживания чётко, русским по белому написано, что заявить о страховом случае в компанию необходимо в течении трёх рабочих дней. Когда я заключаю договор с клиентами, я заостряю внимание на этом моменте и даю визитку — если что-то случится, а вы не будете знать порядок действий, звоните.Ага-ага. Вчера звонит возмущенный мужчина: «Ах, такие сякие, вы мне отказали в выплате!» Начинаю разбираться, в чём суть дела, звоню коллегам, занимающимися выплатами и наконец понимаю причину отказа.Заявлении о происшествии, написанное 15 августа: «Я счищал оледенелый снег с капота своего автомобиля вместе с лакокрасочным покрытием...»Звоню ему и спрашиваю: «А где же, друг мой любезный, ты в середине августа оледенелый снег нашел?» Он только смеётся: «Ты чё, не понимаешь что ли, это было в феврале! Какой ещё снег в августе?»Эх, как же вы мне все дороги...', 'published': 20091016, 'likes': 1188, 'tags': ['insurers'], 'url': 'http://zadolba.li/story/85'}}]}}
None

Теперь работает. :)

Analytics


In [63]:
print(requests.post('http://localhost:9200/zadolbali/story/_search?pretty', data=json.dumps({
    "size": 0,
    "aggs" : {
        "genres" : {
            "terms" : { 
                "field" : "text",
                "size" : 100 
            }
        }
    }
})).text)


{
  "error" : {
    "root_cause" : [
      {
        "type" : "illegal_argument_exception",
        "reason" : "Fielddata is disabled on text fields by default. Set fielddata=true on [text] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead."
      }
    ],
    "type" : "search_phase_execution_exception",
    "reason" : "all shards failed",
    "phase" : "query",
    "grouped" : true,
    "failed_shards" : [
      {
        "shard" : 0,
        "index" : "zadolbali",
        "node" : "-L9u61j5TzaMtOXdBGq9jA",
        "reason" : {
          "type" : "illegal_argument_exception",
          "reason" : "Fielddata is disabled on text fields by default. Set fielddata=true on [text] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead."
        }
      }
    ]
  },
  "status" : 400
}

Сравним с линейным поиском


In [78]:
all_text = ' '.join(story.text for story in stories)
# print(all_text)

In [76]:
%%timeit 
re.findall('кошка', all_text)


10000 loops, best of 3: 84 µs per loop

In [77]:
%%timeit
es.search(index='zadolbali', doc_type='story', body={'query': {'match': {'text': 'кошка'}}})


1000 loops, best of 3: 1.29 ms per loop

In [81]:
%%timeit 
re.findall('кошка', all_text)


10 loops, best of 3: 60.1 ms per loop

In [85]:
%%timeit
es.search(index='zadolbali', doc_type='story', body={'query': {'match': {'text': 'кошка'}}})


100 loops, best of 3: 1.91 ms per loop

In [86]:
%%timeit 
re.findall('я', all_text)


10 loops, best of 3: 96.2 ms per loop

In [87]:
%%timeit
es.search(index='zadolbali', doc_type='story', body={'query': {'match': {'text': 'я'}}})


1000 loops, best of 3: 1.06 ms per loop

In [86]:
%%timeit 
re.findall('страховая', all_text)


10 loops, best of 3: 96.2 ms per loop

In [87]:
%%timeit
es.search(index='zadolbali', doc_type='story', body={'query': {'match': {'text': 'я'}}})


1000 loops, best of 3: 1.06 ms per loop

In [99]:
%%timeit 
re.findall('душистый', all_text)


10 loops, best of 3: 59.3 ms per loop

In [100]:
%%timeit
es.search(index='zadolbali', doc_type='story', body={'query': {'match': {'text': 'душистый'}}})


100 loops, best of 3: 1.91 ms per loop

In [ ]: