When using Moped gem, I can store an array of hashes with:
users = [{username: "ben", password: "123456", type: "admin" }, {username: "joe", password: "abcd1234" }]
Mongoid::Sessions.default["collection"].insert(users)
With mongoid documents it would look like:
class User
field :username, type: String
field :password, type: String
end
users.each { |user_hash| User.create(user_hash) }
Which means an insertion operation for each.
Do you know a way to keep the single operation method? Maybe something like a transaction in ActiveRecord?
You can convert Documents back to Hashes and insert them with single call to #create:
User.create(users.map(&:attributes))
Related
I have been struggling for a few days trying to get queries to work. At the moment my model looks like this:
class Geojson
include Mongoid::Document
field :type, type: String, default: 'Point'
field :coordinates, type: Array
index({coordinates: "2dsphere"}, { bits: 12})
end
The following query returns nil:
Geojson.find(:coordinates => {"$nearSphere" => [-70.1197340629727, 4.67071244438]})
These are the current instances in my database:
[#<Geojson _id: 61b7b21a9eb0c9ef0aa5626d, type: "Point", coordinates: [-74.13041168951031, 4.6638117]>,
#<Geojson _id: 61b7b2619eb0c9ef0aa5626e, type: "Point", coordinates: [-74.1213041168951, 4.5638117]>]
I am able to query similar cases on mongosh with no issues, however I am not sure where the mistake is when doing it directly on rails.
I finally managed to make it work the following way: (for a 2d sphere index)
Geojson.where(:coordinates => {"$nearSphere" => [long, lat]}).to_a
Where longitude and latitude are the parameters received.
My model has a custom_fields column that serializes an array of hashes. Each of these hashes has a value attribute, which can be a hash, array, string, or fixnum. What could I do to permit this value attribute regardless of its type?
My current permitted params line looks something like:
params.require(:model_name).permit([
:field_one,
:field_two,
custom_fields: [:value]
])
Is there any way I can modify this to accept when value is an unknown type?
What you want can probably be done, but will take some work. Your best bet is this post: http://blog.trackets.com/2013/08/17/strong-parameters-by-example.html
This is not my work, but I have used the technique they outline in an app I wrote. The part you are looking for is at the end:
params = ActionController::Parameters.new(user: { username: "john", data: { foo: "bar" } })
# let's assume we can't do this because the data hash can contain any kind of data
params.require(:user).permit(:username, data: [ :foo ])
# we need to use the power of ruby to do this "by hand"
params.require(:user).permit(:username).tap do |whitelisted|
whitelisted[:data] = params[:user][:data]
end
# Unpermitted parameters: data
# => { "username" => "john", "data" => {"foo"=>"bar"} }
That blog post helped me understand params and I still refer to it when I need to brush up on the details.
I'm trying to follow the advice in Mongoid 3 - Check for uniqueness of composite key to have a model with a unique constraint on 2 fields.
The id declaration is this:
field :_id, type: String, default: &method(:generate_id)
private
def generate_id
user.id.to_s + offering.id.to_s
end
But if I do this, it has a conniption when I instantiate an object via new because it tries to generate the id before it has a user and offering and it (rightly) doesn't want to use the id of nil. I can pass in the user and offering as constructor parameters and everything is fine.
My question is, is this the right way of doing this? It feels dirty given all the obtuse wackyness I have to do just to get a unique constraint. The code isn't very intent revealing at all. Is there a better way?
With plain MongoDB you would create this index with JavaScript like so (assuming your collection name is registrations):
db.registrations.ensureIndex( { "user_id": 1, "offering_id": 1 }, { unique: true } )
To generate this index with Mongoid, add this to your model:
index({ user_id: 1, offering_id: 1 }, { unique: true })
And run rake db:mongoid:create_indexes.
If you want to keep generating your _id with generate_id, you could move the generation to a before_validate callback.
field :_id, type: String
before_validate :generate_id
private
def generate_id
self._id ||= "#{user.id}:#{offering}"
end
I have the following array:
#unregistered_users = ['my#email.com', 'your#email.com', ...]
Now, I want to create a document for each array element:
#unregistered_users.each do |email_address|
Model.create(email: email_address, user: self.user, detail: self)
end
But it only creates a single document (the first element of the array). The other array elements are simply not created. Why?
We're using Ruby 1.9.3-p385, Rails 3.2.12, MongoID 3.0.0 and MongoDB 2.2.3
Update #1
So, we had a custom _id field with a custom random token using SecureRandom.hex(64).to_i(16).to_s(36)[0..127].
After I removed it worked normally, but with regular mongo ID's (which is not what we want).
Update #2
This is how the token are being generated:
class Model
include Mongoid::Document
include Mongoid::Timestamps
...
field :_id, default: SecureRandom.hex(64).to_i(16).to_s(36)[0..127]
...
index( { _id: 1 }, { unique: true } )
end
Try something like this to check what are the errors on the mongoid model:
#unregistered_users.each do |email_address|
model = Model.create(email: email_address, user: self.user, detail: self)
puts model.errors.inspect unless model.persisted?
end
or use create! to raise an exception and see what's happening
I have 1000 users that i will be retrieving from Twitter, and I would like to save them at one shot, as opposed to doing 1000 insertions individually.
How can I do this on Mongoid? Something like this would rock:
TwitterUser.createMany([{:name=>u1}, {:name=>u2},{:name=>u3}] )
You should use the Mongo ruby driver to do this. You can pass an array of hashes to the insert method to create multiple documents at once (more info on this google groups discussion). Mongoid makes it easy to access the ruby driver.
The code would look something like this:
user_list = twitter_accounts.map do |account|
# create a hash of all the fields to be stored in each document
{ 'name' => account.name,
'username' => account.username
# some other fields...
}
end
Mongoid.master['twitter_users'].insert(user_list)
You almost got it, it's create, not createMany. You can use it like this:
TwitterUser.create([
{ username: "u1", display_name: "Display Name 1" },
{ username: "u2", display_name: "Display Name 2" },
{ username: "u3", display_name: "Display Name 3" }
])
Also, as #bowsersenior points out, it's a good idea to use it with Array#Map:
TwitterUser.create(
#users_array.map do |u|
{ username: u.username, display_name: u.name }
end
)
From the Mongoid#Persistence Docs:
Model.create
Insert a document or multiple documents into the database
Model.create!
Insert a document or multiple documents into the database, raising an error if a validation error occurs.
Just use MongoidModel.create directly.