fias 0.0.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -22
  3. data/.rubocop.yml +7 -0
  4. data/.travis.yml +10 -0
  5. data/Gemfile +1 -1
  6. data/LICENSE.txt +2 -2
  7. data/README.md +259 -155
  8. data/Rakefile +6 -1
  9. data/config/names.txt +0 -0
  10. data/config/synonyms.yml +50 -0
  11. data/examples/create.rb +106 -0
  12. data/examples/generate_index.rb +63 -0
  13. data/fias.gemspec +33 -21
  14. data/lib/fias.rb +197 -10
  15. data/lib/fias/config.rb +74 -0
  16. data/lib/fias/import/copy.rb +62 -0
  17. data/lib/fias/import/dbf.rb +81 -0
  18. data/lib/fias/import/download_service.rb +37 -0
  19. data/lib/fias/import/restore_parent_id.rb +51 -0
  20. data/lib/fias/import/tables.rb +74 -0
  21. data/lib/fias/name/append.rb +30 -0
  22. data/lib/fias/name/canonical.rb +42 -0
  23. data/lib/fias/name/extract.rb +85 -0
  24. data/lib/fias/name/house_number.rb +71 -0
  25. data/lib/fias/name/split.rb +60 -0
  26. data/lib/fias/name/synonyms.rb +93 -0
  27. data/lib/fias/query.rb +43 -0
  28. data/lib/fias/query/estimate.rb +67 -0
  29. data/lib/fias/query/finder.rb +75 -0
  30. data/lib/fias/query/params.rb +101 -0
  31. data/lib/fias/railtie.rb +3 -17
  32. data/lib/fias/version.rb +1 -1
  33. data/spec/fixtures/ACTSTAT.DBF +0 -0
  34. data/spec/fixtures/NORDOC99.DBF +0 -0
  35. data/spec/fixtures/STRSTAT.DBF +0 -0
  36. data/spec/fixtures/addressing.yml +93 -0
  37. data/spec/fixtures/query.yml +79 -0
  38. data/spec/fixtures/query_sanitization.yml +75 -0
  39. data/spec/fixtures/status_append.yml +60 -0
  40. data/spec/lib/import/copy_spec.rb +44 -0
  41. data/spec/lib/import/dbf_spec.rb +28 -0
  42. data/spec/lib/import/download_service_spec.rb +15 -0
  43. data/spec/lib/import/restore_parent_id_spec.rb +34 -0
  44. data/spec/lib/import/tables_spec.rb +26 -0
  45. data/spec/lib/name/append_spec.rb +14 -0
  46. data/spec/lib/name/canonical_spec.rb +20 -0
  47. data/spec/lib/name/extract_spec.rb +67 -0
  48. data/spec/lib/name/house_number_spec.rb +45 -0
  49. data/spec/lib/name/query_spec.rb +21 -0
  50. data/spec/lib/name/split_spec.rb +15 -0
  51. data/spec/lib/name/synonyms_spec.rb +51 -0
  52. data/spec/lib/query/params_spec.rb +15 -0
  53. data/spec/lib/query_spec.rb +27 -0
  54. data/spec/spec_helper.rb +30 -0
  55. data/spec/support/db.rb +30 -0
  56. data/spec/support/query.rb +13 -0
  57. data/tasks/db.rake +52 -0
  58. data/tasks/download.rake +15 -0
  59. metadata +246 -64
  60. data/lib/fias/active_record/address_object.rb +0 -231
  61. data/lib/fias/active_record/address_object_type.rb +0 -15
  62. data/lib/fias/dbf_wrapper.rb +0 -90
  63. data/lib/fias/importer.rb +0 -30
  64. data/lib/fias/importer/base.rb +0 -59
  65. data/lib/fias/importer/pg.rb +0 -81
  66. data/lib/fias/importer/sqlite.rb +0 -38
  67. data/lib/generators/fias/migration.rb +0 -34
  68. data/lib/generators/fias/templates/create_fias_tables.rb +0 -5
  69. data/tasks/fias.rake +0 -68
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 74d2bb526b9c5ea3f360d6ac0dd82ffed3c16f6c
4
+ data.tar.gz: 5a902f0aea4bad5260a8502bd0276f5f62ba3113
5
+ SHA512:
6
+ metadata.gz: 9d19fc34bb832abacd1579193db308d29e0278adeef76b62abd6a8c9b164a1eb7dd9d993a6bb650bd3dfa932e70488943ec25ebc0d6eff4d27121bf3c05e1ab4
7
+ data.tar.gz: f4bfabda5028093b5107cf98ca8e4d7a0ee7d7ff0f95e9df9a967386ec6398c8c2248bc4809a3455d6ee05adfd2bdad7f86fa2c954094e3f2ef62a9f2baf1fc9
data/.gitignore CHANGED
@@ -1,22 +1,15 @@
1
- *.gem
2
- *.rbc
3
- .bundle
4
- .config
5
- .yardoc
6
- Gemfile.lock
7
- InstalledFiles
8
- _yardoc
9
- coverage
10
- doc/
11
- coverage
12
- InstalledFiles
13
- tmp
14
- pkg
15
- rdoc
16
- spec/reports
17
- test/tmp
18
- test/version_tmp
19
- tmp
20
- .yardoc
21
- _yardoc
22
- doc/
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ test.rb
@@ -0,0 +1,7 @@
1
+ AllCops:
2
+ Include:
3
+ - ./Gemfile
4
+ Exclude:
5
+ - config/**/*
6
+ Documentation:
7
+ Enabled: false
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1
4
+ - 2.2
5
+ cache: bundler
6
+ sudo: false
7
+
8
+ addons:
9
+ code_climate:
10
+ repo_token: 11011732891441d394d80e3469bc3dc1c24e1766a1b017bf2c7680e90d4b9225
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in fias.gemspec
3
+ # Specify your gem's dependencies in fias-import.gemspec
4
4
  gemspec
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 Victor Sokolov
1
+ Copyright (c) 2015 Victor Sokolov
2
2
 
3
3
  MIT License
4
4
 
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
19
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
20
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
21
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,213 +1,317 @@
1
- # Федеральная Информационная Адресная Система (ФИАС)
1
+ # FIAS
2
2
 
3
- Для работы с базой потребуются файлы полной БД ФИАС в формате DBF. Скачать их можно по адресу:
3
+ [![Build Status](https://travis-ci.org/evilmartians/fias.svg)](http://travis-ci.org/evilmartians/fias)
4
+ [![Code Climate](https://codeclimate.com/github/evilmartians/fias/badges/gpa.svg)](https://codeclimate.com/github/evilmartians/fias)
5
+ [![Test Coverage](https://codeclimate.com/github/evilmartians/fias/badges/coverage.svg)](https://codeclimate.com/github/evilmartians/fias)
4
6
 
5
- http://fias.nalog.ru/Public/DownloadPage.aspx
7
+ Ruby wrapper for the Russian [ФИАС](http://fias.nalog.ru) database.
6
8
 
7
- Там же можно скачать описание структуры базы.
9
+ Designed for usage with Ruby on Rails and a PostgreSQL backend.
8
10
 
9
- # Установка и подготовка базы
11
+ <a href="https://evilmartians.com/?utm_source=fias-gem">
12
+ <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54">
13
+ </a>
10
14
 
11
- 1. Подключить гем
15
+ Think twice before you decide to use a standalone copy of FIAS database in your project. [КЛАДР в облаке](https://kladr-api.ru/) could also be a solution.
12
16
 
17
+ ## Installation
18
+
19
+ Add this line to your application's `Gemfile`:
20
+
21
+ ```ruby
22
+ gem 'fias'
13
23
  ```
14
- gem 'fias', git: 'https://github.com/evilmartians/kladr.git'
24
+
25
+ And then execute:
26
+
27
+ $ bundle
28
+
29
+ Or install it yourself:
30
+
31
+ $ gem install fias
32
+
33
+ ## Import into PostgreSQL
34
+
35
+ $ mkdir -p tmp/fias && cd tmp/fias
36
+ $ bundle exec rake fias:download | xargs wget
37
+ $ unrar e fias_dbf.rar
38
+ $ bundle exec rake fias:create_tables fias:import DATABASE_URL=postgres://localhost/fias
39
+
40
+ The rake task accepts options through ENV variables:
41
+
42
+ * `TABLES` to specify a comma-separated list of tables to import or create. See `Fias::Import::Dbf::TABLES` for the list of key names. Use `houses` as an alias for HOUSE* tables and `nordocs` for NORDOC* tables. In most cases you'll need only the `address_objects` table.
43
+ * `PREFIX` for database tables prefix ('fias_' by default).
44
+ * `PATH` to specify DBF files location ('tmp/fias' by default).
45
+ * `DATABASE_URL` to set database credentials (required explicitly even with a Ruby on Rails project).
46
+
47
+ This gem uses `COPY FROM STDIN BINARY` to import data. At the moment it works with PostgreSQL only.
48
+
49
+ ## Notes about FIAS
50
+
51
+ 1. FIAS address objects table contains a lot of fields which are useless in most cases (tax office ID, legal document ID, etc.).
52
+ 2. Address objects table contains a lot of historical records (more than 50%, in fact), which are useless in most cases.
53
+ 3. Every record in the address object table could have multiple parents. For example, "Nevsky prospekt" in Saint Petersburg has two parents: Saint Petersburg (active) and Leningrad (historical name of the city, inactive). Most hierarchy libraries do accept just one parent for a record.
54
+ 4. Using UUID type field as a primary key as it used in FIAS is not a good idea if you want to use `ancestry` or `closure_tree` gems to navigate through record tree.
55
+ 5. Typical SQL production server settings are optimized for reading, so the import process in production environment could take a dramatically long time.
56
+
57
+ ## Notes on initial import workflow
58
+
59
+ 1. Use raw FIAS tables just as a temporary data source for creating/updating primary address objects table for your application.
60
+ 2. The only requirement is to keep AOGUID, PARENTGUID and AOID fields in target table. You will need them for updating.
61
+ 3. Keep your addressing object table immutable. This will give you an ability to work with huge amounts of addressing data locally. Send the result to production environment via a SQL dump.
62
+ 4. FIAS contains some duplicates. Duplicates are records which have different UUIDs but equal names, abbrevations and nesting level. It is up to you to decide on how to deal with it: completely remove them or just mark as hidden. Krasnodar city has a lot of equally named streets situated in different districts.
63
+ 5. [closure_tree](https://github.com/mceachen/closure_tree) works great as a hierarchy backend. Use [pg_closure_tree_rebuild](https://github.com/gzigzigzeo/pg_closure_tree_rebuild) to rebuild the hierarchy table from scratch.
64
+
65
+ [See example](examples/create.rb).
66
+
67
+ ## Toponyms
68
+
69
+ Every FIAS address object has two fields: `formalname`, which holds the toponym (the name of a geographical object) and `shortname`, which holds its type (street, city, etc.). FIAS contains the list of all available `shortname` values and their corresponding long forms in the `address_object_types` table (SOCRBASE.DBF).
70
+
71
+ ### Canonical forms
72
+
73
+ In real life people use a lot of type name variations. For example, 'проспект' can be written as 'пр' or 'пр-кт'.
74
+
75
+ You can convert any variation to a canonical form:
76
+
77
+ ```ruby
78
+ Fias::Name::Canonical.canonical('поселок')
79
+ # => [
80
+ # 'поселок', # FIAS canonical full name
81
+ # 'п', # FIAS canonical short name (as in address_objects table)
82
+ # 'п.', # Short name with dot if needed
83
+ # 'пос', # Alias
84
+ # 'посёлок' # Alias
85
+ # ]
15
86
  ```
16
87
 
17
- 2. Скачать и распаковать базу ФИАС в tmp/fias (по умолчанию)
88
+ See [fias.rb](lib/fias.rb) for a list of settings.
89
+
90
+ ### Append type to toponym
18
91
 
19
- # Импорт структуры данных
92
+ Use `Fias::Name::Append` to build toponym names in conformity with the rules of grammar:
20
93
 
21
- Возможны два варианта:
94
+ ```ruby
95
+ Fias::Name::Append.append('Санкт-Петербург', 'г')
96
+ # => ['г. Санкт-Петербург', 'город Санкт-Петербург']
97
+
98
+ Fias::Name::Append.append('Невский', 'пр')
99
+ # => ['Невский пр-кт', 'Невский проспект']
100
+
101
+ Fias::Name::Append.append('Чечня', 'республика')
102
+ # => ['Респ. Чечня', 'Республика Чечня']
22
103
 
104
+ Fias::Name::Append.append('Чеченская', 'республика')
105
+ # => ['Чеченская Респ.', 'Чеченская Республика']
23
106
  ```
24
- rake fias:create_tables [DATABASE_URL=... PREFIX=... PATH=... ONLY=...]
107
+
108
+ You can pass any form of type name: full, short, an alias, with or without the dot.
109
+
110
+ ### Extract a toponym
111
+
112
+ Sometimes you need to extract a toponym and its type from a plain string:
113
+
114
+ ```ruby
115
+ Fias::Name::Extract.extract('Город Санкт-Петербург')
116
+ # => ['Санкт-Петербург', 'город', 'г', 'г.']
117
+
118
+ Fias::Name::Extract.extract('ул. Казачий Вал')
119
+ # => ['Казачий Вал', 'улица', 'ул', 'ул.']
25
120
  ```
26
121
 
27
- Либо:
122
+ ### Extract house number
123
+
124
+ Sometimes street names come mixed up with house numbers, and you need to extract the house number from a string to clean it up for indexing:
28
125
 
126
+ ```ruby
127
+ Fias::Name::HouseNumber.extract('Ново-Садовая ул,303а')
128
+ # => ['Ново-Садовая ул', '303а']
129
+
130
+ Fias::Name::HouseNumber.extract('пр.Энергетиков 72/2')
131
+ # => ['пр.Энергетиков', '72/2']
29
132
  ```
30
- rails g fias:migration [--path=... --prefix=... --only=...]
133
+
134
+ ## Searching
135
+
136
+ Given you have a set of structured addresses:
137
+
138
+ ```ruby
139
+ [
140
+ { region: 'Еврейская АОбл', city: 'г. Биробиджан', street: 'Шолом-Алейхема' },
141
+ { city: 'Санкт-Петербург', street: 'Лермонтовский проспект' }
142
+ ]
31
143
  ```
32
144
 
33
- Первый вариант - для использования гема вне рельсов, либо для случая, когда
34
- актуальная база ФИАС будет использоваться только для локального мапинга, и
35
- на продакшн попасть не должна.
145
+ You need to find a FIAS item for each address in set.
36
146
 
37
- В параметре only можно передать имена нужных таблиц, houses - все
38
- таблицы домов.
147
+ Your project may use a full-text search engine (Sphinx, ElasticSearch) or just a SQL database. Search principles are the same, but the implementation would differ. This library contains helpful modules and base classes to facilitate searching.
39
148
 
40
- # Импорт данных
149
+ ### Indexing
41
150
 
42
- ```
43
- rake fias:import PREFIX=fias PATH=tmp/fias ONLY=address_objects
44
- rake fias:import PREFIX=fias PATH=tmp/fias ONLY=houses
151
+ Each toponym consists of words; some of them are considered "special". Said "special" words could have synonyms or different forms, they could be skipped by user or could be written differently in FIAS database itself.
152
+
153
+ Examples:
154
+
155
+ * "50 лет Октября" == "50-летия Октября"
156
+ * "1-ая Советская" == "1 Советская" || "Советская 1-я"
157
+ * "Большой Проспект П.С." == "Большой Проспект Петроградской"
158
+ * "имени Максима Горького" == "им. Горького" || "Горького"
159
+ * "ул. Цюрупы" == "Цурюпы" || "Цюрупа" || "Цорюпы" || "Цорупа" (that's my favorite!)
160
+
161
+ You should trait them as equal when performing search.
162
+
163
+ Note that we are talking about toponym names with types extracted (see type extraction above).
164
+
165
+ #### Splitting the words
166
+
167
+ Words are split according to a set of simple rules aimed to simplify disclosure of synonyms and determination of optional parts.
168
+
169
+ ```ruby
170
+ Addressing::Name::Split.split("50 лет Октября")
171
+ # => ["50 лет", "октября"]
172
+
173
+ Addressing::Name::Split.split("Ю.Р.Г.Эрвье")
174
+ # => ["ю.р.г.", "эрвье"]
45
175
  ```
46
176
 
47
- Первый пример импортирует только адресные объекты (без справочников),
48
- второй - только дома.
177
+ #### Finding synonyms and optional words
49
178
 
50
- Поддерживается импорт в Postgres и SQLite (нужно для :memory: баз)
179
+ Given we have a street named `им. академика И.П.Павлова` in FIAS, most people will reference it as just `Павлова` street, some will write it as `имени Павлова`, and some - `академика Павлова`. Basically, nobody except the FIAS database would reference it by the exact original name.
51
180
 
52
- # Импорт данных в память (для рейк тасок)
181
+ ```ruby
182
+ Addressing::Name::Synonyms.expand('им. академика И.П.Павлова')
183
+
184
+ # => [["им", "имени", "им.", ""],
185
+ # ["ак.", "академика", ""],
186
+ # ["и.п.", ""],
187
+ # ["павлова"]]
188
+ ```
189
+
190
+ Will return all possible forms for each word. Empty strings here mark optional words.
53
191
 
54
192
  ```ruby
55
- ActiveRecord::Base.configurations['fias'] = {
56
- :adapter => 'sqlite3',
57
- :database => ':memory:'
58
- }
59
- Fias::AddressObject.establish_connection :fias
60
-
61
- fias = Fias::DbfWrapper.new('tmp/fias')
62
- importer = Fias::Importer.build(
63
- adapter: 'sqlite3', connection: Fias::AddressObject.connection.raw_connection
64
- )
65
- tables = fias.tables(:address_objects)
193
+ Addressing::Name::Synonyms.tokens('им. академика И.П.Павлова')
66
194
 
67
- # Мигрейт. В таком виде так как стандартный мигратор всегда открывает новое
68
- # соединение с БД, а в случае с SQLite это означает пересоздание базы :memory:.
69
- Fias::AddressObject.connection.instance_exec do
70
- eval(importer.schema(tables))
71
- end
195
+ # => ["им", "имени", "им.", "ак.", "академика", "и.п.", "павлова"]
196
+ ```
72
197
 
73
- importer.import(tables) do |name, record, index|
74
- record[:aclevel] == 1 # Только активные
75
- end
198
+ Will return flat array with all words.
199
+
200
+ You can also calculate all possible name combinations:
76
201
 
77
- Fias::AddressObject.count # И дальше импорт
202
+ ```ruby
203
+ Addressing::Name::Synonyms.forms('им. И.П.Павлова')
204
+ # => [
205
+ # 'и.п. им павлова',
206
+ # 'им павлова',
207
+ # 'и.п. имени павлова',
208
+ # 'имени павлова',
209
+ # 'и.п. им. павлова',
210
+ # 'им. павлова',
211
+ # 'и.п. павлова',
212
+ # 'павлова'
213
+ # ]
78
214
  ```
79
215
 
80
- # Некоторые замечания про ФИАС
216
+ #### Generating search index
81
217
 
82
- 1. ФИАС хранит историю изменений информации адресных объектов. Например,
83
- в ней есть и Ленинград и Санкт-Петербург. Исторические версии это
84
- двунаправленный список.
85
- 2. Записи базы данных ФИАС имеют признак актуальности.
86
- 3. Ленинград и Санкт-Петербург, хотя, и хранятся в разных записях базы данных,
87
- являются одним и тем же территориальным образованием. Код территориального
88
- образования хранится в поле AOGUID. Из внешних таблиц логично ссылаться
89
- на AOGUID, а не на AOID.
218
+ In search index you need:
219
+ * name tokens (result of `Fias::Name::Synonyms.tokens`)
220
+ * name forms (result of `Fias::Name::Synonyms.forms`)
221
+ * ancestor ids
90
222
 
91
- # Маппинг, он же итерирование
223
+ See [indexing example](examples/generate_index.rb).
92
224
 
93
- Задачи:
225
+ ### Querying
94
226
 
95
- 1. Сопоставить базу приложения с ФИАСом.
96
- 2. Уведомлять приложение об изменениях в связанных объектах ФИАСа.
97
- 3. Уведомлять приложения о новых данных в ФИАСе.
227
+ Performing a search will execute these three steps:
98
228
 
99
- В таблице, с которой устанавливаются соответствия нужно создать поле для
100
- хранения UUID записи ФИАСа.
229
+ 1. Preparation: sanitizing request values, splitting toponym name and type, etc.
230
+ 2. Querying: finding possible candidates in addressing object tree.
231
+ 3. Decision: determining the most suitable result depending on similarity with request.
101
232
 
102
- На примере регионов:
233
+ #### Defining an in-app query class
234
+
235
+ We'll use the `sequel` gem in this example.
103
236
 
104
237
  ```ruby
105
- # Будем искать соответствия регионов приложения регионам в ФИАС
106
- scope = Region
107
- fias_scope = Fias::AddressObject.leveled(:region)
108
-
109
- # Обработчик событий итератора
110
- matcher = ->(action, record, *objects) {
111
- match = objects.first
112
-
113
- case action
114
-
115
- # Появилась новая запись в ФИАСе
116
- when :created
117
- puts "Region #{match.abbrevated}"
118
- Region.create!(
119
- fias_reference: match.id,
120
- title: match.abbrevated
121
- )
122
-
123
- # Элемент в ФИАСе обновлен, нужно (?) обновить базу приложения
124
- when :updated
125
- record.update_attributes(
126
- title: match.abbrevated,
127
- fias_reference: match.id
128
- )
129
- puts "#{record.title} became #{match.abbrevated}"
130
-
131
- # Объект в ФИАСе был, но из актуального состояния перешел в неактуальное.
132
- # Типа, Припять: умерший город.
133
- # Скорее всего, такой элемент в базе приложения нужно скрыть.
134
- when :deleted
135
- raise NotImplementedError, "#{record.title} #{objects.first.abbrevated}"
136
-
137
- # Элемент в ФИАСе разделился: например, поселение Черто-Полохово
138
- # было преобразовано в две деревни: Чертово и Полохово
139
- # В этом случае параметр objects содержит объекты ФИАСа, на которые
140
- # разделился старый.
141
- when :split
142
- raise NotImplementedError
143
-
144
- # Элемент в ФИАСе объединился
145
- # Типа, присоединили Московскую область к Москве
146
- # В этом случае objects.first это запись ФИАС о новом объекте, а остальные
147
- # элементы objects - записи ФИАС объединившихся объектов.
148
- when :joined
149
- raise NotImplementedError
150
-
151
- # Элементу базы приложения нет соответствия в ФИАСе. Пытаемся сопоставить
152
- # и зафиксировать, если найшли соответствующий вариант.
153
- when :match
154
- match = objects.detect { |b| b.actual? || b.name == record.title }
155
- if match.present?
156
- record.update_attributes(
157
- title: match.abbrevated,
158
- fias_reference: match.id
159
- )
160
- puts "#{record.title} became #{match.abbrevated} (new)"
161
- else
162
- puts "Record not found #{record.title}"
163
- end
164
-
165
- else
166
- raise 'Unknown action!'
167
- end
168
- }
238
+ class Query
239
+ include Fias::Query
169
240
 
170
- # Итерирует существующие в базе приложения элементы
171
- # fias_reference - название колонки с UUID соответствующей региону записи
172
- # ФИАСа
173
- # title - название региона
174
- fias_scope.match_existing(scope, :fias_reference, :title, &matcher)
241
+ def find(tokens)
242
+ return [] if tokens.blank? # Empty array has no type, Sequel fails.
175
243
 
176
- # Итерирует отсутствующие в базе приложения, но имеющиеся в скоупе
177
- # ФИАСа адреса.
178
- fias_scope.actual.match_missing(scope, :fias_reference, &matcher)
179
- ```
244
+ op = Sequel.pg_array_op(:tokens)
180
245
 
181
- Примечания:
246
+ DB[:address_objects]
247
+ .select(:id, :name, :abbr, :parent_id, :ancestry, :forms, :tokens)
248
+ .where(op.overlaps(tokens))
249
+ .to_a
250
+ end
251
+ end
252
+ ```
182
253
 
183
- 1. В случае неоднозначного совпадения ФИАС и приложения, нужно ставить поиск
184
- соответствия на модерацию.
185
- 2. В случае :split, :joined - тоже на модерацию. Хотя, разделения происходят
186
- и нечасто, однако, в ФИАСе таких случаев больше полутора тысяч.
187
- 3. При начальном импорте данных из ФИАСа, #match_missing лучше начать вызывать
188
- только после того как соответствия старой базы ФИАСу будут полностью разрулены.
254
+ `#find` accepts splitted object name (a result of `Fias::Name::Split.split`). It searches all address objects with their tokens matching a given set of tokens. It returns an array of hashes with keys you can see above.
189
255
 
190
- # Работа с данными
256
+ * `:abbr` - FIAS shortname value.
257
+ * `:ancestry` - array of ancestor IDs.
258
+ * `:forms` - object name forms (`Fias::Name::Synonyms.forms`)
259
+ * `:tokens` - object name tokens (`Fias::Name::Synonyms.tokens`)
191
260
 
192
- Существующие регионы:
261
+ #### Query params
193
262
 
194
263
  ```ruby
195
- Fias::AddressObject.actual.leveled(:region).all
264
+ query = Query.new(
265
+ region: 'Еврейская АОбл', city: 'г. Биробиджан', street: 'Шолом-Алейхема'
266
+ )
267
+
268
+ query.params.sanitized
269
+ # => {
270
+ # :region => ["Еврейская", "автономная область", "Аобл", "Аобл"],
271
+ # :city => ["Биробиджан", "город", "г", "г."],
272
+ # :street => ["Шолом-Алейхема"]
273
+ # }
196
274
  ```
197
275
 
198
- Подчиненные объекты в регионе (области, районы, столица региона):
276
+ Allowed params are: `%i(region district city subcity street)`
277
+
278
+ #### Result
199
279
 
200
280
  ```ruby
201
- region = Fias::AddressObject.actual.leveled(:region).first
202
- region.children.actual
281
+ query.perform
282
+ #
283
+ # [[13213, {:id=>72344, :name=>"Шолом-Алейхема", :abbr=>"ул", :parent_id=>184027, :ancestry=>[184027, 12550], :forms=>["шолом-
284
+ # алейхема"], :tokens=>["шолом-алейхема"], :key=>:street}]]
203
285
  ```
204
286
 
205
- # TODO
287
+ Result is array.
288
+
289
+ * Each element of array contains two values: factor of equality and found object.
290
+ * If there are more then one row in array it means that query results are ambigous. All elements will have same factors.
291
+ * Array is empty if nothing found.
292
+
293
+ ## Notes
294
+
295
+ 1. People make mistakes. Search requests can have mistakes. Our goal is to minimize mistake's impact. Everything above (name forms, synonyms, etc.) is made to better understand humans. Over 50K of different real addresses written by humans was used to collect type of mistakes and deduce that rules.
296
+ 2. That's why requests are slow.
297
+ 3. In real applications there could be a lot of similar queries. It's okay to cache request results in database to prevent repeated queries. Cached items do not need TTL because FIAS changes rarely.
298
+ 4. In many cases human can resolve ambigous results or try to find result manually. It could be wise to have some kind of admin interface in your app to do that.
299
+
300
+ ## Contributors
301
+
302
+ * Victor Sokolov (@gzigzigzeo)
303
+ * Vlad Bokov (@razum2um)
304
+
305
+ Special thanks to @gazay.
306
+
307
+ ## Contributing
206
308
 
207
- 1. Индексы. Индексы. ИНДЕКСЫ.
309
+ 1. Fork it ( https://github.com/evilmartians/fias/fork )
310
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
311
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
312
+ 4. Push to the branch (`git push origin my-new-feature`)
313
+ 5. Create a new Pull Request
208
314
 
209
- # Полезные ссылки
315
+ ## License
210
316
 
211
- * http://fias.nalog.ru/Public/DownloadPage.aspx
212
- * http://wiki.gis-lab.info/w/%D0%A4%D0%98%D0%90%D0%A1#.D0.A2.D0.B5.D0.BA.D1.81.D1.82.D0.BE.D0.B2.D1.8B.D0.B5_.D1.8D.D0.BB.D0.B5.D0.BC.D0.B5.D0.BD.D1.82.D1.8B_.D0.B0.D0.B4.D1.80.D0.B5.D1.81.D0.B0
213
- * http://basicdata.ru
317
+ The MIT License