Query with filtred fields for nested objected - ruby-on-rails

lets say I've got a document
book: {name: "book",
"chapters":[{title: "Chapter I",
"sections":[{},{}]
}, {...}
]}
I what to get book object with embedded object chapters but each chapter should not contain nested "sections" (but should contain other attributes, like title):
book: {name: "Book",
"chapters":[{title: "Chapter I"
}, {...}
]}
How should I make a query using Mongo driver and Mongoid (or Mongomapper)?
I tried it with mongoid:
books.all[0].chapters.only(:title)[0].sections # it still works, though I expect sections to be nil

your query
books.all[0].chapters.only(:title)[0].sections
picks the books with all its chapter embedded documents on books.all, so there is no meaning to use 'only' after chapters. Your query can work if the chapters as a separate documents and got a has_many relationship instead embeds_many
So you have to use 'only' like this on books document
books.only('chapters.title').all[0].chapters.sections

Related

How to return data from distinct tables into a JOIN relationship in ruby?

I have a relationship between Actor and Books. A Actor have n Books.
A Actor has a name.
A Book has a title.
A Actor called Fernando wrote 3 books called [ Book 1, Book 2, Book 3]
I want to make a query that returns:
[
{actor.name, book.title,
{actor.name, book.title,...
]
Im my example it will returns
[
{Fernando, Book 1},
{Fernando, Book 2},
{Fernando, Book 3}
]
If I do a
Actor.joins(:books)
It will return only data from Actor.
How can I make a joins in ruby returning data from the 2 tables?
try this...
Actor.joins(:books).select("actors.name, books.title")
Solution:
Actor.joins(:books).select(:name, :title)

How to create one to many relationship into Google Cloud Datastore in Ruby

May I know how to create one to many relationship into Google Cloud Datastore in Ruby and Ruby on Rails?
Now I'm writing a code like this.
class Author
attr_accessor :name, :books
end
class Book
attr_accessor :title, :description, :author_id
end
> #author.books
[2, 3, 5]
> Book.find(#author.books.first)
<Book: ....
I'm putting some Book ids into Author's books.
But I feel there is more sophisticated way.
I'd like to implement the code using 'belongs_to' and 'has_many' into Google Cloud Datastore. Is it possible?
You can declare book as a child of an author:
book_key = datastore.key [["Author", "Someone"], ["Book", "sampleBook"]]
See more at Entities, Properties, and Keys >> Ancestor Paths documentation.
Think VERY CAREFULLY before using this structure. If a book happens to have multiple authors, you'll need to replicate books many times for each author. In Datastore, each child can have only one parent. There's not an official many-to-many relationship in Datastore, although you can mimic it in your code and data structure, similarly to how you're already doing (with a custom authorid property in Book entity).
I concur with Renato. You essentially have a many-to-many scenario. An Author can write several Books and conversely Books can have multiple Authors.
Unfortunately there is no explicit mechanism for many-to-many relationships in Google Datastore. But you can still structure your entities to model the relationship.
For example, instead of using a hierarchical model via ancestor paths, add an array to your Book entity that store keys that reference Author entities which might look something like this:
book_key = datastore.key "Book"
author_key1 = datastore.key "Author", "john"
author_key2 = datastore.key "Author", "jane"
author_entity1 = datastore.entity author_key1 do |t|
t["name"] = "Doe, John"
end
author_entity2 = datastore.entity author_key2 do |t|
t["name"] = "Doe, Jane"
end
datastore.save(author_entity1, author_entity2)
And the array property on the Book entity would look something like this:
book_entity = datastore.entity book_key do |t|
t["authors"] = [author_key1, author_key2]
end
If you want to know all the books associated with author John Doe in your database all you need do is query the "authors" array property:
query = Google::Cloud::Datastore::Query.new
query.kind("Book").
where("authors", "=", datastore.key("Author","john"))
books_by_john = datastore.run query
Notice that I could have also gone the other way and created an array of Book keys on Author entities. But had I gone that route and tried to model a prolific Author like say Stephen King...well that Author entity would end up being pretty bloated with key references to Book entities, so you have to be selective as to which side of the relationship you add the array and try to go for the entities less likely to have bloat.

testing queries using rspec and factory_girl : Is there a better way?

I have the following classes and relationships
City has_many Cinemas
Cinemas has_many Movies
Movies has_many Ratings
Movies Has_many Genres through GenreMovie
and I want to test queries like
* Show me the all movies in NewYork
* Show me the all movies in NewYork order by the rating
* Show me the all movies in NewYork order by length_of_movie, in genre "Action"
* show me all movies in Cinema "X" order by rating, that are in Genre "SciFi"
Currently the way I am doing as below, using factory girl, and chaining a bunch of models together to have data to check against,
city = create(:city)
cinema = create(:cinema, city: city)
5.times do
movie = create(:movie, cinema: cinema, tags: ["sci fi", "action"]
3.times do
create(:rating, score: 2.3, movie: movie)
end
end
and repeating that 3-4 to generate enough data to query against but it seems so clunky.
Is there a better way ?
I normally test this using a very "minimalistic" approach:
e.g. for your first case I would create two movies, one in NY, and one outside. Your method should only return the one in NY
For the second, create three movies, both in NY, with different rating. Create them in a not logic way, so that, no matter what, they will be sorted. Check whether your method returns them in the right order
Similar for the other cases.
I would not just create 5x3 movies. Makes no sense, and only costs time...
There are several factory_girl constructs you could use to clean these up. create_list will create an array of objects, cleaning up your x.times do blocks. A with_ratings trait on your movie factory could allow you to opt in to having ratings automatically created when you create a movie (via FactoryGirl callbacks). You could even have that use a transient attribute in order to control the number and rating. So your result could look something like this:
cinema = create(:cinema)
movies = create_list(
:movie,
5,
:with_ratings,
cinema: cinema,
tags: [...],
ratings_count: 3,
ratings_value: 2.3
)
If you need access to the city, you can get it via cinema.city.
See:
transient attributes
traits

Mongoid assigning has_many embedded document array

I have a mongoid document (School) that has_many embedded documents (Students). Is there a way to do a direct assignment of a set of students, instead of doing a database call for each individual Student:
school.students << student1
school.students << student2
(many database calls)
vs.
school.students = [ student1, student2 ]
The problem is the second example seems to keep failing with an "Invalid Students" error.
It looks like this is fixed in the latest versions of mongoid gems. A full gem update solved this.

Complex Active Record Association

User
first_name: "Johnny"
age: 15
Car
name: "Mazda"
Car
name: "BMW"
I would like results that look like
[ {"first_name": "Johnny", "age": 15, cars: [ { "name": "Mazda" }, { "name" : "BMW" } ] } ]
Is it possible to do this in Rails? So far I can get the Users car by typing johnny.cars but I'd like the data structure to also hold the values of the parent.
I was toying with the idea of using named_scopes and transient attributes but no luck so far any ideas/help is greatly appreciated.
You Could actually retrieve all the records from the other table using :includes statement
includes is magically syntax where in you can eager load all the assocaited records of a record.
Example In your case
User has_many cars
so If you do
#users = User.includes(:cars)
Now this would load all the user records and will eagerload all the cars records for each user objects
so if you loop through a user object and do
<% #users.each do |user| %>
<%= user.cars %>
<%end%>
user.cars wont fire a seperate query to fetch the all cars for the users instead it will fetch those record eager loaded using the includes clause
This technique is widely used to avoid N+1 query problem
Google for N+1 to know more on it
Hope this Help

Resources