新手问题 [已解决] 省市区查询使用什么 gem?或其它实现方式?

chairy11 · 2015年05月06日 · 最后由 chairy11 回复于 2015年05月11日 · 3819 次阅读

非常常见的场景,存储用户所在的省市区信息。

问题一: 数据源哪来?

  1. 是不是要在数据库里存储这些所有的省市区信息?
  2. 还是说像一些 gem 使用的是 csv 之类的格式,或者写什么 json 之类的?

问题二: 结果怎么存储?用 integer 还是 string?

  1. 如果是数据源在数据库,是不是三个省市区表叫 provinces, cities,districts, 使用对象是 users,然后 users 里面用 integer 存储 provinces_id, city_iddistrict_id?感觉好像非常麻烦。
  2. 存储到 users 表里面的字段为 province,city,district,用 string 直接存储结果为"北京市""北京市""朝阳区"? 大家一般怎么做呢?

问题三:借用什么 gem 或 js 包?大家都有什么经验或结论?

  1. @saberma 写的china_city,这个是不是比较经典?数据源够新不?
  2. @tangmonk在依赖 @saberma 的那个基础上写的 rails_admin_china_city_field,感觉倒是很新,但没明白改进的地方在哪?
  3. encoreshao/china_regions
  4. raecoo/china-regions
  5. chinese_regions
  6. 还有个纯 js 实现的PCAS (Province City Area Selector 省、市、地区联动选择 JS 封装类)

大家都用哪个?怎样去分析和对比这些实现方法?

解决方案:

有一个官方数据源,但最新更新到 13 年底 还有一个可参考,qq ip 页面,纯 J's 的,已经有人拔下来了,自己拔也不难。第三方 gem 没用过。 还可以拔比如微信,人人的省市区数据,应该比官方还靠谱点。 不在电脑旁,链接回去再补,你可以搜索下,关键词都给了。 P'S 我用的官方数据,存数据库,有错再改。

貌似国内省市区信息这种数据是在统计局网站上找到,之前坛友给过一个地址一下找不到了

我一般是直接创建一个 addresses 表,然后 user has_one address.

rails_admin_china_city_field 是 rails_admin 的一个插件而已,没有改进

建一个 address 表,province,city, region 用 level 区分,parentId 建立关联,数据结构统一 id, pid, level, name

自己项目中用到这两个不知道能帮到你么

https://github.com/pobing/mvgeo #自己解析省市 json 数据,遵循 iso3166 标准 https://github.com/pobing/sina_geoip #调用 sina 的接口,通过 IP 获取省市信息

数据去统计局网站抓,省市区 基本不会怎么变 记录也不多 12 年我抓过精确到村/居委会一级的 http://nongmin365.com/ 在用,近 80 万条数据

顶 4 楼,现在网上很容易下载到省市关联的数据库文件

#7 楼 @317583395 #6 楼 @huobazi #5 楼 @pobing #4 楼 @kai1248 #1 楼 @flowerwrong 取数据的时候,是用自己数据库查询快,还是像@saberma 写的china_city用 json 大表查询快?

#8 楼 @chairy11 先想着怎么用合适自己,而不是想这怎么快。到市一级能有几个数据,能慢成啥样?

#9 楼 @huobazi 有道理! 但我不知道怎样算合适自己啊! 从偷懒角度看,最想用@saberma 写的china_city。 从思路上,肯定是觉得存进 sql,然后我的字段用的是 province_id, city_id 最好,因为我要根据城市筛选只属于这个城市的数据,诸如此类,需要关联关系,而不是像电商那样把地址填上,展示就行。

如果是我, 我会对比看好的两三种方式,直接实际使用,然后再赛选

问题一

数据来源:中华人民共和国行政区域规划(国家统计局发布)最新的发布时间为 2014-01-17 15:04

  1. 是要在数据库中存储这些所有的省市区信息
  2. csv 之类的格式,或者 json,yaml 之类的只是数据外在的表现形式,这个看具体的需求

问题二

结构存储:

  1. 关联的表记录的是主键.底层存储结构必须要有行政区域代码的字段,便于后续升级,如北京市是 110000,北京市-北京市是 110000,北京市-北京市-东城区是 110101
  2. 要是将将存储格式和表现形式分开,就不会有这个问题.表现形式: 比如,北京市,可以表现为"北京",缩写"京"",首字母"B", 拼音"BJ" 如果是直辖市,合并市辖区和县为直辖市的名称 如果是地级市,去掉市辖区和城区 如果是省直辖县级行政区划,新疆维吾尔自治区直辖县级行政区划, 合并直辖县级到市

问题三:

362 市, 2850 区, 这不是很复杂,自己动手,扩展性和维护性由自己决定...

#12 楼 @chq 哈哈,谢谢:) 原来只有 362 个市这么少啊……

#1 楼 @flowerwrong #2 楼 @ywjno #6 楼 @huobazi #7 楼 @317583395 #12 楼 @chq

这是国家统计局公布的最新县及县以上行政区划代码(截止2014年10月31日) 可是怎么把它批量导入 postgres 啊?

看到这个使用 chinacity 从国家统计局官网取最新城市数据,还是不会……

而且,按道理来说,难道不是应该存成关联关系么?比如我以后要一级筛选省,再根据省筛选出市。 是不是应该三个表,provinces, cities, districts, 然后 cities 里有一个字段是 province_id,districts 表里有一个字段 city_id……

#14 楼 @chairy11 先复制下来,脚本处理

class CreateChinaCities < ActiveRecord::Migration
  def change
    create_table :china_cities do |t|
      t.integer :area_code
      t.string :area
      t.integer :parent_code
      t.integer :level

      t.timestamps null: false
    end
  end
end


def w_f(f, tmp_arr, code, tail, level)
  f.puts tmp_arr[0] + ' ' + tmp_arr[1] + ' ' + code + tail + ' ' + level
end

f = File.new('./china_city_han.txt', 'w')
File.open('./china_city.txt', 'r') do |file|
  while line  = file.gets
    if line[0] != "\n" && line[0] != "\r"
      tmp_arr = line.split(' ')
      # f.puts tmp_arr[0] + ' ' + tmp_arr[1] 3515

      1100.upto(8300).each do |k|
        # han_city(tmp_arr, k.to_s[0], k.to_s[1], k.to_s[2..3], f)
        a = k.to_s[0]
        b = k.to_s[1]
        c = k.to_s[2..3]
        code = a + b
        if tmp_arr[0].start_with?(code + '0000')
          f.puts tmp_arr[0] + ' ' + tmp_arr[1] + ' 0' + ' 1'
          break
        elsif tmp_arr[0].start_with?(code + c) && tmp_arr[0].start_with?(code + c + '00')
          w_f(f, tmp_arr, code, '0000', '2')
          break
        elsif tmp_arr[0].start_with?(code + c)
          w_f(f, tmp_arr, code, c + '00', '3')
          break
        end
      end

    end
  end
end
f.close

# ChinaCity import to seeds.rb
File.open(Rails.root.to_s + '/scripts/china_city_han.txt', 'r') do |file|
  while line  = file.gets
    if line[0] != "\n" && line[0] != "\r"
      tmp_arr = line.split(' ')
      if tmp_arr.length == 4
        cc = ChinaCity.new(
          area_code: tmp_arr[0].to_i,
          area: tmp_arr[1],
          parent_code: tmp_arr[2].to_i,
          level: tmp_arr[3].to_i
        )
        cc.save!
      end
    end
  end
end

#15 楼 @flowerwrong 哭,感动,你太牛了…… 我消化消化……

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册