Query返回两类对象实例:

#第一类是ActiveRecord::Relation 实例, 包括where,group等
Article.where(["title=?", "demo"]).class #=>Article::ActiveRecord_Relation
#第二类是Array实例,Array中每个对象都是model的实例,代表包括:find,first
Article.first.class #=>Article
Article.first(3).class #=>Array
Article.first(3)[0].class #=>Article

?两个多查询方方法的源码分析和总结,其中可以总结Numerator模块方法

#少数据查询
Article.find(1) #查询多个是Article.find([1,2])
Article.take #默认查询任意一条,查询多条为Article.take(3)
Article.first #默认按照id的升序排序,获得升序的第一位
Article.last #默认按照id的降序排序,获得降序的第一位
Article.find_by(name: "demo") #获取任意满足条件的一条值

#find_by和find的区别
User.find(params[:id]) #不存在,会报错, ActiveRecord::RecordNotFound: Couldn't find Article with 'id'=1
User.find_by(id: params[:id]) #不存在,结果为nil

#多查询语句,find_in_batches把一批记录作为模型数组传入块,find_each逐一把每条记录作为模型传入块
find_each
find_in_batches

条件查询

#使用where进行条件查询,参数选择包括字符串,数组和hash

#使用字符串进行查询,就是where后面是纯粹sql语句
Article.where("title='demo'") # sql: select * from article where(title='demo')
Article.where("title='demo' and content='demo'") #sql: select * from article where(title='demo' and content='demo')
Article.where("title='demo' or content='demo'") #sql: select * from article where(title='demo' or content='demo')

#使用数组进行查询
Article.where(["title = ?", "title_demo"]) #单个查询字段
Article.where(["title = ? and content= ?", "title_demo", "content_demo"]) #多个字段
#下面使用hash形式代替“?”,如果指向同一个hash,那么可以节约代码
Article.where(["title = :title_name and content= :content_name", { title_name: "title", content_name: "content" }])
Article.where(["title = #{params[:title]}"]) #下面的代码会产生sql注入,不建议使用

#使用hash进行查询
Article.where(published: true)
Article.where("published" => true) #查询的key可以使用字符串形式
Article.where(title: :demo) #查询会出错,value不可以使用symbol形式
Article.joins(:comments).where(comments: {name: "demo"}) #association中使用hash的形式
Article.where(created_at: (Time.now.midnight - 1.day)..Time.now.midnight) #使用range查询
Article.where(id: [1,3,5]) #使用subset查询

#使用not条件查询
Article.where.not(published: true)

#使用or条件查询
Article.where("title='demo'").or(Article.where("content='demo'"))  #or只有在同一个model的情况才能被使用

#like查询
Article.where(["name like ?", "%#{params[:term]}%"])

多字段查询

#Article中有两个字段,分别为title和content,在唯一的输入框中输入变量,分别模糊查询这个model的两个字段

#简单实现方式,出现问题:sql注入,不可扩展,controller中太多逻辑查询代码
#routes.rb
Rails.application.routes.draw do
  resources :articles do
    collection do
      get 'search'
    end
  end
end

#view
<%= form_tag '/articles/search', method: :get do %>
  <%= text_field_tag :search %>
  <%= submit_tag "submit"%>
<% end %>

#articles_controller.rb
def search
  @articles = Article.where("title like '%#{params[:search]}%'").or(
    Article.where("content like '%#{params[:search]}%'"))
  render 'index'
end

#比较成熟的解决方式,避免sql注入,具备扩展功能,逻辑代码防止在model层中,以下为改进后的代码
#routes.rb和view中的代码不做变化,改变model和controller中的代码
#articles_controller.rb
def search
  @articles =  Article.search(params[:search])
  render 'index'
end
#article.rb,where中使用纯粹的sql语句
class Article < ApplicationRecord
  def self.search(args)
    if args
      args.strip!
      search_fields = %w(title content)
      search_conditions = []
      search_fields.each do |search_field|
        search_conditions << "#{search_field} like :search"
      end
      Article.where(search_conditions.join(' or '), { search: "%#{args}%"})
    else
      Article.all
    end
  end
end

使用arel进行条件查询

#arel的作用
是一种抽象的查询方式,目的是处于对一些复杂查询的简化

#arel的基本用法,arel是rails内置的,不需要添加gem,gemfile.lock中可以查看
@articles = Article.arel
Article.where(@articles[:title].eq("demo")) #title值等于demo
Article.where(@articles[:title].matched("%demo%")) #tilte值like %demo%
Article.where(@articles[:title].matches("demo").or(@articles[:content].matches("demo")))#使用or

#使用arel对上面的多重查询进行改写
class Article < ApplicationRecord
  def self.search(search)
    if search
      search.strip!
      search_fields = %w(title content)
      condition = search_fields.map do |field|
        arel_table[field].matches("%#{search}%")
      end.inject(:or)
      where(condition)
    else
      all
    end
  end
end

使用scope方法

#作用
数据的查询方式在model中定义,controller中直接获取model中定义的scope方法

#常规的scope定义
class Article < ApplicationRecord
  #通过scope定义进行定义
  scope :published, -> { where(published: true) }
  #通过类方法进行定义
  def self.published
    where(published: true)
  end
end

#传入参数
scope :created_before, ->(time) { where("created_at < ?", time) }

#使用条件
scope :created_before, ->(time) { where("created_at < ?", time) if time.present? }

#使用默认的scope
default_scope { where("removed_at IS NULL") }

#合并scope
scope :active, -> { where state: 'active' }
scope :inactive, -> { where state: 'inactive' } 
User.active.inactive

#删除scope
Client.unscoped.all

进行order排序

#排序字段的选择,symbol和string
#desc(descend 下降) asc(ascend 上升)
Client.unscoped.all
Article.order("title")
Artilce.order(:title)

#排序方式,升序asc和降序desc
Article.order(title: :desc)  #或者Article.order("title desc")
Article.order(title: :asc)

#多个字段排序
Article.order(title: :desc, content: :asc)
Article.order(title: :desc).order(content: :asc)

字段选择

#单个字段
Article.select(:title) #或者Article.select("title")
#多个字段
Article.select(:title, :content)

#选择的字段唯一
Article.select(:title).distinct
Article.select(:title).distinct.distinct(false) #去除唯一性

设定取出数量和开始字段

#取出前5个字段
Article.limit(5)
#从第五个开始取5个字段
Article.limit(5).offset(5)

计算

rails g migration add_mount_to_articles mount:integer

Article.count #计算Article的记录
Article.count(:title) #计算Article.title中存在的记录
Article.average(:mount).to_f 
Article.maximum(:mount)
Article.minimum(:mount)
Article.sum(:mount)

todo: enum的使用

results matching ""

    No results matching ""