Rails:保存包含空值的有序列表

时间:2014-02-21 03:14:40

标签: jquery ruby-on-rails mongoid

假设:我想要32个部分,每个部分有4个“槽”。每个插槽都可以从可拖动列表中保存一个人。

什么是保存位置(并回忆每个用户)保持空位或“位置”在适当位置的最佳方法?

现在,我有类似提供的小提琴,但保存后,我只是将data-id作为数组发送,因此它保存了他们的订单,但没有考虑空白空间。 ..当然,我回读列表的方法只返回数组。即。如果我有,在创建时:Tim,空,Bob,清空我的控制器保存['Tim', 'Bob']当用户重新加载时,他们看到:Tim,Bob,空,空

所以这是一个双面的问题:

1)如何保存用户列表包括空白以及使用rails / mongoid执行此操作的最佳方法是什么? 同样,我目前知道如何保存,我只是不知道是否更好地使用数组/对象或什么......

2)如何正确重建/读取/加载列表?

Fiddle:想象一下,这还有31个其他部分需要填写...这只是一个部分。

修改

如果我使用像acts_as_list这样的宝石,那么每个用户的项目有一行会不会(因为每个用户将有128个条目)?在我看来,这将使mongo集合有太多的文件,但我对mongodb的理解也很差。

user_id, 
pick_id,
position

而不是

user_id,
picks [
    "pick_id_1234",
    "pick_id_1235",
    "pick_id_1236"
    ]

4 个答案:

答案 0 :(得分:2)

我没有使用过Mongoid,因此我无法为该部分提供具体的编码;但我可以给你一些结构性的想法


<强>保存

无论如何,您仍需要以某种方式保存列表

因为你没有给出足够的上下文,我会假设你想永久保持列表的状态(使用数据库)。如果您希望列表是临时的(I.E您希望在步骤之间移动等),我建议sessions

<强> DB

如果要保存到数据库,则该过程相对简单。我会在这里使用标准的ActiveRecord,因为我不知道mongoid(对不起):

<强>模型

#app/models/list.rb
Class List < ActiveRecord::Base
   belongs_to :section
   belongs_to :user
end

#app/models/user.rb
Class User < ActiveRecord::Base
    has_many :lists
end

#app/models/section.rb
Class UserListPosition < ActiveRecord::Base
    has_many :lists
    has_many :users, through: :lists
end

<强>架构

users
id | name | email | created_at | updated_at

sections
id | details | about | section | created_at | updated_at

lists
id | section_id | user_id | created_at | updated_at

<强>控制器

#app/controllers/lists_controller.rb
def new
    @list = 32.times do { List.new } ##needs refactoring
end

def create
    @list = List.new(list_params)
    @list.save
end

private

def list_params
    params.require(:list).permit(:section_id, :user_id)
end

<强>视图

#app/views/lists/new.html.erb
<%= form_for @list do |f| %>
    <% Section.all.each do |section| %>
        ##inputs here
    <% end %>
    <%= f.submit %>
<% end %>

答案 1 :(得分:2)

假设

您分配给插槽的对象始终是同一个类Person。

解决方案

如果是这样,那么一种方法就是使用一个简单的Struct,因为你声明它需要保留空白。

# /struct/slotter.rb
Slotter = Struct.new(:section_id, :slot1, :slot2, :slot3, :slot4)

# assuming slots are always the Person#id, i.e. Integer representation 
# of person by their id. Also would work with UUIDs or BSON, etc. 
# The section number would be stored in the variable *section_no*
Slotter.new(section_no, id_1, id_2, id_3, id_4)

# You can also pass a block to a Struct  so you could write read
# accessors for the data, again this assumes Person class exists
Slotter = Struct.new(:section_id, :slot1, :slot2, :slot3, :slot4) do 

  def read_slot1
    Person.find(slot1)
  end

end

文件系统独立于此,因为这将为您提供具有适当Marshal.dump和加载的对象,以便您可以从任何数据库中保存或加载。

由于您的应用程序使用的是Javascript,因此如果在Rails应用程序中使用,则可以将其转换为JSON,因为它具有正确的to_json。如果你需要不同的根节点等,也可以写一个to_json。

侧面思考

这个问题本质上是一个位掩码问题。如果您可以重新格式化您的问题,您可能只需创建一个整数列,并将您的Person类数据存储在一个简单的Integer列中。我会告诉你弄清楚那一个。 :)

此应用程序可能更适合作为JavaScript应用程序,例如在Angular.js中,使用Rails后端来持久存储到数据库中。这样,您就可以在客户端中动态修改巨型JSON对象,并且负载将远离您的服务器。

更新

关于位掩码方法。这是使用Plain Old Ruby Object将位掩码包装/解包为整数的一种方法。它松散地基于Railscast

This particular approach仅利用位掩码来存储节的插槽值。尽管如此,可以应用相同的方法来存储多达n个值。关键是SLOTS阵列基本上是一样的。您可以添加到右侧,例如slot5在slot4的右边,但不能改变它的顺序。

更新#2

位掩码限制

主要限制是

    包装/展开的
  • 复杂性(正如您所看到的那样) 硬)
  • 静态插槽数组控制位掩码的数组只能添加到。即您无法更改现有条目的顺序,只能添加更多内容。

另一个好处是你可以存储一个heckuva很多只有一个整数来补偿IMO。因此,如果你想获得所有拥有x属性集的玩家,你需要一个方法,如:

def in_slot?(slot)
  slots.include?(slot.to_s)
end

你仍然可以获得你在代码与SQL中必须完成的统计数据。即在数组上映射(在ruby中)。就这样:

all.keep_if {|p| p.is_slot?(:slot1)}

但是,如果你知道你正在寻找的面具,例如。在示例中我们想要所有slot1和slot3然后我们知道掩码是 5 所以你只需要查找slot_mask为5的所有条目。

如果你在路上发现你需要更高级的功能,即掩码解决方案的管理比在SQL中存储你的值更慢。您可以随时将掩码展开到一个新的解决方案中,例如序列化或更正式的子类,并在对象Array上快速映射。

答案 2 :(得分:1)

我认为您的问题有两个部分:1。您需要为前端的每个部分存储各种列表2.然后以保留所有数据(包括空白)的方式将这些列表存储在您的数据库中)所以你可以在将来重新创建相同的视图。

我会尝试分别回答每个部分,然后让您了解它们是如何结合在一起的。

现在找到解决方案!

前端

我建议做的是在视图中维护一个JSON对象,该对象包含所有选择的值(无论是null还是填充)。所以我对你的小提琴进行了一些修改,给你一个简单的例子:http://jsfiddle.net/3SSqM/41/(道歉,这里的一些代码不是很强大,而且在windows.document上存储变量真是不好的做法,但我只是想快速煽动一些可以证明这是如何工作的东西。以下是小提琴中值得注意的事项:

  • 每个部分的格式为“picks-group-NUMBER”
  • 有一个名为indexes的JS对象维护所有选择列表,每个键都是0-127的id,所以第一部分的第一个槽的id为0,第四个槽位于第32个部分的id为127
  • 当拖动事件结束时,您可以获取部分名称(即从表ID中提取NUMBER),然后使用所有选择的数据ID填充JS对象。我认为你应该这样做的方式如下:

     $('.picks-group').sortable({
      tolerance: "pointer",
      start: // some code
      stop: function (event, ui) {
          sorting = false;
          sectionNumber = getSectionNumber($(this)[0].id);
          $(this).find('li').each(function (i, obj) {
              var index = sectionNumber + i;
              indexes[String(index)] = $(obj).attr('data-id');
          });
      },
      change: // some code
    });
    

请注意,这将保留'nulls',这正是您想要的。当用户完成选择时,您可以将indexes JSON对象发送到Rails,这应该非常简单。在rails中使用JSON解析器将JSON对象转换为哈希值。

后端

现在,将这些数据存储在Mongoid中的方式是另一个有趣的问题。这实际上取决于您打算如何使用数据。如果您打算将所有数据存储到下次用户访问您的Web应用程序然后重新呈现他/她的选择,那么我将继续将整个对象存储为哈希的一个属性。类似的东西:

Class Pick
  include Mongoid::Document

  field :picks, type: Hash

end

这么简单!你不喜欢mongoid吗?

但是如果你打算查询数据,比如试图找出最佳选择者,那么这可能并不理想。现在可能性非常大,模式实际上取决于您的用例,这里有一些注意事项:

  • 您是否希望在自己的时间内对数据进行分析以供自己使用?在这种情况下,您仍然可以将其存储在哈希中,并在数据上运行map reduce。
  • 您是否需要实时分析,在这种情况下使用map reduce可能并不理想。我会根据您需要查询的粒度来细分数据。

因此,如果您需要知道第一个插槽中第一部分最受欢迎的选择,您可以执行以下操作:

Class Pick
  include Mongoid::Document

  field :pick_0
  field :pick_1
  # ... you get the idea
  field :pick_127

end
顺便说一下,实际上没有列出pick_0到pick_127的每个属性,我只是这样做是为了让你知道我的意思,如果你想沿着这条路走下去使用动态属性,如下所述:{{3 }}

重新发布视图

最后一件事你很好奇:如何在下次用户登录时重新创建视图。这取决于你如何存储数据,但是假设你将整个事物存储为哈希,在查看您只是迭代存储在哈希中的所有不同键值对并呈现您的表。这部分应该非常直接。

答案 3 :(得分:-1)

将列表另存为用户ID数组。使用Mongodb,这个数组通常会嵌入到用户文档中。

使用null s填充数组来表示空格,因此Tim,空,Bob,空在JS / Mongodb中变为['Tim', null, 'Bob', null]或使用用户ID保存位置ID,因此您拥有{ {1}}。哪一个更有效取决于平均有多少个插槽留空。使用[[0, 'Tim'], [2, 'Bob']]可能会更容易,所以我赞成它,其他条件相同。