Rails with MongoID Embedded Doc No Mapping To My Model - ruby-on-rails

This is probably an issue on my part I have been struggling through some Rails and mongo samples to get up to speed (This is all for learning not for work.) This current project I get a JSON string from a api and I put that in mongo. So changing the document structure is not really an option. I then have been trying to map that into a Rails model so I can use the data.
The JSON is a report that contains many transactions that contain many LogLines.
Snippet from a doc
< Report _id: 583 c3baac0baf90a7ee26f6e,
ReportName: "PtSomIntPerfThreeLevelTest-1480341940187",
TestName: "PtSomIntPerfThreeLevelTest",
Transactions: [{
"colId" => "50437d6c-49c1",
"InfoPlate" => "[0SXdokPL-R13VQZwi]",
"rowId" => "1",
"sortDate" => 1480341975952,
"transactionType" => "REQUEST",
"description" => "description text for my document",
"startDate" => "11/28/2016 14:06:15",
"startDateMS" => 1480341975952,
"endDate" => "11/28/2016 14:06:23",
"endDateMS" => 1480341983069,
"finalEndDate" => "11/28/2016 14:06:23",
"finalEndDateMS" => 1480341983069,
"completeDuration" => "7 seconds",
"completeDurationMS" => 7117,
"feedProcessingDuration" => "7 seconds",
"feedProcessingDurationMS" => 7117,
"logLines" => [{
"id" => "1062b1ca-0f04",
"timestamp" => 1480341975952,
"transactionType" => "REQUEST",
"transactionStep" => "RECEIVE",
"collationId" => "50437d6c-49c1-438a-9b8",
"runName" => "runName-1480341940187",
"msg" => "Import default",
"elapsedSeconds" => "0.0",
"elapsedMS" => 0,
"InfoPlate" => "[0SXdokPL-3rmxW3oH]"
},
I have a report model, and a transaction model ( I will do LogLines after doing 1 at a time) My report model does fine I can get a single report doc based on an ID and it returns the report. I can then do a "report.transactions" and get a json blob of the transactions (almost always multiple transactions in a report) BUT its not recognized as a transaction model (will post all code below) So I cannot say transaction.InfoPlate I get a no such method error. I have relationships in my model but I also have a " field :Transactions, type: Array" which in looking at the rails cast on mongoid is not in theres. Without that I get nothing so the relationship "embeds_many :transactions" does not allow me to get a report.transaction. Sorry if that is confusing my Rails lingo is low. Short and sweat I want to get a report then get the transactions and be able to do transactions.ColID and get the col ID .
My goal is to get a model for each part of the document report, transaction, LogLines. I do not seem to understand how to do that.
Report Model (Works Fine)
class Report
include Mongoid::Document
field :ReportName, type: String
field :TestName, type: String
field :Transactions, type: Array
field :ReportDurationMS, type: String
embeds_many :transactions
end
Transaction Model
class Transaction
include Mongoid::Document
field :colId, type: String
field :InfoPlate, type: String
field :rowId, type: String
field :sortDate, type: String
field :transactionDate, type: String
field :description, type: String
field :startDate, type: String
field :startDateMS, type: Integer
field :endDate, type: String
field :endDateMS, type: Integer
field :finalEndDate, type: String
field :completeDuration, type: String
field :completeDurationMS, type: Integer
field :feedProcessingDuration, type: String
field :feedProcessingDurationMS, type: Integer
field :logLines, type: Array
embedded_in :report, :inverse_of => :transactions
end
Report Controller (Index Method) Debug logger is just there while I hack around
def index
#reports = Report.all
Rails.logger.info("********* #{#reports.inspect} ***********")
end
Transaction Controller (This is what I can't get to return a transaction as a model) I get a transaction back from #report.transactions but its just a string of json as opposed to a ruby model. Or at least I can't call anything like #transaction.colId. Just returns no such method. Transactions is an array there are many so I did try transactions.first.InfoPlate but still to me it seems Rails just seems the transaction that comes back as a string of JSON not an object.?
class TransactionsController < ApplicationController
before_action :set_transaction, only: [:show, :edit, :update, :destroy]
def index
#report = Report.find(params[:report_id])
#transaction = #report.transactions
Rails.logger.info("********* report #{#report.inspect} ***********")
#transactions = #report.transactions
Rails.logger.info("********* transaction #{#transactions.inspect} ***********")
end
My route
resources :reports do
resources :transactions
end

The above post made me go back in and match all my case and that seems to be working.

Related

rails error on not existing line

I have an file named weekly_report_message.rb with 26 lines of code
module MessageTypes
class WeeklyReportMessage
include Mongoid::Document
include Mongoid::Timestamps
field :last_week_date, type: DateTime
field :this_week_date, type: DateTime
field :runs, type: Integer
field :runs_completed, type: Integer
field :distance, type: Float
field :distance_completed, type: Float
has_one :message
def basic_info
{
:last_week_date => !last_week_date.blank? ? last_week_date.strftime("%Y-%m-%d") : "",
:this_week_date => !this_week_date.blank? ? this_week_date.strftime("%Y-%m-%d") : "",
:runs => runs,
:runs_completed => runs_completed,
:distance => distance.round(1),
:distance_completed => distance_completed.round(1)
}
end
end
end
this code works ok on local machine and on production server in some cases but there one case in which basic_info method is called and this call results in error (only on production) which says:
undefined method `strftime' for nil:NilClass
app/models/message_types/weekly_report_message.rb:30:in `basic_info'
but I dont' have 30 lines in this file, the code was deployed, I checked with cat the code on server and is the same as on local, no matter what changes I make in this file it still give me this error, I restarted nginx but still have this error

Create or increment a field in Mongoid with Rails 4

I have a model like this:
class Invoice
include Mongoid::Document
field :local, type: String
field :date, type: DateTime
field :total, type: Float
end
class LogInvoice
include Mongoid::Document
field :date, type: Date
field :local, type: String
field :summary, type: Hash
end
The field summary is a Hash like this:
summary = {invoices : 0, amount : 0.0}
For each invoice stored, I must create an LogInvoice for that day with total number of Invoice (count of Invoice) in LogInvoice.summary[:invoices] and with the total ammount of money (summatory of Invoice.total) in LogInvoice.summary[:amount]
If the Invoice exist I must to update the LogInvoice of that day.
I can't know if a object existe because LogInvoice.find() returns an exception if it doesn't, so how can I update an existing object LogInvoice or created if it doesn't?
Thank you
If I've understood your question you should do something like this:
my_invoice_criteria = Invoice.all # or Invoice.where date: the_date_I_want or anything else
my_invoice_criteria.each do |invoice|
log_invoice = LogInvoice.find_or_create_by date: invoice.obtain_proper_date
#Use this for atomic persistence
log_invoice.inc :"summary.invoices" => 1
log_invoice.inc :"summary.amount" => invoice.total
#Use this for standard persistence
log_invoice.summary[:invoices]+=1
log_invoice.summary[:amount]+=invoice.total
log_invoice.save
end
Ref:
Atomic persistence Docs
Standard persistence Docs

Can MongoDB automatically generate special indexes for geolocation data?

I currently have a Mongoid model in a Ruby on Rails application as such:
class Listen
include Mongoid::Document
field :song_title, type: String
field :song_artist, type: String
field :loc, :type => Array
field :listened_at, type: Time, default: -> { Time.now }
index(
[[:loc, Mongo::GEO2D]], background: true
)
end
When I try to query the collection for example
listens = Listen.where(:loc => {"$within" => {"$centerSphere" => [location, (radius.fdiv(6371))]}})
I am returned the error (locations have been blanked out, the X's are not returned)
Mongo::OperationFailure (can't find special index: 2d for: { loc: { $within: { $centerSphere: [ [ XX.XXXXXXX, X.XXXXXXX ], 0.0001569612305760477 ] } } }):
I know I can create the indexed through a rake task such as rake db:mongoid:create_indexes but I don't want to have to do this every time a model is created. Is there any way for the model to create this automatically on insert to the collection?
Nope there is no way.
You must create indexes (not just Geo) once, to use it.

Outputting a serialized object in Rails

In Rails 2.3.6 I'm storing some serialized data in a database field.
My "feed_event.data" field in my database is stored as text and is (for example) equal to:
{:post=>{:pic=>"http://s3.amazonaws.com/criticalcity/datas/3524/big_thumb/send-a-letter.jpg", :name=>"Un’istruzione perfetta", :id=>1995, :authors=>"Delilah"}, :user=>{:pic=>"http://s3.amazonaws.com/criticalcity/avatars/537/thumb/DSCN2744.JPG", :name=>"Luci!", :id=>537}}
Now I need to output this field as a string (exactly as it is in the database), but when I ask:
puts feed_event.data
outputs:
postpichttp://s3.amazonaws.com/criticalcity/datas/3524/big_thumb/send-a-letter.jpgnameUn’istruzione perfettaid1995authorsDelilahuserpichttp://s3.amazonaws.com/criticalcity/avatars/537/thumb/DSCN2744.JPGnameLuci!
Why?
How can I output it as a yaml string?
UPDATE
In order to create it I have this in my FeedEvent model:
class FeedEvent < ActiveRecord::Base
has_many :user_feed_events, :dependent => :destroy
has_many :users, :through => :user_feed_events
serialize :data
end
And in order to create a new FeedEvent element I do:
feed = FeedEvent.create(:event_type => "comment #{commentable_type}", :type_id => id, :data => {:user => {:id => user.id, :name => user.name, :pic => user.avatar.url(:thumb)}, :comment => {:id => id, :body => body, :commentable_id => commentable_id, :commentable_type => :commentable_type, :commentable_name => commentable.name}})
UPDATE #2
following nzifnab's hint I used the .to_yaml method, but what Rails outputs in this case is:
data: "--- \n:post: \n :pic: http://s3.amazonaws.com/criticalcity/datas/3524/big_thumb/send-a-letter.jpg\n :authors: Delilah\n :name: \"Un\\xE2\\x80\\x99istruzione perfetta\"\n :id: 1995\n:user: \n :pic: http://s3.amazonaws.com/criticalcity/avatars/537/thumb/DSCN2744.JPG\n :name: Luci!\n :id: 537\n"
Also commenting "serialize :data" in the model outputs the same.
Thanks,
Augusto
When you call feed_data.data rails has automatically de-serialized your string. You could print it out like this:
feed_data.data.inspect to get the ruby hash representation as a string, but since it's already de-serialized it for you do you need to do anything else?
you can call everything on it like feed_data.data[:post][:pic]
I'm not sure what method you can use to grab the raw serialized string from the record, but usually you don't need to.
By default, serialization is made in a Hash.
Simply loop it to display it's content:
<% feed_event.data.each do |key, value| %>
<%= "#{key}: #{value}" %>
<% end %>
I'm just unsure about nesting level here but you've got the idea.
as you mentioned in your Update, the right way to do this is to put "serialize :data" in your model.
Then, you can access the data attribute as a Hash, that's the default, and it gets automatically persisted when you save your object.
Important Note:
One important thing for this to work is that you define the database field as text or string -- not as a binary field -- otherwise this will not work correctly!

Mongodb , rails modify non-array - debug kind_of? Array >> true

I've been trying to add to an array (or what ruby is saying is an array), but keep getting an error from mongo which says
Cannot apply $addToSet modifier to non-array
when I try to run
User.collection.update({'id'=> current.id},{'$addToSet'=>{ 'following' => current.id}})
User.collection.update({'id'=> user.id},{'$addToSet'=>{ 'following' => user.id}})
or the mongomapper version
User.push_uniq(current.id, :following => user.id)
User.push_uniq(user.id, :followers => current.id)
When I output
<%= debug #user.following.kind_of? Array %>
returns true
However, when running
db.users.find()
directly agains mongo, I get
{ "_id" : ObjectId("4c4a196f15a79004e0000007"), "email" : "test#test.com", "foll
owers" : null, "following" : null, "password_hash" : "98f2188de42bce1554d08fbc81
d5c99a2c234933", "password_salt" : "25d80a83cfe3d126cdbe9fec2b731ab9ea57c3b8", "
username" : "test" }
I would have expected following and followers to be [], not null.
When I output debug #user.followers, rails shows --- []
My model to create the user is
key :username, :type => String
key :email, :type => String
key :password_hash, :type => String
key :password_salt, :type => String
key :followers, :type => Array
key :following, :type => Array
The error leads me to believe that the user.followers is being found, but can't be updated.
When I change
User.push_uniq(current.id, :testing => user.id)
I don't get an error, so I think i have that part right.
Any suggestions?
This works for be in 0.8 when declaring the key using key :following, Array instead of key :following, :type => Array.
I tried both push_uniq and collection.update, and didn't get errors on either one. In your collection.update example, you do need to use _id: value instead of id: value since that command is being passed to mongo directly.
Turns out this is a bit of an inconsistency with defining keys in mongomapper.
Don't use the :type => qualifier with Arrays.
I deleted the entire collection, removed :type, and recreated everything and now it works.

Resources