Adding ObjectID manually in MongoDB (Rails/Mong - ruby-on-rails

I'm working with Rails 4.0 and MongoDB (Mongoid) and I have the following Code to create Documents:
lines.each do |l|
Insert.create(:position => 0, :content => l, :schema_id => Moped::BSON::ObjectId.from_string("52419d2f80a9b88bb9000002"))
end
This works fine and I get the following output in my Mongo-Database:
{
"_id": {
"$oid": "5241ff1280a9b8f16e000057"
},
"position": "0",
"content": "blabla",
"schema_id": "52419d2f80a9b88bb9000002"
}
The only Problem is, that I want to have a "$oid": before the actual schema_id like this:
...
"schema_id": {
"$oid": "52419d2f80a9b88bb9000002"
}
and I really got confuse of how I can insert that "$oid" followed by a colon manually....
Would be great if you could help me...
Thx in advance!!

You should not have $oid at all, neither in your schema_id or in _id , that would make indexing operations hard, or not possible into Mongodb. You probably have $oid because you could be parsing the document to a json in inserting in the DB somehow, anyways, If I were you, I would write a migration to fix this in the db layer, and fix the code to not use the JSON version before persisting.

My suspicion is like Arthur's, and I'll try to clarify. You should re-examine your Ruby code and make sure that you are not somehow implicitly stringifying your object ids in some Ruby code that you did not put in your question.
Your question is confusing to me as I try to decipher it, and probably confusing to you as you try to debug. This is because you are working in Ruby and yet give us the documents in JSON that are not from Ruby nor from the mongo shell, suggesting that you are trying to debug from a context separate from the code in question.
I gently suggest that you follow the standard Rails practice of writing tests. Please write a test that recreates your problem by inserting documents and dumps the document in question using Ruby.

Related

Clone a mongodb collection from within Rails Mongoid

I am trying to implement this solution in rails, using the collection aggregate method, to clone an entire collection within the same database.
In mongo shell, this works perfectly, and a cloned collection is created successfully:
db.source_collection.aggregate([ { $match: {} }, { $out: "target_collection" } ])
The rails-mongoid alternate, according to my research, should be this, which runs without errors:
SourceCollection.collection.aggregate({"$match" => {}, "$out" => "target_collection"})
#<Mongo::Collection::View::Aggregation:0x000000055bced0 #view=#<Mongo::Collection::View:0x44951600 namespace='DB_dev.source_collection' #filter={} #options={}>, #pipeline={"$match"=>{}, "$out"=>"target_collection"}, #options={}>
I also tried with an array
SourceCollection.collection.aggregate([{"$match" => {}}, {"$out" => "target_collection"}])
#<Mongo::Collection::View::Aggregation:0x000000054936d0 #view=#<Mongo::Collection::View:0x44342320 namespace='DB_dev.source_collection' #filter={} #options={}>, #pipeline=[{"$match"=>{}}, {"$out"=>"target_collection"}], #options={}>
UPDATE
This simplest syntax also works in Mongo console:
db.source_collection.aggregate( { $out: "target_collection" } )
But the respective syntax does not seem to work in Ruby:
SourceCollection.collection.aggregate({"$out" => "target_collection"})
Unfortunately, although there are no errors, the collection is not created.
Any clues as to the way I can make this happen?
Mongo gem version 2.5.3
Update2
Apparently $out is not considered in the pipeline, thus rendering the aggregation invalid.
This can be fixed with code... I am looking for a module/class/method override, as contacting mongodb issue tracking system for a change request might not be as quick..
UPDATE - FINAL
This issue has been solved, by help of Thomas R. Koll (thank you).
I add an update to post the response I got from the ticketing service of MongoDB, which pretty much describes Thomas's solution.
The reason you're not seeing the results without count is that the
aggregate method returns a lazy cursor; that is, the query does not
execute until the return value of aggregate is iterated over.
Calling count is one way to do this. This is the same behavior
you'll see if you call find or if you call aggregate without
specifying $out; the difference is that $out has an side-effect
beyond just returning the results, so it's more obvious when exactly
it occurs.
Found the solution, and I have to explain a few thigs:
This returns a Mongo::Collection::View::Aggregation object, it won't send a query to the database
User.collection.aggregate({"$out": "target_collection"})
Only when you call a method like count or to_a on the aggregation object it will be sent to the server, but if you pass a hash you'll get an error, so the pipeline has to be an array of hashes to work
User.collection.aggregate([{"$out": "target_collection"}]).count

Save json api output in database

I am using Net::HTTP to get data out of a JSON API, and want to save the response given by the API in my database.
An example response is:
{
"id": 1234,
"applicationname": "test12347127834",
"publish_key": "79123798d7981728397dddasetr7912",
"streamname": "xxnamexx",
"ingest": "rtmp://master.cdn.com/SSDEL1"
}
I want to save the id, applicationname, publishkey and streamname.
I have absolutely no clue how that could work. I would highly appreciate if someone could give me a hint, tips or examples.
There are two approaches I see. One is simply to save your JSON as text. If you won't be doing a lot of post-processing of this attribute, that might do the job.
The second approach is to use :serialize on the column and Rails 3 will magically handle the back-and-forth interpolation for you. You'll be able to do
some_object.response.id
To do that simple do:
class YourClass
serialize :response

Concurrency and Mongoid

I'm currently trying my hand at developing a simple web based game using rails and Mongoid. I've ran into some concurrency issues that i'm not sure how to solve.
The issue is i'm not sure how to atomically do a check and take an action based upon it in Mongoid.
Here is a sample of the relevant parts of the controller code to give you an idea of what i'm trying to do:
battle = current_user.battle
battle.submitted = true
battle.save
if Battle.where(opponent: current_user._id, submitted: true, resolving: false).any?
battle.update_attribute(:resolving, true)
#Resolve turn
A battle is between two users, but i only want one of the threads to run the #Resolve turn. Now unless i'm completely off both threads could check the condition one after another, but before setting resolving to true, therefore both end up running the '#Resolve turn' code.
I would much appreciate any ideas on how to solve this issue.
I am however getting an increasing feeling that doing user synchronization in this way is fairly impractical and that there's a better way altogether. So suggestions for other techniques that could accomplish the same thing would be greatly appreciated!
Sounds like you want the mongo findAndModify command which allows you to atomically retrieve and update a row.
Unfortunately mongoid doesn't appear to expose this part of the mongo api, so it looks like you'll have to drop down to the driver level for this one bit:
battle = Battle.collection.find_and_modify(query: {oppenent: current_user._id, ...},
update: {'$set' => {resolving: true})
By default the returned object does not include the modification made, but you can turn this on if you want (pass {:new => true})
The value returned is a raw hash, if my memory is correct you can do Battle.instantiate(doc) to get a Battle object back.

How is this method memory intense?

I found the following method to be a HEAVY memory user on Ruby 1.8.7 and return absolutely no results (when there should be lots). The method also works like a charm on Ruby 1.9.2, returning all the wanted results while consuming no memory at all (or so!). I guess that's because a local variable has the same name as the containing method, but anyone have a clear answer for that?
def contact_of
contact_of = Circle.joins(:ties).where('ties.contact_id' => self.guid).map { |circle| circle.owner } || []
return contact_of.uniq!
end
By the way, I'm running Rails 3.1.1.
Thanks!
UPDATE : There's a part of the question that is erroneous. The fact that no contacts are returned when there should be is my misunderstading of 'uniq!' instead of 'uniq'. The first one does return 'nil' when no duplicates are found.
Still trying to figure out the memory problem...
Yeah, contact_of.uniq! would make a recursive call to the same function. I'm surprised it works in Ruby 1.9, actually.
Also, your DB query is terrible, because it retrieves a lot of unnecessary records and then does further select logic on the Ruby side. You probably want to start the find from Owner, not Circle.

Creating a new rails database record from an external script?

I'm trying to send a POST request from an external Ruby script to a Rails app via HTTP#post_form. The request is made to the create action (i.e. the URI is http://server/controller).
If I encode a single parameter into the request, everything is fine:
HTTP::post_form(uri, { :my_param => "value" })
Though I do have to explicitly pull out my_param from params manually, in the controller. This seems inefficient, and breaks creating a new record from within the app itself (because that parameter is not there). I'm consequently trying to make my script pose as Rails itself, passing the appropriate data as the controller would expect it, e.g.
HTTP::post_form(uri, { :object => { :my_param => "value" } })
However, this doesn't work. post_form seems to be escaping my hash into something different, i.e.
{ "object" => [\"my_param\", \"value\"] }
Which obviously doesn't do the same thing. Am I missing something obvious in the way I'm passing the data? Or can I not achieve what I'm after (creating a new record from outside the app)?
One straightforward way might be to simply imitate how Rails formats its parameters, like this:
params = { :my_param => "value", ... }
params = Hash[params.map { |key,value| ["object[#{key}]",value] } ]
HTTP::post_form(uri, params)
Edit: Well, look at that, I looked around a bit and found that Rails actually gives you a method to do the same thing using their own mechanism:
require 'active_support/core_ext'
...
HTTP::post(uri, parameters.to_param)
The to_param method will treat Arrays correctly, and everything else too. Notice that in this case you want to use HTTP::post, not post_form, since the parameters are already converted to a string.
I don't know much about post_form but the natural solution for me would be to use an ActiveResource object.
ActiveResource is available to ruby as well as to Rails. you use it just like you use a model only it posts and gets using XML
There is a Railscast on how this works here
http://railscasts.com/episodes/94-activeresource-basics
http://railscasts.com/episodes/95-more-on-activeresource
I think you'll find that this is a better fit for your requirements than post_form but as I say, I'm not familiar with post_form.

Resources