Search Kick Rails 5 - ruby-on-rails

Quick one.
I am using Searchkick gem for searching in my Rails app. I notice if I try search some text in the Post model it works fine.
Everything is set up correctly however, say I wish to search for author of the Post. It's example relation is Post.user.name
When I search "users name" I get no results. I suspect it is to do with the data being in the User model and not the Post model but in my Post views I can see this as a string.
Is this something simple I am missing? I thought addding
searchkick inheritance: true
to my Post model and reindexing the Post and User model then it would work but nothings changed.
Any ideas? Thanks.

Searchkick is backed by ElasticSearch. All of the searching happens there - not your database. You need to get the data you want to search on in to ElasticSearch somehow. Searchkick provides this via indexing your data with the search_data method (https://github.com/ankane/searchkick#indexing). So if you had an association that had a field you wanted to search on, you can follow something similar to the example set forth on Searchkick's Github page, and do (for your case):
class Post
belongs_to :author, class_name: 'User' # or whatever your association is
def search_data
{
title: title,
author_name: author.name
}
end
end
Don't forget to reindex afterwards, so you can see the results.
Post.reindex
And don't forget that if your association's data changes, like if your Author's name goes from Bob to Robert, the data is not automatically synced. You need to manually call Post.reindex, or set up an after_commit hook on Author that calls out to reindex Post when an author changes. See the docs for more information about that.

Related

Update deeply nested model upon save

I'm attempting to update a deeply nested model from a model that is itself nested using cocoon. (Don't worry, I'll clarify)
I have three Models: Stock Stockholder and Folder. Stock has_many :stockholders, which are all nested via cocoon in a form_for #stock. Stockholder has_one :folder.
Within my stock form, I have a table that lists out stockholders where I can add new ones (to reiterate, via cocoon). To create a Folder for each new Stockholder, I have a before_create filter in my Stockholder model that creates a new Folder for each new Stockholder (see below)
before_create :build_default_folder
def build_default_folder
logger.debug "Inside build_default_folder"
build_folder(name: "#{self.holder_index}. #{self.holder_name}", company_id: self.warrant.company.id, parent_id: self.warrant.company.folders.find_by_name("#{self.warrant.security_series} #{self.warrant.security_class} Warrant").id)
true
end
All of that works very well.
The problem
Is that I would like to add a method that updates the Folder attributes according to any changes in the stockholder information (say they change the name or something). To this end, I've attempted to add the following.
before_save :update_default_folder
def update_default_folder
logger.debug "Inside update_default_folder"
self.folder.update_attributes(name: "#{self.holder_index}. #{self.holder_name}", company_id: self.warrant.company.id, parent_id: self.warrant.company.folders.find_by_name("#{self.warrant.security_series} #{self.warrant.security_class} Warrant").id)
true
end
This however doesn't work and (particularly puzzling to me) is that before_save doesn't even seem to be firing. My best guess would be that this is because stockholder is itself nested using cocoon (but I could be completely wrong about this).
Anyway, how might I achieve the desired functionality? Thanks for any suggestions.

Mongoid: finding documents that are referencing another one (referenced 1-N)

I have a MongoDB database that uses mongoid referencing according to the documentation. Here are my models:
class Mydoc
include Mongoid::Document
# ...
has_and_belongs_to_many :editors, class_name: 'User', inverse_of: nil
# ...
end
and
class User
# actually, it's based on devise with some changes
# ...
# it does not reference back to Mydoc, see inverse_of there!
end
Now if I make a reference to a user (to grant her/him editor role), mongoid creates an array field called editor_ids that contains object ids of the documents in the users table. Works nice.
I'd like to have a query that shows all Mydoc documents where a certain user is editor. Can't figure out how to do that in mongoid. It's very simple in the mongo console:
db.mydocs.find({ editor_ids: { $in: [ ObjectId("52c97e58b878bcf156000001") ] } })
It works like charm. But I need this in rails, not in the console. Based on mongoid docs, I've tried this (and a hell lot of variations of it) but it does not work:
#docs_where_editor = Mydoc.where(:editors.in => [#user._id])
the result is an empty dataset. I would appreciate any hint.
I suppose it's not the where method, since in mongoid, find only accepts object ids you want to find.
If you have an :editor_ids array and you're only looking for a single value then you should be able to use multi-keys thusly:
Mydoc.where(:editor_ids => #user.id)
and if you have several editor IDs to search for then:
Mydoc.where(:editors_ids.in => array_of_user_ids)
would be the transliteration of your MongoDB console query to Mongoid.

Rails and HAML - how to reference deeply nested array items individually

I'm working on someone else's site, and am trying to reference a value's ID in my HAML file, but I've tried every iteration I can think of and can't figure out how to do this. I'm trying to get the ID of a want, which belongs to a project, which belongs to a user. Here's some code from the projects model:
class Project
belongs_to :user
has_many :wants
accepts_nested_attributes_for :wants
In another view, someone's written this:
(id="project_#{ project.id }_wants")= render project.wants, :user => project.user
This returns an array of wants, which it iterates through. I was initially confused because the wants seem to have ids in some of the views, but when I tried referencing project.want[0], for example, that worked perfectly. My question is - how do I pass user(id of blah).project(id of blah).want[place blah in array] ?
Assuming you know the user ID and project ID (we'll call them user_id and project_id), you can:
User.find(user_id).projects.find(project_id).wants[index of the want you want]
or
User.find(user_id).projects.find(project_id).wants.find(wants_id)

Mongoid + Cucumber

I try to run a scenario with Cucumber, through Capybara, in a Rails 3.2.3 app supported by Mongoid. The aim is to have current user add a book to his collection.
Everything goes ok, but the final step definition, where I check that the amount of books is now one, fails.
But if I check on the app controller, the size actually increased. And actually, when I send reload to the user in the step definition, it passes:
user.reload.books(true).size.should == 1
I'm afraid this behavior could harm my app once in production. Any advice how to make sure all tests and app behaviors are consistent?
UPDATE
I checked the test.log to see what's going on.
Calling reload I get this query to MongoDB:
find({"count"=>"books",
"query"=>{:_id=>{"$in"=>[BSON::ObjectId('4f889b473dffd63235000004')]}},
"fields"=>nil}).limit(-1)
while without the reload I get this:
find({"count"=>"books", "query"=>{:_id=>{"$in"=>[]}}, "fields"=>nil}).limit(-1)
It practically doesn't query against the user if I don't reload the model, which doesn't make much sense to me.
The following works for me (updated with actual Cucumber example)
I built a Rails project to test out your issue, rails 3.2.3, mongoid 2.4.8, mongo 1.6.2, mongodb 2.0.4, cucumber 1.1.9.
The following (association generated methods) work as expected, without need for refresh:
user.books << book
book.users << user
Then I tried to bypass the association, which was what I thought that you were doing.
user.push(:book_ids, book.id)
book.push(:user_ids, user.id)
These DO bypass the association, resulting in incomplete (one-way instead of two-way) references, but the memory and db state is consistent. So my guess in my previous answer about what you were experiencing was wrong, there's no refresh needed and you are probably doing something else. Note that you/we do not want the incomplete references, please do not push directly to the internals for Mongoid referenced relations.
Are you using the association append "<<" for adding a user or a book? My current conclusion is that Mongoid referenced relations work as advertized for my test of your issue. There's no need for refresh.
Here's the model:
class User
include Mongoid::Document
field :first_name, type: String
field :last_name, type: String
has_and_belongs_to_many :books
end
class Book
include Mongoid::Document
field :title, type: String
field :author, type: String
has_and_belongs_to_many :users
end
Cucumber feature
Feature: UserAndBook
Test adding a book to a user_s books
Scenario: add_book_to_user
Given starting with no users and no books
And a new user
And that the new user has no books
And a new book
And add book to user
Then I can check that the user has a book
Cucumber steps
require 'test/unit/assertions'
require File.expand_path('../../../test/test_helper', __FILE__)
World(Test::Unit::Assertions)
Given 'starting with no users and no books' do
User.delete_all
Book.delete_all
assert_equal(0, User.count)
assert_equal(0, Book.count)
end
Given 'a new user' do
#user = User.create(first_name: 'Gary', last_name: 'Murakami')
end
Given 'that the new user has no books' do
assert_equal(0, #user.books.count)
end
Given 'a new book' do
#book = Book.create(title: 'A Tale of Two Cities', author: 'Charles Dickens')
end
Given 'add book to user' do
#user.books << #book
end
Then 'I can check that the user has a book' do
assert_equal(1, #user.books.count)
end
I'm open to further exchange of info to help to address your issue.
Blessings,
-Gary
P.S. Looking at the log, it is interesting to see that user.books.length does an actual db "find count $in" query rather than a local array length.
Previous answer
You've pretty much answered your own question. In Rails, you need to use the reload method whenever data for your model has changed in the database, otherwise you will just be looking at the previously loaded/instantiated/cached state of your model. With update of just an attribute, things look pretty consistent, but associations are more complicated and the inconsistency becomes more obvious.

Calling a method on an attribute in rails

I am working on a tutorial and I wanted to really learn by deviating from the tutorial a bit and am encountering some problems. I am creating a showtime app that lists all the movies, times, genres, etc.
I have a Movie model with the attributes name, genre, showtime, showdate, release year, and title. In my movie.rb file, I added an attr_accessor :genre so I can use the setter and getter methods, but when I go into rails console and create a movie with all the attributes, the :genre keeps coming up as "nil" but when I call the movie.genre, it comes up as the correct listed genre. I am not sure why that is?
Also, since genre is not a class, are there any methods I can call on it to list all the genres in the database?
Thank you!
Is genre a column in your movies table? If so, why use attr_accessor?
For the second part of your question, you could write a class method in movie.rb to list all genres. Something like:
def self.genres
Movie.all.map(&:genre).uniq
end

Resources