I currently have the following code to find all the unique IDs of comments placed by a user. This works "fine". However, it's really slow for users with a lot of comments and I'm trying to figure out if there is a more elegant way of handling this as it doesn't seem to be the best solution.
def find_unique_user_grades
#comments = []
#environment.users.includes(:comments).map(&:comments).select do |comments|
comments.select { |comment| #comments.push(comment.id) }
end
#comments.uniq!
end
I'm hoping someone can help me with this.
You should always prefer to do this in the database, not in Ruby. The code you've posted will load all users from the database, and then convert the raw row data to ActiveRecord objects, which is (comparatively) extremely expensive. You don't need any of that data to join through to comments. Then, you'll do the same for every user's comments (query and create ActiveRecord objects) and again, you don't need any of that to get at the comment's id column.
What you're after (assuming I've guessed correctly at the shape your schema) is a simply join followed by a pluck. This will run a single query and return a single array of numbers, without any of the cost of loading users, creating objects, loading comments, creating objects, or iterating in Ruby.
Finally, it will also perform the distinct query in the database, where it can take advantage of any relevant indexes, rather than uniqing in Ruby.
The correct query is something near to:
#environment.users.joins(:comments).distinct.pluck('comments.id')
I have Hanami models User and UserInfo that have has_one association.
Repositories look the following:
class UserInfoRepository < Hanami::Repository
end
class UserRepository < Hanami::Repository
associations do
has_one :user_info
end
end
Question: who can I join and load both tables with one query? (I am looking for something similar to Rails' includes).
So far I've tried
def users_with_info
users.join(:user_info)
end
It does the join, but does not select columns from user_infos table.
Thanks in advance.
When you fetch data via a Repository in Hanami the result set is mapped into entities.
By default the UserRepository will map to the User entity. I'm guessing that that entity does not have attributes for the columns from user_info.
What you need to do is create an Entity that can contain the data that you want to fetch from the database and then call .as(ThatEntityYouCreated) on the result set. E.g.,
def users_with_info
users.join(:user_info).map_to(UserWithInfo)
end
If you do not want to create an Entity and just want to get a plain hash, you can do this:
users.join(:user_info).map.to_a
However, I consider this to be a crutch. You should not return Hashes from your Repository methods.
I believe we faced this exact issue with a teammate of mine on one of our Hanami project, and this is how we solved it.
We basically bypassed Hanami repository, going straight to the underlying ROM relation, using ROM::Relation#wrap in order to get our User entity joined with the entity of interest.
Let met know if it helped you, or if you need more details. Cheers!
checklist = model("user_checklist").findAll(include="tb_machine_checklist");
Above is the query I am using to fetch records from a table called user_checklist and it is related to table called tb_machine_checklist.
Now there is a table called "tb_machine_checklist", however it it gives me an error saying;
The "tb_machine_checklists" table could not be found in the database.
Why is the "s" being added when I didn't specify?
Be sure to read the chapter in the documentation about Conventions.
CFWheels adopts a Rails-style naming conventions for database tables and model files. Think of a database table as a collection of model objects; therefore, it is named with a plural name. Think of a model object as a representation of a single record from the database table; therefore, it is named with a singular word.
Fortunately, CFWheels lets you override most conventions with a little bit of extra code.
In your tb_machine_checklist model's init method, add this line of code:
<cffunction name="init">
<cfset table("tb_machine_checklist")>
</cffunction>
Then any queries that CFWheels writes for you will use your singular table name instead of the conventional plural one.
I guess the include in findAll specifies the model name, not the table Name. Wheels ORM pluralizes in some occasions the model Name and maps it to the table Name. Maybe you could explicitely set the table name for the model in the model?
I solve this matter, My models were not named the same as my database tables. and once that was done properly, this problem went away.
I just ran into an interesting situation about relationships and databases. I am writing a ruby app and for my database I am using postgresql. I have a parent object "user" and a related object "thingies" where a user can have one or more thingies. What would be the advantage of using a separate table vs just embedding data within a field in the parent table?
Example from ActiveRecord:
using a related table:
def change
create_table :users do |i|
i.text :name
end
create_table :thingies do |i|
i.integer :thingie
i.text :discription
end
end
class User < ActiveRecord::Base
has_many :thingies
end
class Thingie < ActiveRecord::Base
belongs_to :user
end
using an embedded data structure (multidimensional array) method:
def change
create_table :users do |i|
i.text :name
i.text :thingies, array: true # example contents: [[thingie,discription],[thingie,discription]]
end
end
class User < ActiveRecord::Base
end
Relevant Information
I am using heroku and heroku-posgres as my database. I am using their free option, which limits me to 10,000 rows. This seems to make me want to use the multidimensional array way, but I don't really know.
Embedding a data structure in a field can work for simple cases but it prevents you from taking advantage of relational databases. Relational databases are designed to find, update, delete and protect your data. With an embedded field containing its own wad-o-data (array, JSON, xml etc), you wind up writing all the code to do this yourself.
There are cases where the embedded field might be more suitable, but for this question as an example I will use a case that highlights the advantages of a related table approch.
Imagine a User and Post example for a blog.
For an embedded post solution, you would have a table something like this (psuedocode - these are probably not valid ddl):
create table Users {
id int auto_increment,
name varchar(200)
post text[][],
}
With related tables, you would do something like
create table Users {
id int auto_increment,
name varchar(200)
}
create table Posts {
id auto_increment,
user_id int,
content text
}
Object Relational Mapping (ORM) tools: With the embedded post, you will be writing the code manually to add posts to a user, navigate through existing posts, validate them, delete them etc. With the separate table design, you can leverage the ActiveRecord (or whatever object relational system you are using) tools for this which should keep your code much simpler.
Flexibility: Imagine you want to add a date field to the post. You can do it with an embedded field, but you will have to write code to parse your array, validate the fields, update the existing embedded posts etc. With the separate table, this is much simpler. In addition, lets say you want to add an Editor to your system who approves all the posts. With the relational example this is easy. As an example to find all posts edited by 'Bob' with ActiveRecord, you would just need:
Editor.where(name: 'Bob').posts
For the embedded side, you would have to write code to walk through every user in the database, parse every one of their posts and look for 'Bob' in the editor field.
Performance: Imagine that you have 10,000 users with an average of 100 posts each. Now you want to find all posts done on a certain date. With the embedded field, you must loop through every record, parse the entire array of all posts, extract the dates and check agains the one you want. This will chew up both cpu and disk i/0. For the database, you can easily index the date field and pull out the exact records you need without parsing every post from every user.
Standards: Using a vendor specific data structure means that moving your application to another database could be a pain. Postgres appears to have a rich set of data types, but they are not the same as MySQL, Oracle, SQL Server etc. If you stick with standard data types, you will have a much easier time swapping backends.
These are the main issues I see off the top. I have made this mistake and paid the price for it, so unless there is a super-compelling reason do do otherwise, I would use the separate table.
what if users John and Ann have the same thingies? the records will be duplicated and if you decide to change the name of thingie you will have to change two or more records. If thingie is stored in the separate table you have to change only one record. FYI https://en.wikipedia.org/wiki/Database_normalization
Benefits of one to many:
Easier ORM (Object Relational Mapping) integration. You can use it either way, but you have to define your tables with native sql. Having distinct tables is easier and you can make use of auto-generated mappings.
Your space limitation of 10,000 rows will go further with the one to many relationship in the case that 2 or more people can have the same "thingies."
Handle users and thingies separately. In some cases, you might only care about people or thingies, not their relationship with each other. Some examples, updating a username or thingy description, getting a list of all thingies (or all users). Selecting from the single table can make it harding to work with.
Maintenance and manipulation is easier. In the case that a user or a thingy is updated (name change, email address update, etc), you only need to update 1 record in their table instead of writing update statements "where user_id=?".
Enforceable database constraints. What if a thingy is not owned by anyone? Is the user column now nillable? It would have to be in the single table case, so you could not enforce a simple "not nillable" username, for example.
There are a lot of reasons of course. If you are using a relational database, you should make use of the one to many by separating your objects (users and thingies) as separate tables. Considering your limitation on number of records and that the size of your dataset is small (under 10,000), you shouldn't feel the down side of normalized data.
The short truth is that there are benefits of both. You could, for example, get faster read times from the single table approach because you don't need complicated joins.
Here is a good reference with the pros/cons of both (normalized is the multiple table approach and denormalized is the single table approach).
http://www.ovaistariq.net/199/databases-normalization-or-denormalization-which-is-the-better-technique/
Besides the benefits other mentioned, there is also one thing about standards. If you are working on this app alone, then that's not a problem, but if someone else would want to change something, then the nightmare starts.
It may take this guy a lot of time to understand how it works alone. And modifing something like this will take even more time. This way, some simple improvement may be really time consuming. And at some point, you will be working with other people. So always code like the guy who works with your code at the end is the brutal psychopath who knows where you live.
I'm trying to create a one-to-one association between two models, called A and B. Model B can exist in either table Foo, if Foo exists, or table Bar, all other situations.
I tried using the :dataset flag of the one_to_one association method to get it to work, but can't seem to figure out how to make it work without introducing a circular dependency.
Is there a way to accomplish this with Sequel associations? Or is the best course of action to hand-write SQL?
I'm not sure I completely understand what you are attempting to do, but from your description, you should just need to make B's table dependent on whether table Foo exists:
class B < Sequel::Model(DB.table_exists?(:Foo) ? :Foo : :Bar); end
If you really wanted to do it just for the association and not for all of model B, you can modify the FROM table in the association block:
A.many_to_one(:b){|ds| ds.from(ds.db.table_exists?(:Foo) ? :Foo : :Bar)}
I haven't tested either of these, but they should work. If that's not what you want, you probably want to add more detail to your description.