As the title says, for example we have a library system with two tables Books and RentalRecord with a one to many relationship. It looks like
Book1
--name
--RentalRecords
----record11
----record12
Book2
--name
--RentalRecords
----record21
----record22
----record23
----record24
Each record contains 2 time stamps for date rented, and date returned
and I want to query for the Books that hasn't been rented out in the past 2 weeks.
I'm new to Ruby and couldn't think of any way to do this without using multiple queries.
Using two queries sometimes is a viable option. If you prefer constructing a single query, use something along the lines of
Book.joins(:rental_records)
.where("max(rental_records.created_at) < ?", 2.weeks.ago)
Things to note:
You need to join the required tables explicitly (but by association name)
You can use a plain string in a Rails where statement
You can bind a Ruby value to a question mark within such a string
If your queries get out of hand or you don't like using plain SQL strings like that, then take a look at the gem squeel. It provides an fully-fledged SQL algebra for Ruby on Rails.
HTH
Related
I have a Postgresql 11.8 table named posts where I would like to define a column slugs of type JSONB, which would contain arrays of strings such as ["my-first-post", "another-slug-for-my-first-post"].
I can find a post having a specific slug using the ? existence operator: SELECT * FROM posts WHERE slugs ? 'some-slug'.
Each post is expected to only have a handful of slugs but the amount of posts is expected to grow.
Considering the above query where some-slug could be any string:
How can I define an index to have a reasonably performant query (no full table scan)?
How can I ensure the same slug cannot appear multiple times (across and within the different arrays)?
I am primarily looking for a solution for Postgresql 11 but also would be interested to know solutions in future versions, if any.
The database is used in a Rails 6.0 app so I am also interested by the Rails migration syntax.
You can support the ? operator with a normal GIN index on the jsonb column.
However, you cannot enforce uniqueness of array values.
Particularly if you want to have database constraints, you should not model your data using JSON. Use a regular normalized data model with several tables and foreign keys, then it will be easy to implement such an uniqueness constraint.
I need get union of two ActiveRecord::Relation objects in such a way that the resultant should be another active record relation. How can I accomplish this?
Update for Rails 5
ActiveRecord now brings built-in support for UNION/OR queries! Now you can (the following examples are taken, as-is, from this nice post. Make sure you read the full post for more tricks and limitations):
Post.where(id: 1).or(Post.where(title: 'Learn Rails'))
or combine with having:
posts.having('id > 3').or(posts.having('title like "Hi%"'))
or even mix with scopes:
Post.contains_blog_keyword.or(Post.where('id > 3'))
Original answer follows
I do not think that AR provides a union method. You can either execute raw SQL and use SQL's UNION or perform the 2 different queries and union the results in Rails.
Alternatively you could take a look in these custom "hacks":
ActiveRecord Query Union or
https://coderwall.com/p/9hohaa
I am attempting to build a "pull some data out and display it" app. To pull the data out, we have a great deal of site-specific oracle sql (stored procedures, weird expressions and whatnot).
One way of doing what I need to do is to create oracle views and then to point activerecord at those views. But I would rather have all the SQL in one place - the ruby app itself - rather than in oracle and ruby. I'd like to make an active record object over a raw SQL select clause, and then be able to use all the usual activerecord stuff with this.
Now - I know Oracle SQL supports this. Let's take:
select n.name_id, name, munge(n.name) as munged from name
Normally you'd create a view munged_name, a class MungedName, with the result that
MungedName.like(:name , 'Foo%')
would (eventually) generate SQL that looks like this:
select T1.name_id, T1.name, T1.munged
from munged_name as T1
where T1.name like 'Foo%'
What I would like to do, however, is have ActiveRecord use the sql select as a subquery in the FROM clause:
select T1.name_id, T1.name, T1.munged
from (select n.name_id, name, munge(n.name) as munged from name) as T1
where T1.name like 'Foo%'
Just jam it in there literally - Oracle will optimise it perfectly ok. This way, all the weird queries that my people want to do are in the one spot - in the model class definition.
Will ActiveRecord do this? I'll keep looking - see if I can find out before I get an answer here :) .
I suspect that passing that SQL fragment should work.
MungedName.from("select n.name_id, name, munge(n.name) as munged from name").like(:name , 'Foo%')
But you're probably looking for something even more Ruby-like, so you can of course generate that from fragment in Arel too:
sql = Name.select([:name_id, :name, "munge(name)"]).to_sql
MungedName.from(sql).like(:name , 'Foo%')
I did not try any of this from the command line. I have doubts that what I entered will work cleanly, but the idea is basically the same. Arel can be used to generate complex SQL, and have that SQL be fed into another Arel query.
I don't know for certain about the from part, but I suspect that will work as expected. If you check out the method source:
http://www.ruby-doc.org/gems/docs/a/arel-3.0.2/Arel/SelectManager.html#method-i-from
def from table
table = Nodes::SqlLiteral.new(table) if String === table
...
end
The SqlLiteral class is just a way to bypass any quoting or whatnot.
Looking at the code - it won't work. There is no self.from= method. When you jam raw sql in the table_name, it quotes that and treats it as though it were a table name. Which is fair enough. The activerecord ruby has a thing 'quoted_table_name' all the way through it.
There is, however, an intriguing comment on associations.rb:
# == Table Aliasing
#
# Active Record uses table aliasing in the case that a table is referenced multiple times
# in a join. If a table is referenced only once, the standard table name is used. The
# second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>.
# Indexes are appended for any more successive uses of the table name.
If activerecord could be persuaded to always alias the MungedName model (even when there was no join), and to use some unquoted thingy as the content of the from, that might work. I'll have to hack up the library, though.
I've got a huge monster of a database (Okay that's not quite true, but there are over 8 million records in one product table)..
This table is fed by 13 suppliers.
Even with the best indexing I could come up with, searching for the top 10,000 records that are ready for supplier 8, is crazy slow.
What I'd like to do is create a product table for each supplier and parse the table into smaller tables.
Now in c++ or what have you, I'd just switch the table that I'm working with inside the class.
In ruby, it seems I'll have to create a new class for each table, and do a migration.
Also as I plan to have some in session tables #, I'd be interested in getting ruby to work with them..
Oh.. 8 million and set to grow to 20 million in the next 6 months.
A question posed, was what's my db engine.. Right now it's sql, but I'm open to pushing my db to another engine, if it will mean I can use temp tables, and "partitioned" tables.
One additional point to indexing.. Indexing on fields that change frequently isn't practical. Like price and quantity.. I'd have to re-index the changed items, each time I made a change.
By Ruby, I am assuming you mean that inheriting from the ActiveRecord::Base class in a Ruby on Rails application. By convention, you are correct in that each class is meant to represent a separate table.
You can easily execute arbitrary SQL using the "ActiveRecord::Base.connection.execute" method, and passing a string that is your SQL query. This would bypass having to create separate Ruby classes that would represent transient tables. This is not the "Rails approach", however it does address your question of allowing switching of the tables inside a class file.
More information on ActiveRecord database statements can be found here: http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html
However, as other people have pointed out, you should be able to optimize your query such that splitting across multiple tables is not necessary. You may want to analyze your SQL query's execution plan using various tools to optimize the execution. If you are using MySQL view check out their query execution planning functionality: http://dev.mysql.com/doc/refman/5.5/en/execution-plan-information.html
By introducing indexes, or changing join methods between tables, etc you should be able to return reduce your query execution time.
I was curious about how the MongoDB plugin for Grails would handle relationships. In order to test this I made a very simple application with two domain classes:
Authors have two fields: String firstName and String lastName
Books have two fields: String title and Author author
After setting up MongoDB and Grails I made some Authors and Books and took a peek using the MongoDB interactive shell. What I found is that the relationships were being handled the same way they would be handled in a relational database: references to other objects' id fields.
So now for the questions:
In order for GORM to pull this off, does it need a separate connection to retrieve each document?
If yes, wouldn't this be better off in a relational database such as PostgreSQL or MySQL?
If the answer to the above two questions is indeed 'yes,' then is there a better way to manage relationships in a document database such as MongoDB? I realize MongoDB isn't supposed to be relational, but there are some things that I don't see how to get around relationships without duplicating data (thereby making update nightmares).
Edit: I also just noticed that grails is not sorting properly on the 'id' property of my authors. Does this have to do with using MongoDB? In the shell I can see that the _id property of all the documents made by Grails is of the datatype NumberLong.
I realize MongoDB isn't supposed to be relational, but there are some things that I don't see how to get around relationships without duplicating data
Then don't sweat it. MongoDB is not anti-relational, it's document-oriented.
In this case, Books and Authors are two top-level objects. It's not reasonable to nest either of them, they are both core entities in their own right.
In the case of each Book having only one Author (N:1), it's completely reasonable for the Book to contain a "Reference To" the Author. Sure you'll have to do two queries. But is that terribly different from doing a join query? The join query still has to do two index look-ups and two data lookups. So you're not really costing yourself anything here.
In the case of each Book supporting multiple Authors (M:N), then you have several options based on your needs.
I don't like to think of MongoDB as "not relational", I think it's cleaner to think of MongoDB as query-optimized.
I also just noticed that grails is not sorting properly on the 'id' property of my authors...
I would check directly with the Grails author. Sounds like they may be storing "strings" instead of actual ObjectIds (or MongoIDs). While not critical this may be a bug.
In regards to the id property, the documentation now shows that you can put a declaration of ObjectId id or String id in your domain class in order to not use the default GORM implementation of using an iterating long. Simply declare the field in your class, and the plugin will take care of the rest.