Reputation: 11
The app is using typeahead and bootstrap 3 for, autocomplete function. but now we have so many records that , it is getting really slower.
var substringMatcher = function(strs) {
return function findMatches(q, cb) {
var matches, substrRegex;
// an array that will be populated with substring matches
matches = [];
// regex used to determine if a string contains the substring `q`
substrRegex = new RegExp(q, 'i');
// iterate through the pool of strings and for any string that
// contains the substring `q`, add it to the `matches` array
$.each(strs, function(i, str) {
if (substrRegex.test(str)) {
// the typeahead jQuery plugin expects suggestions to a
// JavaScript object, refer to typeahead docs for more info
matches.push({ value: str });
}
});
cb(matches);
};
};
$('.typeahead').each(function(elem) {
$(elem).typeahead({
hint: false,
highlight: true,
minLength: 3
}, {
displayKey: 'value',
source: substringMatcher($(elem).data('source'))
});
});
these are javasxript parts of typeahead fuction used by the app
on the view side of the form
<div class="form-group">
<label class="col-sm-2 control-label" for="typeahead_book">Title</label>
<div class="col-sm-3">
<input value="<%[email protected](:title)%>" name="option[book_title]" type="text" class="form-control typeahead typeahead-remote-book" id="typeahead_book" data-provide="typeahead" data-items="10" data-source='<%=@book_titles%>'>
<p class="help-block"><small><%= $autocomplete_message %></small></p>
</div>
i use a callback as
before_filter :get_autocomplete_lists, only: [:new, :edit]
def get_autocomplete_lists
@book_titles = Rails.cache.fetch("booktitles", :force => true, expires_in: 10.minutes) do
Book.scoped.map(&:title)
end
@publisher_names = Rails.cache.fetch("publishernames", :force => true, expires_in: 10.minutes) do
Publisher.all.map(&:name)
end
@users_by_email_name = Rails.cache.fetch("users", :force => true, expires_in: 7.days) do
User.scoped.map{|user| "#{user.email} - #{user.name}"}
end
@country_names = Rails.cache.fetch("countrynames", :force => true, expires_in: 7.days) do
Country.all.map(&:first).sort
end
# @languages = LanguageList::COMMON_LANGUAGES.map(&:name)
# @country_names = Anatolialit::Country::List.sort
#
@languages = Rails.cache.fetch("languages", :force => true, expires_in: 7.days) do
Anatolialit::Language::EnList.sort
end
end
on controller that generates source of the book , as @book_titles , THE APP IS WORKING NOW but i have to refactor the code for performance issues.Because after 10k record @book_titles is very big.
THAN WHAT I DID i created a new controller action on application controller
def book_titles
searchterm = (params[:title])
books =Book.any_of({ :title => /.*#{searchterm}.*/ })
respond_to do |format|
format.json { render json: books.map(&:title) }
end
end
and i defined a route to use as application controller as an api service
get 'booktitles/:title' => 'application#book_titles', format: :json
now when i use a localhost:3000/booktitles/the it brings me as a json data all the books that indicates 'the' in the title. I think everything Until here is ok. about refactoring.
but when i use this code below for userside per request for sourcing typeahead
$(document).ready(function() {
$('.typeahead-remote-book').typeahead({
source: function (query, process) {
return $.get('/booktitles', { query: query }, function (data) {
return typeahead.process(data);
});
}
});
});
and i changed the view part of the form to
<div class="form-group">
<label class="col-sm-2 control-label" for="typeahead_book">Title</label>
<div class="col-sm-3">
<input value="<%[email protected](:title)%>" name="option[book_title]" type="text" class="form-control typeahead typeahead-remote-book" id="typeahead_book" data-provide="typeahead" data-items="10" >
<p class="help-block"><small><%= $autocomplete_message %></small></p>
</div>
</div>
*I do not know what is wrong with the situation , but it does not work *
could u please help me ? about solving the problem. Thanks for your kind help.. best regards.
Upvotes: 1
Views: 57
Reputation: 11226
The problem is you're trying to do realime searching in the database and that is not very efficient. At least make sure you're indexing books on title. You should have a migration like:
class AddIndexOnBookTitles < ActiveRecord::Migration
def change
add_index :books, :title, unique: false #or omit false if dupes not allowed.
end
end
Then you don't want book objects, but just titles, which you should probably cache somewhere. We can talk about that later.
Don't use .map
on an ActiveRecordRelation
object but instead .pluck
to just pluck the fields you need out of the DB.
def book_titles
books = Book.any_of({ :title => /.*#{params[:title]}.*/ }).pluck(:title)
respond_to do |format|
format.json { render json: books }
end
end
That still might be slow if you have a lot of books. But give that a try first and see if it's any better. If not, we'll need to do some caching or maybe use Elasticsearch which is designed for this type of thing.
Upvotes: 2