Giter Club home page Giter Club logo

scoped_search's Introduction

The scoped_search gem makes it easy to search your ActiveRecord models. Searching is performed using a query string, which should be passed to the named_scope search_for. Based on a definition in what fields to look, it will build query conditions and return those as a named scope.

Scoped search is great if you want to offer a simple yet powerful search box to your users and build a query based on the search string they enter. It comes with a built-in search syntax auto-completer and a value auto-completer. It also comes with a set of helpers that makes it easy to create a clean web UI with sorting and an ajax auto-completer.

Preview

A demo application using the scoped search can be found here: github.com/abenari/scoped_search_demo_app A running version of the demo application can be found here: scope-search-demo.heroku.com A Rails 3.2 demo with assets pipeline and twitter bootstrap: theme github.com/abenari/search-demo2

Installing

Add the following line in your Gemfile, and run bundle install:

gem "scoped_search"

Scoped search 4.x supports Rails 4.2 to 5.0, with Ruby 2.0.0 or higher. Use previous versions, e.g. 3.x to support older versions of Rails or Ruby.

Usage

Scoped search requires you to define the fields you want to search in:

class User < ActiveRecord::Base
  scoped_search on: [:first_name, :last_name]
end

For more information about options and using fields from relations, see the project wiki on search definitions: github.com/wvanbergen/scoped_search/wiki/search-definition

Now, the search_for scope is available for queries. You should pass a query string to the scope. This can be empty or nil, in which case all no search conditions are set (and all records will be returned).

User.search_for('my search string').each { |user| ... }

The result is returned as named_scope. Because of this, you can actually chain the call with other scopes, or with will_paginate. An example:

class Project < ActiveRecord::Base
  scoped_search on: [:name, :description]
  named_scope :public, conditions: { public: true }
end

# using chained named_scopes and will_paginate in your controller
Project.public.search_for(params[:q]).paginate(page: params[:page], include: :tasks)

Search profiles

If you include a :profile option to the scoped_search call, the fields specified will only be searched when you include this :profile into the search_for command as well:

class User < ActiveRecord::Base
  scoped_search on: :public_information
  scoped_search on: :private_information, profile: :members
end

This will only search the :public_information column:

User.search_for('blah blah blah')

And this will only search the :private_information column:

User.search_for('blah blah blah', profile: :members)

More information

More information about usage can be found in the project wiki: github.com/wvanbergen/scoped_search/wiki/usage

Query language

The search query language is simple, but supports several constructs to support more complex queries:

words

require every word to be present, e.g.: some search keywords

phrases

use quotes for multi-word phrases, e.g. "police car"

negation

look for “everything but”, e.g. police -uniform, -"police car", police NOT car

logical keywords

make logical constructs using AND, OR, &&, ||, &, | operators, e.g. uniform OR car, scoped && search

parentheses

to structure logic e.g. "police AND (uniform OR car)"

comparison operators

to search in numerical or temporal fields, e.g. > 22, < 2009-01-01

explicit fields

search only in the given field. e.g. username = root, created_at > 2009-01-01

NULL checks

using the set? and null? operator with a field name, e.g. null? graduated_at, set? parent_id

A complex query example to look for Ruby on Rails programmers without cobol experience, over 18 years old, with a recently updated record and a non-lame nickname:

("Ruby" OR "Rails") -cobol, age >= 18, updated_at > 2009-01-01 && nickname !~ l33t

For more info, see the the project wiki: github.com/wvanbergen/scoped_search/wiki/query-language

Additional resources

License

This plugin is released under the MIT license (see LICENSE).

This plugin was originally developed for Floorplanner.com by Willem van Bergen (github.com/wvanbergen) with help from Wes Hays (github.com/weshays). The current maintainer is Amos Benari (github.com/abenari).

scoped_search's People

Contributors

abenari avatar adamruzicka avatar anderscarling avatar andynu avatar ares avatar brocktimus avatar cj avatar domcleal avatar hermanhiddema avatar isratrade avatar jcmcken avatar jlsherrill avatar jnewland avatar joshk avatar kennon avatar lzap avatar mmoll avatar ohadlevy avatar parthaa avatar paulsd avatar paulspringett avatar pcasaretto avatar peterhellberg avatar rolfb avatar rpaterson avatar shimshtein avatar stbenjam avatar tbrisker avatar treydock avatar wvanbergen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

scoped_search's Issues

Date querying test failures due to timezones

I was looking at a few things today and was getting test failures for seemingly random reasons. I figured out later that I think its probably due to TZ conversions.

I was testing stuff before 8AM and I'm living in +8. So I'm guessing was 2013-03-16 but the DB entries were 2013-03-15 (because they're saved as UTC).

https://github.com/wvanbergen/scoped_search/blob/master/spec/integration/ordinal_querying_spec.rb#L191

https://github.com/wvanbergen/scoped_search/blob/master/spec/integration/ordinal_querying_spec.rb#L195

Does this reasoning sound about right? I've got a few ideas to try, like calling .to_utc or something on the date as well (if that's a thing!?).

Feature: compose searches from associated models

This is not an "issue" - really just a feature request or discussion. Please let me know if there's a more appropriate place to talk about this.

Let's say my app tracks books and magazines. Books have an Editor and Magazines have a Publisher, both of which are of type Person. I want to be able to search books by editor and magazines by publisher. Currently I have to do something like this:

class Person < ActiveRecord::Base
end

class Book < ActiveRecord::Base
  has_one :editor, :class_name => :person
  scoped_search :in => :editor, :on => :name, :rename => "editor.name"
end

class Magazine < ActiveRecord::Base
  has_one :publisher, :class_name => :person
  scoped_search :in => :publisher, :on => :name, :rename => "publisher.name"
end

What I would like to do is to define how to search on Person name in only one place, and then reference that definition from Book and Magazine. Something like this:

class Person < ActiveRecord::Base
  scoped_search :on => :name
end

class Book < ActiveRecord::Base
  has_one :editor, :class_name => :person
  scoped_search :merge => :editor, :prefix => "editor."
end

class Magazine < ActiveRecord::Base
  has_one :publisher, :class_name => :person
  scoped_search :merge => :publisher, :prefix => "publisher."
end

Is there any way to do this?

Case insensitive search in Oracle

Hey,

Really loving the plugin so far - just one small gripe. I'm using the oracle_enhanced db adaptor, and oracle is a bit funny with case-insensitive searches, and was wondering if you'd be able to extend scoped_search to handle these?

You can set a flag at Oracle session level, so that text comparisons are done 'linguistically' (as explained in here: http://www.orafaq.com/node/91 ).

A slightly nicer way is to do a query like the following:

select * from people where lower(name) like '%steve%';

although I'm not sure how compatible this would be with your query builder (I had a little peek at it, but nothing too in depth). Would be good to hear from you about this anyway, and thanks for a great plugin :-)

Dave

Test failures under ActiveRecord 4.1

On master, I see some test failures when using ActiveRecord 4.1.

(4.1 has been pushed to Fedora, and the RPM builds are failing: https://bugzilla.redhat.com/show_bug.cgi?id=1107234)

Failures:

  1) ScopedSearch using a sqlite database querying a :has_and_belongs_to_many relation should find record which is related to @bar_1
     Failure/Error: Joo.search_for('= bar').should have(1).items
     NoMethodError:
       undefined method `klass' for nil:NilClass
     # ./lib/scoped_search/query_builder.rb:354:in `reflection_keys'
     # ./lib/scoped_search/query_builder.rb:258:in `has_many_through_join'
     # ./lib/scoped_search/query_builder.rb:230:in `sql_test'
     # ./lib/scoped_search/query_builder.rb:434:in `block in to_default_fields_sql'
     # ./lib/scoped_search/query_builder.rb:433:in `map'
     # ./lib/scoped_search/query_builder.rb:433:in `to_default_fields_sql'
     # ./lib/scoped_search/query_builder.rb:461:in `to_sql'
     # ./lib/scoped_search/query_builder.rb:54:in `build_find_params'
     # ./lib/scoped_search/query_builder.rb:25:in `build_query'
     # ./lib/scoped_search/definition.rb:258:in `block in register_named_scope!'
     # ./spec/integration/relation_querying_spec.rb:235:in `block (4 levels) in '

  2) ScopedSearch::Definition ScopedSearch::Definition::Field #column should raise an exception when using an unknown field
     Failure/Error: lambda {
       expected ActiveRecord::UnknownAttributeError, got # with backtrace:
         # ./lib/scoped_search/definition.rb:87:in `exception'
         # ./lib/scoped_search/definition.rb:87:in `raise'
         # ./lib/scoped_search/definition.rb:87:in `column'
         # ./spec/unit/definition_spec.rb:15:in `block (5 levels) in '
         # ./spec/unit/definition_spec.rb:14:in `block (4 levels) in '
     # ./spec/unit/definition_spec.rb:14:in `block (4 levels) in '

It looks like the Gemfile.activerecord4 for Travis CI is only testing 4.0, while the general Gemfile will install 4.1.

undefined method `type` for nil:NilClass

When trying to search on a field that could be nil, the error:

undefined method `type' for nil:NilClass

is thrown.

Full trace:

activesupport (3.1.3) lib/active_support/whiny_nil.rb:48:in `method_missing'
scoped_search (2.3.6) lib/scoped_search/definition.rb:49:in `type'
scoped_search (2.3.6) lib/scoped_search/definition.rb:197:in `block in default_fields_for'
scoped_search (2.3.6) lib/scoped_search/definition.rb:197:in `select'
scoped_search (2.3.6) lib/scoped_search/definition.rb:197:in `default_fields_for'
scoped_search (2.3.6) lib/scoped_search/query_builder.rb:337:in `to_sql'
scoped_search (2.3.6) lib/scoped_search/query_builder.rb:411:in `block in to_sql'
scoped_search (2.3.6) lib/scoped_search/query_builder.rb:411:in `map'
scoped_search (2.3.6) lib/scoped_search/query_builder.rb:411:in `to_sql'
scoped_search (2.3.6) lib/scoped_search/query_builder.rb:54:in `build_find_params'
scoped_search (2.3.6) lib/scoped_search/query_builder.rb:25:in `build_query'
scoped_search (2.3.6) lib/scoped_search/definition.rb:234:in `block in register_named_scope!'
activerecord (3.1.3) lib/active_record/named_scope.rb:179:in `call'
activerecord (3.1.3) lib/active_record/named_scope.rb:179:in `block in scope'
actionpack (3.1.3) lib/action_controller/metal/implicit_render.rb:4:in `send_action'
actionpack (3.1.3) lib/abstract_controller/base.rb:167:in `process_action'
actionpack (3.1.3) lib/action_controller/metal/rendering.rb:10:in `process_action'
actionpack (3.1.3) lib/abstract_controller/callbacks.rb:18:in `block in process_action'
activesupport (3.1.3) lib/active_support/callbacks.rb:434:in `_run__2893552642173859430__process_action__138708235223421847__callbacks'
activesupport (3.1.3) lib/active_support/callbacks.rb:386:in `_run_process_action_callbacks'
activesupport (3.1.3) lib/active_support/callbacks.rb:81:in `run_callbacks'
actionpack (3.1.3) lib/abstract_controller/callbacks.rb:17:in `process_action'
actionpack (3.1.3) lib/action_controller/metal/rescue.rb:17:in `process_action'
actionpack (3.1.3) lib/action_controller/metal/instrumentation.rb:30:in `block in process_action'
activesupport (3.1.3) lib/active_support/notifications.rb:53:in `block in instrument'
activesupport (3.1.3) lib/active_support/notifications/instrumenter.rb:21:in `instrument'
activesupport (3.1.3) lib/active_support/notifications.rb:53:in `instrument'
actionpack (3.1.3) lib/action_controller/metal/instrumentation.rb:29:in `process_action'
actionpack (3.1.3) lib/action_controller/metal/params_wrapper.rb:201:in `process_action'
activerecord (3.1.3) lib/active_record/railties/controller_runtime.rb:18:in `process_action'
actionpack (3.1.3) lib/abstract_controller/base.rb:121:in `process'
actionpack (3.1.3) lib/abstract_controller/rendering.rb:45:in `process'
actionpack (3.1.3) lib/action_controller/metal.rb:193:in `dispatch'
actionpack (3.1.3) lib/action_controller/metal/rack_delegation.rb:14:in `dispatch'
actionpack (3.1.3) lib/action_controller/metal.rb:236:in `block in action'
actionpack (3.1.3) lib/action_dispatch/routing/route_set.rb:65:in `call'
actionpack (3.1.3) lib/action_dispatch/routing/route_set.rb:65:in `dispatch'
actionpack (3.1.3) lib/action_dispatch/routing/route_set.rb:29:in `call'
rack-mount (0.8.3) lib/rack/mount/route_set.rb:152:in `block in call'
rack-mount (0.8.3) lib/rack/mount/code_generation.rb:96:in `block in recognize'
rack-mount (0.8.3) lib/rack/mount/code_generation.rb:82:in `optimized_each'
rack-mount (0.8.3) lib/rack/mount/code_generation.rb:95:in `recognize'
rack-mount (0.8.3) lib/rack/mount/route_set.rb:141:in `call'
actionpack (3.1.3) lib/action_dispatch/routing/route_set.rb:532:in `call'
sass (3.1.12) lib/sass/plugin/rack.rb:54:in `call'
actionpack (3.1.3) lib/action_dispatch/middleware/best_standards_support.rb:17:in `call'
rack (1.3.6) lib/rack/etag.rb:23:in `call'
rack (1.3.6) lib/rack/conditionalget.rb:25:in `call'
actionpack (3.1.3) lib/action_dispatch/middleware/head.rb:14:in `call'
actionpack (3.1.3) lib/action_dispatch/middleware/params_parser.rb:21:in `call'
actionpack (3.1.3) lib/action_dispatch/middleware/flash.rb:247:in `call'
rack (1.3.6) lib/rack/session/abstract/id.rb:195:in `context'
rack (1.3.6) lib/rack/session/abstract/id.rb:190:in `call'
actionpack (3.1.3) lib/action_dispatch/middleware/cookies.rb:331:in `call'
activerecord (3.1.3) lib/active_record/query_cache.rb:64:in `call'
activerecord (3.1.3) lib/active_record/connection_adapters/abstract/connection_pool.rb:477:in `call'
actionpack (3.1.3) lib/action_dispatch/middleware/callbacks.rb:29:in `block in call'
activesupport (3.1.3) lib/active_support/callbacks.rb:392:in `_run_call_callbacks'
activesupport (3.1.3) lib/active_support/callbacks.rb:81:in `run_callbacks'
actionpack (3.1.3) lib/action_dispatch/middleware/callbacks.rb:28:in `call'
actionpack (3.1.3) lib/action_dispatch/middleware/reloader.rb:68:in `call'
rack (1.3.6) lib/rack/sendfile.rb:101:in `call'
actionpack (3.1.3) lib/action_dispatch/middleware/remote_ip.rb:48:in `call'
actionpack (3.1.3) lib/action_dispatch/middleware/show_exceptions.rb:47:in `call'
railties (3.1.3) lib/rails/rack/logger.rb:13:in `call'
rack (1.3.6) lib/rack/methodoverride.rb:24:in `call'
rack (1.3.6) lib/rack/runtime.rb:17:in `call'
activesupport (3.1.3) lib/active_support/cache/strategy/local_cache.rb:72:in `call'
rack (1.3.6) lib/rack/lock.rb:15:in `call'
actionpack (3.1.3) lib/action_dispatch/middleware/static.rb:53:in `call'
railties (3.1.3) lib/rails/engine.rb:456:in `call'
rack (1.3.6) lib/rack/content_length.rb:14:in `call'
railties (3.1.3) lib/rails/rack/log_tailer.rb:14:in `call'
rack (1.3.6) lib/rack/handler/webrick.rb:59:in `service'

In combination with ordering and will_paginate the sql is broken on postgresql

Example call:

ConfigTemplate.order('name').search_for("name ~ Kickstart Default and kind = PXELinux", {:order=>nil}).paginate({:page=>1})

Produces:

ActiveRecord::StatementInvalid: PGError: ERROR:  syntax error at or near "FIRST"

LINE 1: ...fig_templates".id, "config_templates"."name"NULLS FIRST  AS ...

 SELECT  DISTINCT "config_templates".id, "config_templates"."name"NULLS FIRST  A
S alias_0 FROM "config_templates" LEFT OUTER JOIN "taxable_taxonomies" ON "taxab
le_taxonomies"."taxable_id" = "config_templates"."id" AND "taxable_taxonomies"."
taxable_type" 

Issue with DECIMAL columns and MySQL

Hey

I have just started using the gem and it's great!

Whilst using it I have encountered a problem with scoped_search on decimals. I've isolated my problem down to the simple test case below:

class Order < ActiveRecord::Base

    scoped_search :on => :total

end

"total" is a DECIMAL column within the database, then running this line within console:

> Order.search_for("123.12")

Gives these results in the development log:

Order Load (65.0ms)  SELECT * FROM `orders` WHERE (`orders`.`total` = 123)

It seems that at some point my search number is being converted into an integer. I believe this should be querying for "orders.total = 123.12". As the moment, the query is, of course, effectively looking for values of 123.00, and hence is not finding the order I'm looking for.

I am using rails version 2.3.10, scoped_search version 2.3.1 and I am using MySQL.

I apologise if this post doesn't make sense.

Thanks for the plugin,
Steve

Find a record that 'owns' records that match conditions

Say I have something like this

class Parent < ActiveRecord::Base
  has_many :children
  scoped_search :in => :children, :on => [:attribute]
end

When I enter Parent.search_for('attribute = Value1 AND attribute = Value2')
I get no results because this is not a "valid" query.
Is this possible?
Am I making sense?

"scoped_search :in => :association" not building correct query

Hi Willem,

I was using the gem successful for the last two weeks. Yesterday I updated to Rails 3.0.7 and now I hit an error with a search on associated objects.

class Organisation < ActiveRecord::Base
  has_many :profiles
  scoped_search :on => [ :name, :description, :url ]
  scoped_search :in => :profiles, :on => [ :identifier ]
  ...
end

I get the MySQL error message: "Unknown column 'profiles.identifier' in 'where clause'"

SELECT `organisations`.* FROM `organisations` WHERE (`organisations`.`name` LIKE '%SEARCHTERM%' OR `organisations`.`description` LIKE '%SEARCHTERM%' OR `organisations`.`url` LIKE '%SEARCHTERM%' OR `profiles`.`identifier` LIKE '%SEARCHTERM%')

It is missing to include the relation into the query. A query like:

SELECT `organisations`.* FROM `organisations`, `profiles` WHERE (`organisations`.`name` LIKE '%SEARCHTERM%' OR `organisations`.`description` LIKE '%SEARCHTERM%' OR `organisations`.`url` LIKE '%SEARCHTERM%' OR `profiles`.`identifier` LIKE '%SEARCHTERM%')

would work.

I'm currently using Ruby 1.9.2dev, Rails 3.0.7 and Scoped Search 2.2.1. But also with a downgrade to Rails 3.0.6 I get the same error.

Do you have any idea how I could fix this?

complete_for returns empty array

Using scoped_search version 2.4.0 gem.

I have the following declared in my Property model:

  scoped_search :on => [:name], :complete_value => true

Searching works as expected.

1.9.3-p194 :001 > Property.search_for('Conrad')
  Property Load (1.6ms)  SELECT "properties".* FROM "properties" WHERE (("properties"."name" ILIKE '%Conrad%')) ORDER BY name ASC
   (0.2ms)  BEGIN
   (0.1ms)  COMMIT
 => [#<Property id: 42, name: "Conrad Centennial Singapore", ..... >] 

However, when I tried to use the auto-complete feature, the value auto-completion does not work. I tried the complete_for method manually and it is returning zero results.

1.9.3-p194 :002 > Property.complete_for('Conrad')
 => []

I tried the demo at http://scope-search-demo.heroku.com/ and the auto-completion for Book names does not work as well.

Any ideas?

Rails 4 support

Does this gem support rails 4?
It would be great if you can open a branch for that
And you might want to change runtime dependency on master to be 'activerecord', '>= 2.1.0', '< 4.0.0'

IN / NOT IN

Hi,

Have you thought about/tried adding support for IN/NOT IN?

p.s. thank you for this gem.

Using like operator produces case sensitive results

The documentation mentions that when using the LIKE operator, the search should be case insensitive. However, I cannot seem to get this to work. All my searches are case sensitive. Below is what I have done, am I missing something?

DATABASE

MySQL
"content" attribute is a varchar

RAILS

Rails: 3.1, Ruby: 1.8.7

SCOPE

scoped_search :on => :content, :default_operator => :like

SEARCH

Post.search_for("Attempt")

GENERATED QUERY

SELECT posts.* FROM posts WHERE ((posts.content LIKE '%Attempt%'))

ACTUAL POST CONTENT

This is a test, attempt #3

Since the post contains the word "attempt" as a lower case word, this record isn't found when searching "Attempt". How can I make this case insensitive. Other than this issue, I am loving the gem! Thanks for your help.

Search_for overwrites custom SELECTs

Hello! Here's my humble addition to the short list of bugs ;-)

Scoped_search seems to overwrite custom selects in certain cases.

Take this simple model, for example:

class User < ActiveRecord::Base
  has_many :products
  scoped_search :in => :products, :on => :name
end
  • This works fine: User.search_for("stuff").
  • This still works: User.search_for("stuff").select("count(products.id) as product_count").having("product_count > 0") (I know that one should better use "where" instead of "having", but it's just a simplified example of the complex query I have locally).
  • This doesn't work, because 2 INNER JOINs are generated: User.search_for("stuff").select("count(products.id) as product_count").joins("INNER JOIN products ON products.user_id = users.id").having("product_count > 0"), so I decided to go with:
  • User.search_for("stuff").select("count(products.id) as product_count").joins(:products).having("product_count > 0") - while this helps to avoid JOIN duplicates, this time search_for replaces my custom select with its own stuff, without my "product_count", so HAVING statement cannot find "product_count" now.

To summarize, it appears that if I use joins("INNER JOIN products ON products.user_id = users.id"), scoped_search does not generate its own SELECT statement. But when I use joins(:products), by specifying the association name instead of hard-coding the SQL statement, scoped_search completely replaces the SELECT statement with that of its own.

I think that instead of trying to be "smart", scoped_search should always use custom select whenever that is available.

Object#type is deprecated; use Object#class

Great gem, but the following deprecation warning is filling up my logs:

~/projects/foo/vendor/gems/scoped_search-2.3.1/lib/scoped_search/definition.rb:49: warning: Object#type is deprecated; use Object#class
➜  ~/projects/foo git:(master) ✗ ruby -v 
ruby 1.8.7 (2009-12-24 patchlevel 248) [x86_64-linux]

Dropping support for Ruby 1.8 and ActiveRecord 2?

I propose to drop support for:

  • Ruby 1.8.7: no longer supported, hard to compile on a recent OSX many gems, (including rake and ActiveRecord) do not support in anymore. Text encoding issues.
  • ActiveRecord 2: old, incompatible syntax with newer versions.

Having these 2 removed would make future development and maintenance a lot easier, and will clean up the Travis build matrix complexities significantly (see https://github.com/wvanbergen/scoped_search/blob/master/.travis.yml). Aftewards, we can do a major version bump.

@abenari what do you think? If we do a major version bump, is there some other backwards incompatible change we want to do at the same time?

autocomplete_field_tag_jquery and auto_complete_field_tag helpers override user-passed CSS classes

Potential fix (untested):

--- a/lib/scoped_search/rails_helper.rb
+++ b/tmp/rails_helper.rb
@@ -190,7 +190,8 @@ module ScopedSearch
     def auto_complete_field_tag(method, val,tag_options = {}, completion_options = {})
       auto_completer_options = { :url => { :action => "auto_complete_#{method}" } }.update(completion_options)

-      text_field_tag(method, val,tag_options.merge(:class => "auto_complete_input")) +
+      classes = "auto_complete_input " << tag_options[:class].to_s
+      text_field_tag(method, val,tag_options.merge(:class => classes)) +
           auto_complete_clear_value_button(method) +
           content_tag("div", "", :id => "#{method}_auto_complete", :class => "auto_complete") +
           auto_complete_field(method, auto_completer_options)
@@ -202,7 +203,8 @@ module ScopedSearch
     # auto_complete_method to respond the JQuery calls,
     def auto_complete_field_tag_jquery(method, val,tag_options = {}, completion_options = {})
       url = url_for(:action => "auto_complete_#{method}", :filter => completion_options[:filter])
-      options = tag_options.merge(:class => "auto_complete_input")
+      classes = "auto_complete_input " << tag_options[:class].to_s
+      options = tag_options.merge(:class => classes)
       text_field_tag(method, val, options) + auto_complete_clear_value_button(method) +
           auto_complete_field_jquery(method, url, completion_options)
     end

Can't init variables when searching on associated records

class Post < ActiveRecord::Base
scoped_search :on => [:name, :description]
scoped_search :in => :user, :on => [:first_name, :last_name]

after_initialize :init

def init
self.publish_time ||= DateTime.now
end
end

In the above example i get "missing attribute: publish_time". If i comment out "scoped_search :in => :user, :on => [:first_name, :last_name]" it works well but i can't search on the associated User.

Any ideas how to fix this ?

complete_for sometimes fail

model.complete_for(' ')
TypeError: no implicit conversion of nil into String
    from /home/ohad/.gem/ruby/gems/scoped_search-2.6.3/lib/scoped_search/auto_complete_builder.rb:145:in `end_with?'
    from /home/ohad/.gem/ruby/gems/scoped_search-2.6.3/lib/scoped_search/auto_complete_builder.rb:145:in `block in build_suggestions'
    from /home/ohad/.gem/ruby/gems/scoped_search-2.6.3/lib/scoped_search/auto_complete_builder.rb:144:in `map'
    from /home/ohad/.gem/ruby/gems/scoped_search-2.6.3/lib/scoped_search/auto_complete_builder.rb:144:in `build_suggestions'
    from /home/ohad/.gem/ruby/gems/scoped_search-2.6.3/lib/scoped_search/auto_complete_builder.rb:51:in `build_autocomplete_options'
    from /home/ohad/.gem/ruby/gems/scoped_search-2.6.3/lib/scoped_search/auto_complete_builder.rb:22:in `auto_complete'
    from /home/ohad/.gem/ruby/gems/scoped_search-2.6.3/lib/scoped_search/definition.rb:273:in `complete_for'
    from (irb):14
    from /home/ohad/.gem/ruby/gems/railties-3.2.17/lib/rails/commands/console.rb:47:in `start'
    from /home/ohad/.gem/ruby/gems/railties-3.2.17/lib/rails/commands/console.rb:8:in `start'
    from /home/ohad/.gem/ruby/gems/railties-3.2.17/lib/rails/commands.rb:41:in `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'

when using external methods that returns an empty hash, incorrect SQL is generated

using the following definition

scoped_search :in => :search_parameters, :on => :value, :on_key=> :name, :complete_value => true, :rename => :params, :ext_method => :search_by_params, :only_explicit => true


def search_by_params method 
  {}
end

and while combining the search query with another query, the resulting query is something like

LINE 1: ...(("something"."name" = 'xxx') AND ())) ORDER ...

database.yml looks for sqlite3-ruby

Hi,
test checks whether sqlite3-ruby is installed (statement is in spec/database.yml):
<% if Gem.available?('sqlite3-ruby') %>
But on recent Fedoras (F16+) this gem is called simply sqlite3.
Note that e.g. RHEL 5 and 6 still use sqlite3-ruby. So it would be nice if that test can support both names.

Hierarchal data search.

This is not an issue - just a request for clarification if this is the right tool for solving a particular use case. Any help appreciated!

Ruby 2, Rails 4

I've got schema with hierarchal data (6 levels deep) that I need to be able to search globally, and starting with a parent.

eg

parent scoped_search :in => :child, :on => [:name]
   child scoped_search :in => :grandchild, :on => [:name]
      grandchild 

This way I can start with parent (effectively making the search global?),

Parent.scoped_search 'grandchild_name'

and return a result of parent - but I need the grandchild.

I also need to be able to start with child, scoping results to its children only. Doing this has been problematic however because if I start with and instance of child, and do child.scoped_search('something') it fails because scoped search is a class method obviously. (?)

I notice I can use a named scope.

https://github.com/wvanbergen/scoped_search/wiki/Usage#leveraging-named_scope

So I tried providing a scope, essentially mapping to a parent - but that did not work.

scope :my_parent, lambda {|parent_id| where(parent_id: parent_id)}
scope :scoped_search_of_parent,  lambda{ |parent_id, search_for| my_parent(parent_id).scoped_search(search_for)}

I'm sure that looks stupid, forgive me :)

TypeError: no implicit conversion of Symbol into Integer
    from /Users/paul/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/scoped_search-2.6.0/lib/scoped_search.rb:26:in

Is this something I might be able to do or am I barking up the wrong gem :) Thank you, Paul

closest matching result on top

Hi,

I have implemented scoped_search for one of my app, it just works great. But only my requirement is that, I want the closes matching result to be on top. Is there any method which I can use to get it in that way?

Thanks.

Searching for 'marco' finds too much

Following scoped_search definition:

scoped_search :on => [:id, :name, :content, :created_at, :updated_at]

If you search for marco it finds every record which has been created or updated on 1st of March 2013.

Caused by the following SQL:

... OR (
  `records`.`created_at` >= '2013-03-01 00:00:00'
  AND `records`.`created_at` < '2013-03-02 00:00:00'
) OR (
  `records`.`updated_at` >= '2013-03-01 00:00:00'
  AND `records`.`updated_at` < '2013-03-02 00:00:00'
) ...

It works, if I remove created_at and updated_at from the definition.

The bug is in defintion.rb#L217. DateTime#parse parses 'marco' as the first of March...:

irb(main):025:0> DateTime.parse('marco')
=> Fri, 01 Mar 2013 00:00:00 +0000

My purpose would be to use DateTime#parse only if there is an operator in front...

Failing rspec test

I'm trying to run spec you bundle in your project.

First I make sure it runs only on sqlite:

sed 5,15d -i spec/database.ruby.yml

then I run tests:

+ rspec spec
......................................................................................................................................F..........................................................................................................

Failures:

  1) ScopedSearch using a sqlite database using order resetting order when selecting distinct values
     Failure/Error: Set.new(distinct_search.map(&:explicit)).should == Set['baz', nil]
       expected: #<Set: {"baz", nil}>
            got: #<Set: {"baz"}> (using ==)
       Diff:
       @@ -1,2 +1,2 @@
       -#<Set: {"baz", nil}>
       +#<Set: {"baz"}>
     # /usr/share/gems/gems/rspec-expectations-2.8.0/lib/rspec/expectations/fail_with.rb:32:in `fail_with'
     # /usr/share/gems/gems/rspec-expectations-2.8.0/lib/rspec/matchers/operator_matcher.rb:47:in `fail_with_message'
     # /usr/share/gems/gems/rspec-expectations-2.8.0/lib/rspec/matchers/operator_matcher.rb:69:in `__delegate_operator'
     # /usr/share/gems/gems/rspec-expectations-2.8.0/lib/rspec/matchers/operator_matcher.rb:59:in `eval_match'
     # /usr/share/gems/gems/rspec-expectations-2.8.0/lib/rspec/matchers/operator_matcher.rb:28:in `block in use_custom_matcher_or_delegate'
     # /tmp/tito-build/rpmbuild-rubygem-scoped_search-b4294a91c7451c618f919cfb92a4b5383ede5a0fmUXQtJ/BUILD/scoped_search-2.4.0/usr/share/gems/gems/scoped_search-2.4.0/spec/integration/string_querying_spec.rb:216:in `block (4 levels) in <top (required)>'
     # /usr/share/gems/gems/rspec-core-2.8.0/lib/rspec/core/example.rb:80:in `instance_eval'
     # /usr/share/gems/gems/rspec-core-2.8.0/lib/rspec/core/example.rb:80:in `block in run'
     # /usr/share/gems/gems/rspec-core-2.8.0/lib/rspec/core/example.rb:173:in `with_around_hooks'
     # /usr/share/gems/gems/rspec-core-2.8.0/lib/rspec/core/example.rb:77:in `run'
     # /usr/share/gems/gems/rspec-core-2.8.0/lib/rspec/core/example_group.rb:355:in `block in run_examples'
     # /usr/share/gems/gems/rspec-core-2.8.0/lib/rspec/core/example_group.rb:351:in `map'
     # /usr/share/gems/gems/rspec-core-2.8.0/lib/rspec/core/example_group.rb:351:in `run_examples'
     # /usr/share/gems/gems/rspec-core-2.8.0/lib/rspec/core/example_group.rb:337:in `run'
     # /usr/share/gems/gems/rspec-core-2.8.0/lib/rspec/core/example_group.rb:338:in `block in run'
     # /usr/share/gems/gems/rspec-core-2.8.0/lib/rspec/core/example_group.rb:338:in `map'
     # /usr/share/gems/gems/rspec-core-2.8.0/lib/rspec/core/example_group.rb:338:in `run'
     # /usr/share/gems/gems/rspec-core-2.8.0/lib/rspec/core/command_line.rb:28:in `block (2 levels) in run'
     # /usr/share/gems/gems/rspec-core-2.8.0/lib/rspec/core/command_line.rb:28:in `map'
     # /usr/share/gems/gems/rspec-core-2.8.0/lib/rspec/core/command_line.rb:28:in `block in run'
     # /usr/share/gems/gems/rspec-core-2.8.0/lib/rspec/core/reporter.rb:34:in `report'
     # /usr/share/gems/gems/rspec-core-2.8.0/lib/rspec/core/command_line.rb:25:in `run'
     # /usr/share/gems/gems/rspec-core-2.8.0/lib/rspec/core/runner.rb:80:in `run_in_process'
     # /usr/share/gems/gems/rspec-core-2.8.0/lib/rspec/core/runner.rb:69:in `run'
     # /usr/share/gems/gems/rspec-core-2.8.0/lib/rspec/core/runner.rb:10:in `block in autorun'

Finished in 0.86039 seconds
241 examples, 1 failure

Is is bug in code itself, or just bug in that test?

joins instead of includes

Is there a way to force a joins instead of an includes?

In here:

  searchable in: :order, on: :client_id, alias: :order_client_id

autocompleter => false

Can a feature be added to not include a scoped search definition in the complete_for list. This will be helpful when we want to keep backward compatibility legacy API search scripts, but to have the UI auto-completer without the legacy fields.

Belongs_to association

Hey,

I only just started working with the gem and i'm loving it!
However, there is one issue i can't get my head around when it comes to multiple belongs_to associations on the same model.
My app has the following model:


class Invoice < ActiveRecord::Base

  belongs_to :debtor, :class_name => "Company", :foreign_key => "debtor_id"
  belongs_to :creditor, :class_name => "Company", :foreign_key => "creditor_id"
  has_many :invoice_items

  accepts_nested_attributes_for :invoice_items, :reject_if => lambda { |a| a[:name].blank? || a[:amount].blank? || a[:price_per_unit].blank? }, 
                                                :allow_destroy => true
  
  validates_uniqueness_of :number
  validates_presence_of :debtor
  validates_presence_of :creditor

  searchable_on :number, :creditor_name, :debtor_name

  def self.search(search, page)
    Invoice.search_for(search).paginate(:page => page, :per_page => 5)
  end
end

Notice how the Invoice belongs to a Debtor and a Creditor which both have the same table.
Now if i run a search it generates the following sql query

SELECT DISTINCT `invoices`.id 
FROM `invoices` 
LEFT OUTER JOIN `companies` 
ON `companies`.`id` = `invoices`.`creditor_id` 
LEFT OUTER JOIN `companies` `debtors_invoices` 
ON `debtors_invoices`.`id` = `invoices`.`debtor_id` 
WHERE (`invoices`.`number` LIKE '%something%'  OR `companies`.`name` LIKE '% something%'  OR `companies`.`name` LIKE '% something%') LIMIT 0, 5

Where i think it should generate something like:

SELECT DISTINCT `invoices`.id 
FROM `invoices` 
LEFT OUTER JOIN `companies` AS debtors 
ON debtors.`id` = `invoices`.`debtor_id` 
LEFT OUTER JOIN `companies` AS creditors
ON creditors.`id` = `invoices`.`creditor_id` 
WHERE (debtors.`name` LIKE '%something%' OR creditors.`name` LIKE '%something%') LIMIT 0, 5

Using rails Rails 3.0.0.beta4
Ruby 1.8.7

So now, it only searches on the belongs_to attribute i specified first in the searchable_on
Am I doing something wrong, or is the search query not set up correctly?

(I apologise in advance if my post doesn't make sense, its my first post here on github)

Simple Queries

Is there currently a way to force simple queries only? And not parse keywords and special symbols like "AND", "OR", "&", "|", etc..

Because I don't really publish the query language to users and I don't expect them to use the query language. But when they enter a search term with a keyword they'll get an invalid query because the query is malformed.

Using period as search query throws error

Using the demo app here:
https://github.com/abenari/scoped_search_demo_app

Typing a period . as the search term throws an error:


NoMethodError (undefined method `to_sym' for nil:NilClass):
  app/controllers/books_controller.rb:47:in `auto_complete_search'

Here is the stack track without the auto_complete_search


NoMethodError (undefined method `to_sym' for nil:NilClass):
  vendor/gems/scoped_search-2.3.1/lib/scoped_search/definition.rb:168:in `field_by_name'
  vendor/gems/scoped_search-2.3.1/lib/scoped_search/query_builder.rb:327:in `to_sql'
  vendor/gems/scoped_search-2.3.1/lib/scoped_search/query_builder.rb:52:in `build_find_params'
  vendor/gems/scoped_search-2.3.1/lib/scoped_search/query_builder.rb:25:in `build_query'
  vendor/gems/scoped_search-2.3.1/lib/scoped_search/definition.rb:228

This is using ruby 1.9.2

When you use ruby 1.8.7, additional errors are thrown for typing a quote "as the search query


F, [2011-09-20T13:52:51.964356 #23827] FATAL -- : 
ArgumentError (interning empty string):
  vendor/gems/scoped_search-2.3.1/lib/scoped_search/definition.rb:167:in `to_sym'
  vendor/gems/scoped_search-2.3.1/lib/scoped_search/definition.rb:167:in `field_by_name'
  vendor/gems/scoped_search-2.3.1/lib/scoped_search/query_builder.rb:327:in `to_sql'
  vendor/gems/scoped_search-2.3.1/lib/scoped_search/query_builder.rb:52:in `build_find_params'
  vendor/gems/scoped_search-2.3.1/lib/scoped_search/query_builder.rb:25:in `build_query'
  vendor/gems/scoped_search-2.3.1/lib/scoped_search/definition.rb:228

PostgreSQL Full Text Search

I've created a fork to try and implement PostgreSQL full text searching in place of ILIKE. This allows for the creation and use of an index, which greatly increases query performance. Initial working changes can be seen in the commit below.

brocktimus@8828d1d

A few questions:

  1. The 'english' is relating to the current config. This is necessary if an index is to be used in the query. Any ideas on the best way of implementing this? My initial thoughts would be to create a method for config on the adapter with a default of 'english' which could be monkey patched if a different config would be used. Setting it to a field property seems inappropriate since it is a database wide setting.
  2. Should my negation variable use the to_not_sql function instead which negates an AST node?

Some other notes:

  1. An index needs to be created using manual SQL, this can be noted in the wiki.
  2. The ability to have wildcards mid word is lost, this can also be documented in the wiki.
  3. At the moment queries involving "common" words result in the below message. Google suggests this is possibly just a small bug with the version I am using of the client or server.
    NOTICE:  text-search query contains only stop words or doesn't contain lexemes, ignored

PostgreSQL doesn't support GROUP BY to get rid of duplicate results

MySQL is very liberal in executing aggregate queries and allows group by a field in order to get rid of duplicate rows introduced by a join:

SELECT * 
  FROM table
  JOIN other_table ON table.id = other_table.id -- this may yield multiple rows per record in table
  WHERE other_table.field = 'stuff'
  GROUP BY table.id -- get rid of the duplicate rows here

However, this is against the SQL standard and therefore PostgreSQL doesn't allow this. Instead, you need to use a subquery and EXISTS to achieve the same goal:

SELECT *
  FROM table
  WHERE EXISTS (
    SELECT 1 
      FROM other_table 
      WHERE other_table.table_id = table.id AND field = 'stuff'
  )

Date handling in Ruby 1.9.1

Date(Time).parse handles date in mm/dd/yy format differently in 1.8 and 1.9. This is why we have some failing specs in 1.9.1.

We could solve this by handling the date formats we support by ourselves instead of relying on Date.parse. We can do this by providing an alternative implementation for parse_temporal in the QueryBuilder class.

Unable to search with multiple relationships to the same table

When working with a model with multiple belongs_to relationships to the same table, the query being generated ends up ignoring the relationships due to the WHERE clause of the query going directly to the table name rather than the association. For example:

class support_ticket < ActiveRecord::Base
  belongs_to :requestor, class_name: "User"
  belongs_to :worker, class_name: "User"
  ...
  scoped_search :in => : requestor, :on => :name
  scoped_search :in => : worker, :on => :name
end

The WHERE clause in the query generated from those relationships is similar to the following:

WHERE (`users`.`name` LIKE '%john%') ...

Thus the filtering only applies to whichever association happens to JOIN to the users table without an alias. Ideally it would be:

WHERE (`workers_support_tickets`.`name` LIKE '%john%') ...

Or something similar, depending on how the db alias on the JOIN is generated by ActiveRecord for the query.

Working with acts_as_taggable

Do you have any thoughts on using this with acts_as_taggable? The only solution I can think is adding a tag_search_list and then saving all the tags as a string in the same table, but these seems "kludgy"

Thanks for the great gem!

TypeError thrown when search terms unspecified

I like how when searching for invalid columns ScopedSearch exceptions are raised with appropriate messages. Such as the situation below.

@objects = SomeModel.search_for('invalid_column=bar')

What would also be nice is if a similar exception and appropriate message were raised in the following situation.

@objects = SomeModel.search_for('valid_column=')

At the moment this just raises TypeError with 'cannot convert nil to string. Could similar logic be used as above?

Then queries like this could have very obvious error messages.

@objects = SomeModel.search_for('valid_column=foo OR another_column=')

Should I write failing tests for these? I'll have a look at it when I have some more time. At the moment I just had the idea and haven't looked into the query parser in detail.

Search completion fails for inherited attributes

Having a model that inherits from a common base with defined attributes

class Animal < ActiveRecord::Base
   scoped_search :on => :name, :complete_value => :true
end

class Mammal < Animal
end

the complete_for called on the model doesn't complete the attribute 'name'

Mammal.complete_for('')
> []

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.