fias 0.0.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -22
- data/.rubocop.yml +7 -0
- data/.travis.yml +10 -0
- data/Gemfile +1 -1
- data/LICENSE.txt +2 -2
- data/README.md +259 -155
- data/Rakefile +6 -1
- data/config/names.txt +0 -0
- data/config/synonyms.yml +50 -0
- data/examples/create.rb +106 -0
- data/examples/generate_index.rb +63 -0
- data/fias.gemspec +33 -21
- data/lib/fias.rb +197 -10
- data/lib/fias/config.rb +74 -0
- data/lib/fias/import/copy.rb +62 -0
- data/lib/fias/import/dbf.rb +81 -0
- data/lib/fias/import/download_service.rb +37 -0
- data/lib/fias/import/restore_parent_id.rb +51 -0
- data/lib/fias/import/tables.rb +74 -0
- data/lib/fias/name/append.rb +30 -0
- data/lib/fias/name/canonical.rb +42 -0
- data/lib/fias/name/extract.rb +85 -0
- data/lib/fias/name/house_number.rb +71 -0
- data/lib/fias/name/split.rb +60 -0
- data/lib/fias/name/synonyms.rb +93 -0
- data/lib/fias/query.rb +43 -0
- data/lib/fias/query/estimate.rb +67 -0
- data/lib/fias/query/finder.rb +75 -0
- data/lib/fias/query/params.rb +101 -0
- data/lib/fias/railtie.rb +3 -17
- data/lib/fias/version.rb +1 -1
- data/spec/fixtures/ACTSTAT.DBF +0 -0
- data/spec/fixtures/NORDOC99.DBF +0 -0
- data/spec/fixtures/STRSTAT.DBF +0 -0
- data/spec/fixtures/addressing.yml +93 -0
- data/spec/fixtures/query.yml +79 -0
- data/spec/fixtures/query_sanitization.yml +75 -0
- data/spec/fixtures/status_append.yml +60 -0
- data/spec/lib/import/copy_spec.rb +44 -0
- data/spec/lib/import/dbf_spec.rb +28 -0
- data/spec/lib/import/download_service_spec.rb +15 -0
- data/spec/lib/import/restore_parent_id_spec.rb +34 -0
- data/spec/lib/import/tables_spec.rb +26 -0
- data/spec/lib/name/append_spec.rb +14 -0
- data/spec/lib/name/canonical_spec.rb +20 -0
- data/spec/lib/name/extract_spec.rb +67 -0
- data/spec/lib/name/house_number_spec.rb +45 -0
- data/spec/lib/name/query_spec.rb +21 -0
- data/spec/lib/name/split_spec.rb +15 -0
- data/spec/lib/name/synonyms_spec.rb +51 -0
- data/spec/lib/query/params_spec.rb +15 -0
- data/spec/lib/query_spec.rb +27 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/support/db.rb +30 -0
- data/spec/support/query.rb +13 -0
- data/tasks/db.rake +52 -0
- data/tasks/download.rake +15 -0
- metadata +246 -64
- data/lib/fias/active_record/address_object.rb +0 -231
- data/lib/fias/active_record/address_object_type.rb +0 -15
- data/lib/fias/dbf_wrapper.rb +0 -90
- data/lib/fias/importer.rb +0 -30
- data/lib/fias/importer/base.rb +0 -59
- data/lib/fias/importer/pg.rb +0 -81
- data/lib/fias/importer/sqlite.rb +0 -38
- data/lib/generators/fias/migration.rb +0 -34
- data/lib/generators/fias/templates/create_fias_tables.rb +0 -5
- data/tasks/fias.rake +0 -68
checksums.yaml
ADDED
@@ -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
|
-
|
2
|
-
|
3
|
-
.
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/LICENSE.txt
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c)
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
44
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
69
|
-
Fias::AddressObject.connection.instance_exec do
|
70
|
-
eval(importer.schema(tables))
|
71
|
-
end
|
195
|
+
# => ["им", "имени", "им.", "ак.", "академика", "и.п.", "павлова"]
|
196
|
+
```
|
72
197
|
|
73
|
-
|
74
|
-
|
75
|
-
|
198
|
+
Will return flat array with all words.
|
199
|
+
|
200
|
+
You can also calculate all possible name combinations:
|
76
201
|
|
77
|
-
|
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
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|
-
|
96
|
-
2. Уведомлять приложение об изменениях в связанных объектах ФИАСа.
|
97
|
-
3. Уведомлять приложения о новых данных в ФИАСе.
|
227
|
+
Performing a search will execute these three steps:
|
98
228
|
|
99
|
-
|
100
|
-
|
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
|
-
|
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
|
-
#
|
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
|
-
|
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
|
-
|
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
|
-
|
202
|
-
|
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
|
-
|
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
|
-
|
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
|