Mongo return in one query - ruby-on-rails

I have the following n to n (done with mongoid gem) with two collections books and publishers:
{
name: "O'Reilly Media",
founded: 1980,
location: "CA",
books: [123456789, 234567890, ...]
}
{
_id: 123456789,
title: "MongoDB: The Definitive Guide",
author: [ "Kristina Chodorow", "Mike Dirolf" ],
published_date: ISODate("2010-09-24"),
pages: 216,
language: "English"
}
{
_id: 234567890,
title: "50 Tips and Tricks for MongoDB Developer",
author: "Kristina Chodorow",
published_date: ISODate("2011-05-06"),
pages: 68,
language: "English"
}
I need to return in one query the publishers, but separated in documents, like this:
{
name: "O'Reilly Media",
founded: 1980,
location: "CA",
book: 123456789 # or books:[123456789]
}
{
name: "O'Reilly Media",
founded: 1980,
location: "CA",
book: 234567890 # or books:[123456789
}
I want to this inside mongo in a query, actually I do it in the rabl file modifying the collection, but this is not good por gaination and using in other representations, So I want to do this transformation in Mongo, not in ruby, Or maybe I should change the query calling for books instead on publishers.
This is the code in ruby:
#publishers is a mongoid::Criteria
#publishers = #publishers.collect do |s|
s.books.count > 1 ? s.publisher_separate_by_books : s
end.flatten
class Publisher
has_and_belongs_to_many :books, inverse_of: :books, dependent: :nullify
def publisher_separate_by_books
books.map {|i| Publisher.new({name: name, founded: founded, location: location, books: [i]})}
end
end
How can achieve this in mongo query

There is no advantage to expanding the query result like that in the database server (any database server). If you wanted to perform additional operations per book in the query (which in case of MongoDB would involve the aggregation pipeline, and for relational databases JOIN operations) then it would make sense. But simply expanding a field like that in the database is wasteful.
MongoDB does support this operation via unwind (https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/#pipe._S_unwind), but then you lose the ability to query the models using DSL provided by Mongoid and have to construct the aggregation pipelines instead.

Related

How to return a response from rails api with ActiveRecord relations 2 levels deep?

I am creating a Rails api for teachers to rank students based on certain criteria. I have four models: classroom, student, criterion and rank.
Students/Criteria are many to many through Rank
Students/Classroom are many to many
Rank is a join table between Student/Criteria with the additional field of rank, which is an integer between 1-4.
I am able to return the list of Students belonging to a Classroom in a response (1 relation deep) by allowing Classroom.students through in my classroom serializer. How can I return each student's ranks nested within students in my Classroom response (2 relations deep) from my API? Ideal response as below:
Classroom_A:
{
id: "123",
name: "classroom A",
students: [
{ id: "456"
name: Juanita,
gender: female,
ranks: [
{ id: "789",
student_id: "456",
name: "willingness to help others",
rank: "4"
},
{ id: "101",
student_id: "456",
name: "Leadership",
rank: "3"
} ...
]
},
{ id: "232"
name: Billy,
gender: male,
ranks: [
{ id: "789",
student_id: "232",
name: "willingness to help others",
rank: "3"
},
{ id: "101",
student_id: "232",
name: "Leadership",
rank: "3"
} ...
]
}
]
}
Thanks in advance.
A similar question was posted at Rails: Serializing deeply nested associations with active_model_serializers (thanks for the link #lam Phan)
However, the most upvoted answer for that post was not super clear and there was no code example. Super dissapointing. I also looked into the JsonApi adapter and was not able to get the solution I was looking for. In the end I ended up taking the same route as the OP for the linked question: in the serializer I wrote my own function and manually sideloaded the other data that I wanted. I was hoping to do this the "Rails way" but in the end I chose to just get it done.
class ClassroomSerializer < ApplicationSerializer
attributes :id, :name, :school_name, :students
def students
customized_students = []
object.students.each do |student|
custom_student = student.attributes
custom_student['ranks'] = student.student_ranks
customized_students.push(custom_student)
end
customized_students
end
end

Rails Sort/Group by dynamic field

I'm working on a rails app that is passing serialized values in a JSON dump to the client. I need the requirements to be sorted in a specific order, with objects that contain equal "deadline_dates" to be grouped together. That field is dynamic and many of my objects don't contain that field at all.
Here is what my model looks like:
#------ app/models/program_requirement.rb ------#
class ProgramRequirement < ActiveRecord::Base
include HasFields
belongs_to :program
has_many :responses
has_many :applications, through: :responses
store_accessor :fields, :deadline_date
end
Here is the method I'm using to pass the objects into my serializer:
#------ app/models/program.rb ------#
def sorted_program_requirements
self.program_requirements.sort_by{|a| [a.deadline, a.position]}.map {
|requirement| ProgramRequirementSerializer.new(requirement, root: false) }
end
The sorting for the "deadline" and "position" values works fine, but some reason, I'm unable to sort by including a param into my sort_by method such as:
a.fields[:deadline_date]
I've attempted using group_by, but that also doesn't seem to do what I'd expect. I just want to group requirements by equal deadline_dates if they exist, and then sort the rest by the other two static fields.
Any help is appreciated!
UPDATE
Output from: program_requirements.each { |pr| p pr.fields[:deadline_date] }
[#<ProgramRequirement:0x007fb198a12d58
id: 126,
program_id: 1159,
title: "Req",
deadline: "manual",
position: 1,
fields: {"deadline_date"=>"2015-09-16"}
#<ProgramRequirement:0x007fb198a12bf0
id: 127,
program_id: 1159,
title: "Req",
deadline: "initial",
position: 2,
fields: {}
#<ProgramRequirement:0x007fb198a12a60
id: 132,
program_id: 1159,
title: "Req",
deadline: "precampaign",
position: 3,
fields: {}
#<ProgramRequirement:0x007fb198a128d0
id: 133,
program_id: 1159,
title: "t444",
description: nil,
deadline: "manual",
position: 4,
fields: {"deadline_date"=>"2015-09-16"}
]
Ok, so if you just need to sort by the deadline_date, well, sort by it:
program_requirements.sort_by { |a| a.deadline_date.to_s }
Since not every program_requirement contains this field, you'll need to convert all nils to strings, so they can be compared to each other. This way all program_requirements that don't contain deadline_dates will be grouped at the top, and everybody else will be sorted on the way to bottom.
Sorting in ruby is not a good idea. Doing it in Sql it much faster:
program_requirements.order(deadline_date: :asc) or :desc

What's the difference between using has_many and using an array in ruby on rails? (Mongodb)

I want to implement a few simple models to my ruby on rails project. I have the model hierarchy, Topic and Project. A Topic will contain many Projects. I wanted to know what the difference was between adding a "has_many projects" relation in the Topic model vs just adding an array field in the Topic model that stores all of the projects within it.
I am using Ruby on Rails with mongodb as my database and mongoid as the object document mapper.
Let's say in you're Topic model:
has_many :projects
Now, if you query like, #project.topic will execute a single query.
Let's say, you add a column in topic which stores project_ids, e.g.: [1,2,3]
Now if you want to find any individual #topic projects you need to do like this:
first, fetch the #topic.project_ids
Then, for each id query to the Project table, if array contains 3 values then 3 query will run.
So, all is about execution time.
Array Store
you can use to take reference ids of child association. eg
users:
[
{
_id: ObjectId('oii3213ui23uo')
name: 'John',
email: 'jhn#example.com',
project_ids: ['oii3213ui23u1', 'oii3213ui23uo2' ,'oii3213ui23uo3']
}
]
projects:
[
{
_id: ObjectId('oii3213ui23u1'),
title: 'Project 1'
},
{
_id: ObjectId('oii3213ui23u2'),
title: 'Project 2'
},
{
_id: ObjectId('oii3213ui23u3'),
title: 'Project 3'
}
]
has_many
You can use to embed child associations in parent document. eg
users:
[
{
_id: ObjectId('oii3213ui23uo')
name: 'John',
email: 'jhn#example.com',
projects: [
{
title: 'Project 1'
},
{
title: 'Project 2'
},
{
title: 'Project 3'
}
]
}
]

Searchkick without stemming

I am indexing some of my data with searchkick (https://github.com/ankane/searchkick) as an array and it works almost fine :)
def search_data
{isbn: isbn,
title: title,
abstract_long: abstract_long,
authors: authors.map(&:name)}
end
The field I'm interested in is authors.
What I'd like to accomplish is to search for "Marias" and find all the authors that actually have that exact string in their surname like (Javier Marias) and not all the Maria/Mario/Marais that Searchkick returns, and have them with a much bigger priority.
This is how I search right now
Book.search(#search_key,
fields: [:authors, :title, {isbn: :exact}],
boost_by: {authors: 10, title: 5, isbn: 1})
Is this possible at all? TIA
In Elasticsearch it has a regular match to deal with this case, but abandoned by Searchkick.
You could choose a walk around way for this case:
def search_data
{
isbn: isbn,
title: title,
abstract_long: abstract_long,
author_ids: authors.map(&:id)
}
end
For search:
author_ids = Author.where(surname: 'Marias').map(&:id)
Book.search(
#search_key,
where: {author_ids: {in: author_ids}},
fields: [:title, {isbn: :exact}],
boost_by: {title: 5, isbn: 1}
)

How to create has_and_belongs_to_many relationship with Ember.js & ember-data?

Is it possible to create a hasAndBelongsToMany relationship with Ember.js & ember-data?
Edit: Added ActiveRecord model examples to clarify.
class Project < ActiveRecord::Base
has_and_belongs_to_many :tags
end
class Tag < ActiveRecord::Base
has_and_belongs_to_many :projects
end
I have an associative table "projects_tags" linking project_id <=> tag_id.
In this example, I'd suggest embedding the tags in the Post, so that the resulting structure looks like this
{
title: "some post",
tags: [ "tag1", "tag2" ]
}
This is generally good practice when you don't need to have additional value on the relation. If you want to have the Tag model on the client, you can still do it via embedding.
{
title: "some post",
tags: [ { name: "first tag" }, { name: "second tag" } ]
}
in this case you could specify it as an embedded relation, such as this
App.Post = DS.Model.extend({
title: DS.attr("string"),
tags: DS.hasMany("App.Tag")
});
App.Tag = DS.Model.extend({
name: DS.attr("string"),
post: DS.belongsTo("App.Post")
});
App.store.adapter.serializer.map("App.Tag", {
tags: { embedded: 'load' }
});
keep in mind that embedding only works in Ember Data revision 10. It is not supported in 5-9.
Most of the time you don't really want to model everything the same way as you would model it in a relation database.
You should lean towards NoSQL-ish JSON, which generally means embedding things, rather than making complex associations. The reason for this is that your database can JOIN data very efficiently, especially with indexes, but doing this over the network is a bad idea because it results in a lot of requests.

Resources