• 请教一个累计统计的 sql at 2013年08月18日

    #9 楼 @as181920 #12 楼 @ery

    下午我给 statlysis.gem 增加了对 sum 的支持 。主题的解决方案如下:

    第一步,数据准备:

    class Mxx; include Mongoid::Document; field :col_sum, :type => Float; end
    
    class Mxx
      before_save do
        self.col_sum = self.col_a * self.col_b
      end
    end
    

    第二步,配置统计:

    Statlysis.setup do
      daily Mxx, :sum_columns => [:col_sum]
    end
    

    第三步,统计和察看结果:

    cron = Statlysis.daily['mxx'].first => #<Statlysis::Timely:0x7f891cc9db30 @source_type=:mongoid, @time_column=:created_at, @time_unit=:day, @time_zone=#<ActiveSupport::TimeZone:0x7f891b4783e8>, @multiple_dataset=#<Statlysis::MongoidDataset:0x7f891cca2338>, @sum_columns=#<Array:0x7f891cc9fd18>, @stat_table_name="timely_mxxes_d", @stat_model=#<Class:0x7f8920966918>>
    
    cron.stat_model # => TimelyMxxesD
    
    cron.run
    
    # 统计结果类似于
    cron.output[1] # => {:t=>Fri, 14 Dec 2012 00:00:00 +0800, :timely_c=>11, :totally_c=>16, :timely_favcount_s=>24.0, :totally_favcount_s=>38.0}
    

    然后显示的时候从统计表 timely_mxxes_d 抽取就可以了,平均数,中位数等都可以从里面算出来。

    欢迎报告 BUG 或功能改进:)

  • 请教一个累计统计的 sql at 2013年08月17日

    #6 楼 @as181920 数据大了也是可以近实时的,只要把比如之前月份的统计数据缓存了,只处理最近数据即可。千万以下级别的数据实在不算什么。

    很好奇如果有几十条类似 SQL 统计需求,不作些模式封装的话,程序在性能和维护上会有问题。

  • 请教一个累计统计的 sql at 2013年08月17日

    建议使用 https://github.com/eoecn/statlysis 这个面向 ORM 统计的 Ruby DSL,具体见 README,比这样复杂地拼 SQL 好理解多了。

    我看到你这里需要对两个字段做额外计算,建议也是预先在事务里写入到该表其他字段为好。

    补: 抱歉没看到 SQL 里有 SUM,过段时间给出解决方案:)

  • 真建议做个网站,把每家公司技术方面的面试流程和特点都爆出来,然后大家一一去'调戏'吧

  • Learning Advanced Javascript at 2013年08月07日

    #6 楼 @loveky 赞同。

    如果这些能全部看懂,就可以算很熟悉 JavaScript 了。

  • 做开源软件的目的 at 2013年08月06日

    4, 公司本身就倡导开源,比如 github

  • @lb563

    整个流程的解决方案

    从你的说明来看,我把过程分为两个步骤,

    其一是收集,日志以 CSV 格式写入,而不是数据库,可能是应用程序为了写入性能的考虑。

    其二是分析数据的清理和初始化,上面你提到为了时间和性能上的考虑,你是想把它们全部读入内存来提升访问速度以此来加快计算的。

    但是这里的“全部读入内存”我觉得是不可取的,尤其是要”大量查询“和“性能”,我觉得应该存在为数据索引而生的数据库里(推荐支持 MapReduce 的 Mongodb,或者专门为统计分析优化的列式数据库),并尽量缓存部分计算结果在磁盘上是比较可取的自然的一个方案。(如果你需要实时统计等其他需求就别论了)。

    另外推荐我写的一个基于 Ruby DSL 的统计分析解决方案,地址在 https://github.com/eoecn/statlysis ,它也支持多表的数据查询统计。目前在开发第二版,欢迎使用和完善:)

    日志导入的解决方案

    CSV 是一个很规范的数据格式,mysql 和 mongodb 等数据库是直接支持导入的,不必再解析然后通过 ORM 导入数据库。

  • 如果只是简单的 count,而且这几个 collection 要统计的数据结构都是一致的,推荐使用我正在开发的面向 ActiveRecord 和 Mongoid 统计分析的 gem 包 statlysis,项目地址在:https://github.com/eoecn/statlysis

    使用方法如下:

    批量初始化 model 和表结构

    {'07' => 1..31, '08' => 1..12}.map do |month, day_range|
      day_range.map do |day|
        # define model dynamically, e.g. MultipleLog20130729
        date_str = "2013#{month}#{day.to_s.rjust(2, '0')}"
        collection_class_name = "MultipleLog#{date_str}"
        collection_name = collection_class_name.sub("MultipleLog", "multiple_log_")
    
        eval("
          class #{collection_class_name}
            include Mongoid::Document
            self.default_collection_name = #{collection_name.to_json}
            field :t, :type => DateTime
            field :url, :type => String
            index({t: -1}, {:background => true})
          end
        ")
      end
    end
    

    简单的配置

    Statlysis.setup do
      set_database :statlysis
    
      daily Mongoid[/eoe_logs_[0-9]+$/].where(:ui => {"$ne" => 0}), :time_column => :t
    end
    

    访问和统计

    [23] pry(#<Statlysis::Configuration>)> Statlysis.daily['multi'].first
    => #<Statlysis::Timely:0x7fb9cc60a490 @is_activerecord=#<FalseClass:0x0>, @is_mongoid=#<TrueClass:0x4>, @is_orm=#<TrueClass:0x4>, @time_column=:t, @time_unit=:day, @time_zone=#<NilClass:0x8>, @multiple_dataset=#<Statlysis::MongoidDataset:0x7fb9cc67e840>, @stat_table_name="timely_multiplelog_d", @stat_model=#<Class:0x7fb9cc6116c8>, @output=#<Array:0x7fb9cc798118>>
    [24] pry(#<Statlysis::Configuration>)> Statlysis.daily['multi'].first.output
    => [{:t=>Mon, 01 Jul 2013 00:00:00 +0000, :timely_c=>1, :totally_c=>1},
     {:t=>Tue, 02 Jul 2013 00:00:00 +0000, :timely_c=>2, :totally_c=>3},
     {:t=>Wed, 03 Jul 2013 00:00:00 +0000, :timely_c=>3, :totally_c=>6},
     {:t=>Thu, 04 Jul 2013 00:00:00 +0000, :timely_c=>4, :totally_c=>10},
     {:t=>Fri, 05 Jul 2013 00:00:00 +0000, :timely_c=>5, :totally_c=>15},
     {:t=>Sat, 06 Jul 2013 00:00:00 +0000, :timely_c=>6, :totally_c=>21},
     {:t=>Sun, 07 Jul 2013 00:00:00 +0000, :timely_c=>7, :totally_c=>28},
     {:t=>Mon, 08 Jul 2013 00:00:00 +0000, :timely_c=>8, :totally_c=>36},
     {:t=>Tue, 09 Jul 2013 00:00:00 +0000, :timely_c=>9, :totally_c=>45},
     {:t=>Wed, 10 Jul 2013 00:00:00 +0000, :timely_c=>10, :totally_c=>55},
     {:t=>Thu, 11 Jul 2013 00:00:00 +0000, :timely_c=>11, :totally_c=>66},
     {:t=>Fri, 12 Jul 2013 00:00:00 +0000, :timely_c=>12, :totally_c=>78},
     {:t=>Sat, 13 Jul 2013 00:00:00 +0000, :timely_c=>13, :totally_c=>91},
     {:t=>Sun, 14 Jul 2013 00:00:00 +0000, :timely_c=>14, :totally_c=>105},
     {:t=>Mon, 15 Jul 2013 00:00:00 +0000, :timely_c=>15, :totally_c=>120},
     {:t=>Tue, 16 Jul 2013 00:00:00 +0000, :timely_c=>16, :totally_c=>136},
     {:t=>Wed, 17 Jul 2013 00:00:00 +0000, :timely_c=>17, :totally_c=>153},
     {:t=>Thu, 18 Jul 2013 00:00:00 +0000, :timely_c=>18, :totally_c=>171},
     {:t=>Fri, 19 Jul 2013 00:00:00 +0000, :timely_c=>19, :totally_c=>190},
     {:t=>Sat, 20 Jul 2013 00:00:00 +0000, :timely_c=>20, :totally_c=>210},
     {:t=>Sun, 21 Jul 2013 00:00:00 +0000, :timely_c=>21, :totally_c=>231},
     {:t=>Mon, 22 Jul 2013 00:00:00 +0000, :timely_c=>22, :totally_c=>253},
     {:t=>Tue, 23 Jul 2013 00:00:00 +0000, :timely_c=>23, :totally_c=>276},
     {:t=>Wed, 24 Jul 2013 00:00:00 +0000, :timely_c=>24, :totally_c=>300},
     {:t=>Thu, 25 Jul 2013 00:00:00 +0000, :timely_c=>25, :totally_c=>325},
     {:t=>Fri, 26 Jul 2013 00:00:00 +0000, :timely_c=>26, :totally_c=>351},
     {:t=>Sat, 27 Jul 2013 00:00:00 +0000, :timely_c=>27, :totally_c=>378},
     {:t=>Sun, 28 Jul 2013 00:00:00 +0000, :timely_c=>28, :totally_c=>406},
     {:t=>Mon, 29 Jul 2013 00:00:00 +0000, :timely_c=>29, :totally_c=>435},
     {:t=>Tue, 30 Jul 2013 00:00:00 +0000, :timely_c=>30, :totally_c=>465},
     {:t=>Wed, 31 Jul 2013 00:00:00 +0800, :timely_c=>27, :totally_c=>483}]
    [25] pry(#<Statlysis::Configuration>)> 
    
  • 官网上线后第一时间报名!

  • 工程师文化在整个产业里太小众,再加上中国是世界工厂,催生不了那么多技术人才。

  • #3 楼 @hooopo 。。。话说你们都是如何处理这样有很多数据和流程依赖的单页应用?

  • #1 楼 @luikore

    _.keys(eoe) // ["domain_to_urls", "domain", "app", "uid", "uname", "uhash", "avatar", "homeUrl", "appUrl", "app_item", "app_item_id", "visitor_list_pattern", "fp", "jsfun_user_avatar_url", "is_uid_a_teacher", "utils", "faye_port", "learn_summary_data", "is_local", "current_user", "is_uid_uhash_valid", "course", "all_classes", "teachers", "is_a_teacher", "current_class", "is_current_class_time_end", "period_id", "students", "is_a_student", "lessons", "lesson_id_to_seqs", "current_issue", "is_current_issue_finished", "current_lesson_time_racings", "class_channel", "lesson_channel", "lesson", "uid_to_unames", "section_with_exam", "online_uids", "user", "issue", "spend_seconds", "is_not_finished_issue", "lesson_to_spend_time", "lessons_seq_in_students", "lesson_to_questions_count", "lesson_to_answers_count", "learn_id_to_exam_count", "students_in_summary_unfinished", "students_in_summary_finished_sort", "seq_in_class", "learn_summary", "feedback", "is_debug", "faye"]
    _.keys(eoe).length // 59
    _.reject(eoe, function(s) { return _.isFunction(s); }).length // 49
    

    在 Chrome 控制台可以看到的有 49 个变量数据输出,如何管理是个大问题,里面还包括读写和前后依赖,目前我想到的解决办法是分到不同的流程里面。

  • 数据处理 at 2013年07月09日

    来个接近自然语言一点的,也是我之前做数据展示时用的技巧。

    require 'date'
    require 'active_support/all'
    
    date_to_count_hash = Hash[ (Date.parse("2013-6-30")..Date.parse("2013-07-07")).to_a.map {|i| [i, 0] } ]
    
    [["2013-07-05", 12], ["2013-07-01", 24], ["2013-06-30", 34]].each {|date_str, count| date_to_count_hash[Date.parse(date_str)] = count }
    
    date_to_count_hash.map(&:last)
    
  • 数据处理 at 2013年07月08日

    Array 也好,或者时间对应数据的 Hash(Ruby 1.9 的 Hash 是按插入顺序排序的) 也好,缺失的值都是必须显示声明的,不管是 0 还是 空值 nil。

    之前我写过一篇 Android 优亿市场数据采集分析系统概要 ,有个技巧是 统计数据存储和展示应该是分开的,特别是涉及到多维时有很多空值的情况。希望能对你有所帮助:)

  • 实现 block 里的变量 at 2013年07月08日

    @coolesting 我猜测你要实现的需求是实现变量的作用域动态配置,而不用作为一般的方法参数传入。

    在 Ruby 里,局部变量 (local variable) 只能在 method 和 loop 内被访问,实例变量 (instance variable) 可以在一个类的实例的方法之间互访。

    所以要么和 @guyanbiao 说的一样,把 i 这个变量拎到外面。

    或者用 实例变量 也可以实现,如下:

    1.9.3p392 :001 > class HH; def initialize &blk; @i = 5; instance_eval &blk; end; end
     => nil 
    1.9.3p392 :002 > HH.new { puts (@i + 5) }
    10
     => #<HH:0x000001010588e8 @i=5> 
    1.9.3p392 :003 > @i
     => nil 
    1.9.3p392 :004 > 
    

    关于 Ruby 里的各种变量的用途在 http://www.techotopia.com/index.php/Ruby_Variable_Scope 这里解释的比较清楚了,包括类变量等。

  • https://www.ruby-toolbox.com/categories/rails_admin_interfaces 看下这个 gem 分类

    RailsAdmin ActiveAdmin

  • request formats 的问题 at 2013年07月02日

    使用

    self.formats = [:html]
    @paginate_html = render_to_string(:partial => "qa/paginate")
    self.formats = [:json]
    

    进行切换即可。

    可以参考这里 https://github.com/eoecn/qa-rails/blob/master/app/controllers/qa_controller.rb#L15

  • 业务流程化,技术模块化。不管是是业务逻辑还是技术逻辑,尽量作成 DSL,这样技术的人看到的技术,业务的人看到的是业务。

    上面这些可以考虑和 devise 一样,作成一个模块插件。

  • #3 楼 @virgil 两点准时到

  • 更新不能,昨天的包了

    curl -I http://ruby.taobao.org/quick/Marshal.4.8/rack_image_assets_cache_control-0.2.gemspec.rz
    HTTP/1.1 404 Not Found
    Server: nginx/1.1.6
    Date: Wed, 08 May 2013 02:16:15 GMT
    Content-Type: text/html
    Content-Length: 168
    Connection: keep-alive
    
    
    curl -I http://rubygems.org/quick/Marshal.4.8/rack_image_assets_cache_control-0.2.gemspec.rz
    HTTP/1.1 302 Moved Temporarily
    Server: nginx
    Date: Wed, 08 May 2013 02:16:25 GMT
    Content-Type: text/html
    Content-Length: 154
    Connection: keep-alive
    Location: http://tokyo-m.rubygems.org/quick/Marshal.4.8/rack_image_assets_cache_control-0.2.gemspec.rz
    
  • #5 楼 @saiga 不可否认 Active Model Serializer 分割的更细致,而在我的方法里只需要维护原来的 MVC 三个层次即可,扩展的只是在 M 里多配置 as_json 一个方法。

    我举的分页的例子其实是说明返回一段 HTML 是如何便利,这个在 MVC Single Page Application 特别有用,比如 js 里没有 kaminari 那么好用的分页模块,在 Rails 的 Controller 里 render_to_string 渲染后整合到返回 JSON 数据里,也特别符合 Rails 默认 MVC 的约定逻辑。

  • #3 楼 @saiga rabl 我尝试过很不好用,很诡异的 API(上一次用是两年前,现在全然忘记了需要重新学习)。as_json 可以更让接口更面向 RESTful 设计,没有思维切换过程。

    1. 输出一个 show 或 index 没问题
    2. 输出组合的 json,比如 ruby format.json { render :json => {:topic => @topic, :replies => @topic.replies}, :status => 200} 里面对应的 topic 和 reply 的 JSON 格式在各自的 model 里定义就可以了,重载或覆盖。
    3. 甚至返回一段渲染的 HTML 也可以 ruby format.json { render :json => {:topics => @topics, :paginate_html => @paginate_html}, :status => 200}
  • 我觉得 Rails 自带的 as_json 很不错了。有问题的话,那很大程度上是数据库没设计好。

  • #6 楼 @hpyhacking 我从《Ruby 元编程》里学到的最大的体会就是:class, module, def, proc, lambda 等都是定义作用域的思想确实让人耳目一新。

  • 多表查询效率太低,建议用 text 字段代替,然后用 JSON 序列和反序列化

    推荐用 https://github.com/bdurand/json_record,操作封装的很好了,比如:

    class Post < ActiveRecord::Base
      serialize_to_json(:json_data) do |schema|
        schema.key :title, :required => true
        schema.key :body, :required => true
        schema.key :author, Person, :required => true
        schema.many :comments, Comment
      end
    end