Ruby/Rails: How to map and condense database query into multidimensional arrays - ruby

I have a Location model and a Service model. I also have a Meeting model that belongs to one Location and to one Service and has a weekday param and a time param.
I need to display all of my meetings by location then by service in a condensed format. My plan was to pull all my meetings (it's a closed database and will remain very low volume) by location and then by type (service), condense them according to the day while putting them in a multidimensional array that gets read out in the view. The following is how it should look in the view:
Tennille Campus (from Location model)
Weekend Services (a Service)
Saturdays (weekday param) at 7:00pm (time param)
Sundays at 9:30am and 11:15am
Youth
Sundays at 6:00pm
My plan was for output array to look like:
array = [["Weekend Services", ["Saturdays at 7:00pm", "Sundays at 9:30am and 11:15am"]], ["Youth", ["Sundays at 6:00pm"]]]
I figured I would pass the location to the function and use my scope for that part but I'm getting lost when it comes to ordering meetings by services in the services order of importance (a Service scope). The problem I'm having is trying to not make so many requests to the database with if statements. How do I do this without the if statements?
**meeting.rb**
belongs_to :location
belongs_to :service
#scopes based on meeting types
scope :in_partial, -> { where(level: 1) }
scope :location, lambda { |location| where(:location_id => location) }
scope :titled, lambda { |title| where(:title => title) }
scope :on_day, lambda { |weekday| where(weekday: Date::DAYNAMES.index(weekday)) }
**service.rb**
has_many :meetings
scope :ordered_by_priority, -> { order(priority: :asc) }
I'm using rails 5 and a postgresql db.
Thank you for the help and let me know if there's a better way than using the array to do this.

Related

Mongoid's .includes() Not Populating Relations

I am using Mongoid v4.0.2, and I'm running into an interesting issue using .includes(). I have a record that represents invoices, who has a list of charges.
I want to query for a single invoice and have the charges be populated after I run the query. According to the docs (search for "Eager Loading"), I should be able to do something like this to have Mongoid populate the charges:
Invoice.includes(:charges).find_by({ _id: <objectId> })
When I get the record back the charges are still showing up as a list of ObjectId's, and removing the .includes() seems to have no effect one way or another. I've verified each charge exists in the record I'm querying for, so I'm confused why they aren't populating.
I believe I have the data models set up correctly, but I'll include them here for completeness.
class Invoice
include Mongoid::Document
has_many :charges
field :status, type: String
field :created, type: Time, default: -> { Time.now }
end
class Charge
include Mongoid::Document
field :created, type: Time, default: -> { Time.now }
field :transactionId, type: String
field :category, type: String
field :amount, type: Float
field :notes, type: String
belongs_to :invoices
end
There is no reason to use includes if you are only finding one document. Just find the document and then access the relation. Either way, 2 database requests will be issued.
The only time includes provides a performance increase is when you are loading multiple relations for multiple documents, because what Mongoid will do is load the queried documents, go through and gather all of the ids that should be queried for all of those documents and then query for all relations as one database call using the :id.in => ids feature. In your case, there is no point to do this.

Return database results in the same JSON parent and children

I have Team and Players classes and want to return the data in one JSON string which contains Team info but at the same time it displays all the information about the players.
class Team < ActiveRecord::Base
has_many :players
end
class Players < ActiveRecord::Base
belongs_to :team
end
I know how to retrieve the information about team and players but not in the same query. Another problem is I don't how to merge the result JSONs in one JSON.
team = Team.last.to_json
player = team.players.to_json
How can I query the info about Team and Players in the same query. I tried:
#team = Team.includes(:players).where(players: {team_id: Team.last}).last.to_json
and it only returns me information about the team. I want a JSON like:
-id
-name
-players
-player
-player
In case it's impossible, how can I merge into one JSON all the information from the two queries.
You can write a "join" to incorporate the players in the team with the team information. At that point you'll have a structure that has the information needed to create the JSON. See "12 Joining Tables" from the Active Record documentation for more information.
Or, you can make two separate queries, then create a bit more complex JSON hash or array allowing you to output both sets of data into one larger serialized object. For instance:
require 'json'
team = {
'name' => 'bears'
}
players = {
'1' => 'fred',
'2' => 'joe'
}
puts ({
'team' => team,
'players' => players
}).to_json
Here's the output:
{"team":{"name":"bears"},"players":{"1":"fred","2":"joe"}}
Here's the data returned back to the Ruby object:
data = '{"team":{"name":"bears"},"players":{"1":"fred","2":"joe"}}'
JSON[data]
# => {"team"=>{"name"=>"bears"}, "players"=>{"1"=>"fred", "2"=>"joe"}}
Also, since you're using Sinatra, it's not necessary to use Active Record. Sequel is a very good ORM, and is my personal favorite when working with Sinatra. You might find it easier to work with.
Another option to manual serialization is to use ActiveModel::Serializer which allows you to define relationships between objects and gives you finer grained choices of what to include when you serialize, what to filter out and what related objects to preload. An alternative could also be Rabl which also has quite a nice API.
If you're just playing around with a small amount of JSON this might be overkill, but it's a nice practice to be more organized

ruby serialise a model to represent in

I have a set of legacy database tables that i cannot normalize out to what should have been done in the first place. e.g one big table with 200 columns.
I'm building an API and would like to represent this data to the consumer in a better state, and perhaps address the database issues at a later stage, there are many backend systems that reply on the data and changes are not easy.
I wanted to represent the current database schema using Active Record, however perform a model transformation into a new model that will be used for presentation only to an API consumer as json data.
current database schema:
Products table (200 columns)
New Model:
Product
+ Pricing
+ Assets
+ Locations
+ Supplier
I could hard-code a json string in a template, but feel that would not be a very poor approach.
What approach or gem would you recommend to tackle this best?
I have looked at :
RABL
ActiveModel::Serializers
If you define an as_json method that returns a hash, ActiveRecord will take care of the serialization for you. E.g.
class Product < ActiveRecord::Base
def as_json options = {}
{
product: <product value>,
pricing: <pricing value>,
# ... etc.
}
end
end
Now you can do:
> Product.first.to_json
=> "{\"product\":<product_value> ... }"
You can even render these as json from the controllers via:
render json: #model

Rails 4 - Comparing two variables in scope

How do you compare two variables in a custom scope with Rails 4 ?
Documentation and common examples always show basic comparisons with table attributes
Like in this example :
class Post < ActiveRecord::Base
scope :created_before, ->(time) { where("created_at < ?", time) }
end
But in my case, I want to compare a datetime from my Model to a date string coming from a form submission
scope :departure, -> (departure) { where("departure = ?", "%#{departure}%")}
The problem is that I want to do some manipulation to my model variable before the comparison (ex, convert to string or change format)
I tried with different methods like this one
def self.departure(departure)
#date_without_time = flights.departure.to_date.to_s
where(#date_without_time, "= ?", departure)
end
But the condition end up like this (2012-01-03) instead of (departure = "%#{departure}%")
Is there a better way to do it?
To be more general, how do you create methods or scope to compare two variables instead of only comparing one variable to a model attribute?
In this case, I want to compare my model attribute which is a Datetime with a form submitted string date, so before I can compare the two dates I need to do some treatment on my model attribute since I need to ignore the time part and format it to a string
Update :
I ended up making it work using this method, but I'd still like to know if it's possible to call class or instance method on a Model column in a scope or if there is a better way to handle the Datetime/form date comparison in a better way
scope :departure_s, -> (departure) { where("departure > ?", departure.to_datetime.beginning_of_day)}
scope :departure_e, -> (departure) { where("departure < ?", departure.to_datetime.end_of_day)}
scope :departure, -> (departure) { departure_e(departure).departure_s(departure)}
You're taking the wrong approach trying to manipulate models. You're running a query to return models, you don't have any models to manipulate yet.
You can accomplish what you're trying to do on the database level with SQL. I would write the scope like this
if the departure variable is a string that represents a date in the format 'yyyy-mm-dd' you can do
scope :departure, -> (departure) { where("date(departure) = ?", departure )}
This assumes you're using postgres, it might be slightly different for mysql. the date() function converts a datetime to date on the database elevel

Dynamics CRM 2011 LinQ to Find New Records

I have a bunch of custom entity records in a List (which comes from a csv file).
What is the best way to check which records are new and create those that are?
The equality check is based on a single text field in this case, but I need to do the same thing elsewhere where the equality check is based on a lookup and 2 text fields.
For arguments sake lets say I was inserting Account records, this is what I currently have:
private void CreateAccounts()
{
var list = this.GetAccounts(); // get the list of Accounts, some may be new
IEnumerable<string> existingAccounts = linq.AccountSet.Select(account => account.AccountNumber); // get all Account numbers in CRM, linq is a serviceContextName variable
var newAccounts = list.Where(account => !existingAccounts.Contains(account.AccountNumber)); // Account numbers not in CRM
foreach (var accountNumber in newAccounts) // go through the new list again and get all the Account info
{
var account = newAccounts.Where(newAccount => newAccount.AccountNumber.Equals(accountNumber)).FirstOrDefault();
service.Create(account);
}
}
Is there a better way to do this?
I seem to be iterating through lists too many times, but it must be better than querying CRM multiple times:
foreach (var account in list)
{
// is this Account already in CRM
// if not create the Account
}
Your current method seems a bit backwards (get everything out of CRM, then compare it to what you have locally), but it may not be too bad depending on how many accounts you have ie < 5000.
For your simple example, you should be able to apply a where in statement.
Joining on multiple fields is a little more tricky. If you are running CRM > R12, you should be able to use the ExecuteMultipleRequests, creating a seperate request for each item in your list, and then batching them all up, so there is one big request "over the wire" to CRM.

Resources