新手问题 关于多对多关联的一个查询问题!

stephen · 2013年04月27日 · 最后由 stephen 回复于 2013年04月28日 · 4996 次阅读

product 和 category 多对多关联,根据 category 搜索 product! 应该如何写? 数据库是 mongodb,用 mongoid!

Product

id:1, category_ids:[1,5,6]
id:2, category_ids:[1,3,6]
id:3, category_ids:[2,3,5]
id:4, category_ids:[4,5,6]

现在条件是 [5,6],应该怎么写才能搜索出 id:1 和 id:4???

这个是 java 版本的,楼主可以参考 @Query("select p from Product p inner join p.tags tag where tag IN (:tags) GROUP BY p HAVING COUNT(tag) >= :size") size 就是同时符合的 tag 数量

@Teddy 要自己写语句?

猜测一下,数据库中有 3 张相关的表,分别为:products,categories 和 product_categories。 楼主试试这个: select p.id from products as p left join product_categories as pc on (p.id = pc.product_id) where p.category_id in :id_arr :id_arr 就是指[5,6]这个 id 数组。

#2 楼 @stephen 这是最终要生成的结果 我对 rails query 也不熟,这个要靠你自己了

#3 楼 @lionzixuanyuan 这个是是 in 条件,符合其一就行了,楼主要的是符合多个

@Teddy @lionzixuanyuan @Rei 谢谢!

MOPED: 127.0.0.1:27017 COMMAND      database=meiyuewuyou_development command={:count=>:products, :query=>{"category_ids"=>{"$all"=>[9, 2, 6]}}} (0.4029ms)
=> #<Product _id: 3, _type: nil, created_at: 2013-04-27 01:37:21 UTC, updated_at: 2013-04-27 05:30:24 UTC, liked_user_ids: [], likes_count: 0, name: "干爽网面", howmany: nil, price: 0, exprice: 0, inprice: 0, size: "", use: nil, feature: "", inventory: 0, sales: 0, cover: 0, selling: true, barcode: "12121121212", special_offer: false, node_id: 1, manufacturer_id: 6, category_ids: ["2", "6", "9"], identifier: "", smell: "">

但是搜索没结果,是不是category_ids有顺序限制?

#9 楼 @stephen 字符串和数字差别?

@Rei 悲催!我看看!

@Rei 这样转换挺麻烦的?有办法在保存 product 数据时把它转换成整型保存吗?或者其他好办法?

#12 楼 @stephen 这看你怎么保存的

@Rei 因为是多对多,所以在 product 的表单里用 checkbox,他自动保存为字符

<%= check_box_tag "product[category_ids][]", category.id, @product.categories.map(&:id).include?(category.id) %>

#14 楼 @stephen params[:product][:category_ids].map(:to_i)

@Rei

params[:product][:category_ids] = params[:product][:category_ids].map{|i| i.to_i}
logger.info(params[:product][:category_ids])

在 create 转换了为 i,但是保存到数据库还是 s,category_ids: ["5", "8", "3"]

conditions.merge!(:category_ids => params[:category].split("-").map{|i| i.to_s}) unless params[:category].blank?
logger.info(params[:category].split("-")) unless params[:category].blank?
logger.info(params[:category].split("-").map{|i| i.to_s}) unless params[:category].blank?
@products = Product.all(conditions).paginate(:page => params[:page], :per_page => 10)

而在搜索的时候转换,也没用!我用 logger.info 输出,不转换已经是 ["5", "8", "3"] 了,但是执行数据库搜索的时候,就会变成 [5, 8, 3]!

#16 楼 @stephen 你不能 logger 一下 conditions 么

Product.find(5) & Product.find(6)

可利用数组的求交集操作:& 具体的 find 只有自己去实现了,没用过 mongodb。

find(5) => ids: [1, 3, 4]
find(6) => ids: [1, 2, 4]
find(5) & find(6) => [1, 4]

@ChanceDoor {:node_id=>"1", :category_ids=>["9", "3", "6"]}这是 conditions! 这是 product,category_ids: ["6", "9", "3"]

#<Product _id: 3, _type: nil, created_at: 2013-04-27 01:37:21 UTC, updated_at: 2013-04-28 03:38:38 UTC, liked_user_ids: [], likes_count: 0, name: "干爽网面", howmany: nil, price: 0, exprice: 0, inprice: 0, size: "", use: nil, feature: "", inventory: 0, sales: 0, cover: 0, selling: true, barcode: "12121121212", special_offer: false, node_id: 1, manufacturer_id: 6, category_ids: ["6", "9", "3"], identifier: "", smell: "">

#19 楼 @stephen 另外你这个 category_ids 是无序还是有顺序?

如:

id:4, category_ids:[4,5,6]
id:5, category_ids:[4,6,5]

是符合需求呢,还是不符合?

@zfjoy520 你误会了!我不是要交集!我的意图是:几个商品,同时拥有 category_ids:["6","9","3"]!现在传入参数 category=6-3-9,然后把他转换成 array,传入 conditions,然后进行搜索!

@zfjoy520 是无序的,只有拥有 4,5,6 都符合!

#21 楼 @stephen 。。我的理解,你想要的就是 all.find(category_ids.include?([6, 9, 3]))

那就是对 6 9 3 单条记录求交集的结果

@zfjoy520 恩,商品可能拥有更多 categories,例如 [3,7,12,6,8,9]! 顾客可能要筛选,[6,3,9],所以要搜索出 products 中拥有 [6,3,9]categories 的 products,返回给顾客,product 和 category 是多对多的关联

#24 楼 @stephen 那你就用 all.find 呗....先实现了功能再说

#24 楼 @stephen 呵呵,感觉需求很简单,逻辑很复杂,应该类似于网购裤子,鞋子等 尺码、颜色、款型一类的综合筛选吧。

@ChanceDoor 我就是不知道应该怎么写,你的意思是把参数的数组,循环搜索? @zfjoy520 是啊,就是这个功能!

#27 楼 @stephen 个人感觉,你存的时候,还是分开存成实体吧,没必要搞到一个 category_ids 里面 如:尺码_id、颜色_id、款型_id,xxx_id

虽然这样可能会增大存储空间,但应该会节省许多 cpu 运算时间。 而且组合查询的时候,逻辑就非常简单了,where(a_id = 5 and b_id = 6)

如果展示时会用到 category_ids,那自己写一个方法把这些 id 组合一下就好。

@zfjoy520 @ChanceDoor 我本来也是这种,这样做不能通用,淘宝那些应该也不是这样的。有不同的商品这种方法就不好使了,例如有鞋子和手机,他们的属性没一个相同,所以 product 这个 model 里面,没可能有这么多 xx_id 呢!

#30 楼 @stephen all.find(category_ids.include?(id_array)) 这样不行吗?

#30 楼 @stephen 。呵呵。他们应该是用的类似 redis 一类东东的做查询加速。 一个大的 product 父类下分很多小的 product 子类,然后服务启动的时候,从实体表往内存导入这些 id,起到加速查询的效果。

@zfjoy520 那只有优化吧?但表的设计还是不变的啊

@ChanceDoor 不行呢,还是没数据!

#33 楼 @stephen 你可以按这样去写:一个大的 product 父类下分很多小的 product 子类,后期做查询算法,做查询优化。 如果所有 product 都整成一个类的话,个人觉得是比较坑爹的。 目前手上的项目就是这样,呵呵,不知道未来会不会存在什么未知的问题。

@zfjoy520 你的意思是不是用类的继承?

@zfjoy520 如果不涉及到你项目的私密,可以详细分享是你是如何设计 product 和 category 的吗?

@saberma 请教下,你的 shopqi,商品和分类的设计思路是怎样的?是如何针对不同商品有不同分类进行设计?

#16 楼 @stephen 这个数据库设计没问题。

现在问题是不知为什么保存进去变了字符串。

16 楼语言有些混乱我看不懂。你用终端操作看看保存正不正常,会写测试的话写测试。

@Rei 我看看,我代码如下 product form

<div class="control-group">
  <label class="control-label">面质</label>
    <div class="controls">
      <label class="checkbox inline">
        <input id="product_category_ids_" name="product[category_ids][]" type="checkbox" value="2" />
        棉柔
      </label>
      <label class="checkbox inline">
        <input checked="checked" id="product_category_ids_" name="product[category_ids][]" type="checkbox" value="3" />
        网面
      </label>
    </div>
  </div>

products_controller.rb create

def create
  @product = Product.new(params[:product])

  if @product.save
    session[:admin_return_to] ||= cpanel_products_path
    redirect_to session[:admin_return_to], :notice => "保存成功!"
  else
    render :action => "new", :notice => "保存失败!"
  end
end

@Rei


# encoding: utf-8
class Product
  include Mongoid::Document
  include Mongoid::Timestamps
  include Mongoid::BaseModel
  include Mongoid::CounterCache
  include Mongoid::Likeable

  field :name        #产品名称
  field :howmany,     :type => Integer#内含多少片
  field :price,             :type => Float, :default => 0  #销售价格
  field :exprice,         :type => Float, :default => 0  #超市价格
  field :inprice,          :type => Float, :default => 0  #入货价格
  field :size          #规格
  field :use           #用途
  field :feature    #产品特点
  field :inventory,      :type => Integer, :default => 0 #库存量
  field :sales,              :type => Integer, :default => 0 #销售量
  field :cover,             :type => Integer, :default => 0 #封面
  field :selling,           :type => Boolean, :default => true #在售
  field :barcode,         :default => "" #条形码
  field :special_offer, :type => Boolean, :default => false

  belongs_to :node, :inverse_of => :products
  belongs_to :manufacturer, :inverse_of => :products
  has_many :line_items
  has_many :pictures, :as => :imageable, :dependent => :destroy
  has_and_belongs_to_many :categories

  attr_accessible :name, :howmany, :price, :exprice, :inprice, :size, :smell, :use, :feature, :sales, :manufacturer_id, :cover, :identifier, :barcode, :selling, :inventory, :category_ids,:special_offer, :node_id

  validates_presence_of :manufacturer_id, :name
  validates_uniqueness_of :barcode, :name

  index :inventory => 1
  index :likes_count => 1

  scope :low_inventory, where(:inventory.lt => 5)
  scope :high_likes, desc(:likes_count, :_id)
  scope :popular, where(:likes_count.gt => 5)
  scope :sell, where(:selling => true)
  scope :special_offering, where(:special_offer => true)

  def format_selling
    self.selling ? "上架" : "下架"
  end

end

@Rei


class Category
  include Mongoid::Document
  include Mongoid::Timestamps
  include Mongoid::BaseModel

  field :name
  field :sort, :type => Integer, :default => 0

  belongs_to :node
  belongs_to :parent, :class_name => "Category"
  has_many :children, :class_name => "Category", :foreign_key => "parent_id", :dependent => :destroy
  has_and_belongs_to_many :products

  scope :roots, where(:parent_id => nil)
end

在终端试试直接赋值一个整数数组保存会有什么情况

哈 在 console 赋值试试

@Rei @tumayun 刚刚外出了!在终端成功!

2.0.0-p0 :003 > p.update_attributes(:category_ids => [4,5,10])
  MOPED: 127.0.0.1:27017 COMMAND      database=admin command={:ismaster=>1} (0.3510ms)
  MOPED: 127.0.0.1:27017 UPDATE       database=meiyuewuyou_development collection=products selector={"_id"=>3} update={"$set"=>{"category_ids"=>[4, 5, 10], "updated_at"=>2013-04-28 11:48:22 UTC}} flags=[] (0.1547ms)
  MOPED: 127.0.0.1:27017 UPDATE       database=meiyuewuyou_development collection=categories selector={"$and"=>[{"_id"=>{"$in"=>[4, 5, 10]}}]} update={"$addToSet"=>{"product_ids"=>3}} flags=[:multi] (0.0887ms)
  MOPED: 127.0.0.1:27017 UPDATE       database=meiyuewuyou_development collection=categories selector={"$and"=>[{"_id"=>{"$in"=>[6, 9, 3]}}]} update={"$pull"=>{"product_ids"=>3}} flags=[:multi] (0.0861ms)
 => true
2.0.0-p0 :005 > p
 => #<Product _id: 3, _type: nil, created_at: 2013-04-27 01:37:21 UTC, updated_at: 2013-04-28 11:48:22 UTC, liked_user_ids: [], likes_count: 0, name: "干爽网面", howmany: nil, price: 0, exprice: 0, inprice: 0, size: "", use: nil, feature: "", inventory: 0, sales: 0, cover: 0, selling: true, barcode: "12121121212", special_offer: false, node_id: 1, manufacturer_id: 6, category_ids: [4, 5, 10], identifier: "", smell: "">

category_ids: [4, 5, 10]

那就是 action 里面要转一下类型了。

@product = Product.new(params[:product].merge(:category_ids => params[:product][:category_ids].map(&:to_i))

@Rei 成功了!是类型问题!谢谢各位解答!

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