thinking-sphinx 2.1.0 → 3.0.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +8 -0
- data/.travis.yml +13 -0
- data/Appraisals +7 -0
- data/Gemfile +10 -0
- data/HISTORY +2 -267
- data/LICENCE +1 -1
- data/README.textile +194 -226
- data/Rakefile +24 -0
- data/gemfiles/.gitignore +1 -0
- data/gemfiles/rails_3_1.gemfile +11 -0
- data/gemfiles/rails_3_2.gemfile +11 -0
- data/lib/thinking-sphinx.rb +1 -1
- data/lib/thinking_sphinx.rb +34 -292
- data/lib/thinking_sphinx/active_record.rb +22 -383
- data/lib/thinking_sphinx/active_record/association.rb +9 -0
- data/lib/thinking_sphinx/active_record/association_proxy.rb +68 -0
- data/lib/thinking_sphinx/active_record/associations.rb +68 -0
- data/lib/thinking_sphinx/active_record/attribute.rb +20 -0
- data/lib/thinking_sphinx/active_record/attribute/sphinx_presenter.rb +32 -0
- data/lib/thinking_sphinx/active_record/attribute/type.rb +79 -0
- data/lib/thinking_sphinx/active_record/attribute/values.rb +18 -0
- data/lib/thinking_sphinx/active_record/base.rb +36 -0
- data/lib/thinking_sphinx/active_record/callbacks/delete_callbacks.rb +31 -0
- data/lib/thinking_sphinx/active_record/callbacks/delta_callbacks.rb +55 -0
- data/lib/thinking_sphinx/active_record/callbacks/update_callbacks.rb +59 -0
- data/lib/thinking_sphinx/active_record/column.rb +30 -0
- data/lib/thinking_sphinx/active_record/database_adapters.rb +51 -0
- data/lib/thinking_sphinx/active_record/database_adapters/abstract_adapter.rb +13 -0
- data/lib/thinking_sphinx/active_record/database_adapters/mysql_adapter.rb +23 -0
- data/lib/thinking_sphinx/active_record/database_adapters/postgresql_adapter.rb +25 -0
- data/lib/thinking_sphinx/active_record/field.rb +11 -0
- data/lib/thinking_sphinx/active_record/index.rb +55 -0
- data/lib/thinking_sphinx/active_record/interpreter.rb +47 -0
- data/lib/thinking_sphinx/active_record/log_subscriber.rb +10 -58
- data/lib/thinking_sphinx/active_record/property.rb +28 -0
- data/lib/thinking_sphinx/active_record/property_sql_presenter.rb +60 -0
- data/lib/thinking_sphinx/active_record/sql_builder.rb +159 -0
- data/lib/thinking_sphinx/active_record/sql_source.rb +138 -0
- data/lib/thinking_sphinx/active_record/sql_source/template.rb +46 -0
- data/lib/thinking_sphinx/batched_search.rb +26 -0
- data/lib/thinking_sphinx/callbacks.rb +15 -0
- data/lib/thinking_sphinx/configuration.rb +80 -331
- data/lib/thinking_sphinx/configuration/consistent_ids.rb +31 -0
- data/lib/thinking_sphinx/configuration/defaults.rb +5 -0
- data/lib/thinking_sphinx/core.rb +6 -0
- data/lib/thinking_sphinx/core/index.rb +68 -0
- data/lib/thinking_sphinx/core/interpreter.rb +19 -0
- data/lib/thinking_sphinx/deltas.rb +35 -26
- data/lib/thinking_sphinx/deltas/default_delta.rb +56 -56
- data/lib/thinking_sphinx/excerpter.rb +23 -21
- data/lib/thinking_sphinx/facet.rb +22 -127
- data/lib/thinking_sphinx/facet_search.rb +95 -162
- data/lib/thinking_sphinx/index.rb +39 -143
- data/lib/thinking_sphinx/index_set.rb +51 -0
- data/lib/thinking_sphinx/masks.rb +8 -0
- data/lib/thinking_sphinx/masks/group_enumerators_mask.rb +23 -0
- data/lib/thinking_sphinx/masks/pagination_mask.rb +60 -0
- data/lib/thinking_sphinx/masks/scopes_mask.rb +35 -0
- data/lib/thinking_sphinx/masks/weight_enumerator_mask.rb +11 -0
- data/lib/thinking_sphinx/middlewares.rb +36 -0
- data/lib/thinking_sphinx/middlewares/active_record_translator.rb +73 -0
- data/lib/thinking_sphinx/middlewares/geographer.rb +53 -0
- data/lib/thinking_sphinx/middlewares/glazier.rb +39 -0
- data/lib/thinking_sphinx/middlewares/ids_only.rb +13 -0
- data/lib/thinking_sphinx/middlewares/inquirer.rb +62 -0
- data/lib/thinking_sphinx/middlewares/middleware.rb +9 -0
- data/lib/thinking_sphinx/middlewares/sphinxql.rb +149 -0
- data/lib/thinking_sphinx/middlewares/stale_id_checker.rb +45 -0
- data/lib/thinking_sphinx/middlewares/stale_id_filter.rb +46 -0
- data/lib/thinking_sphinx/panes.rb +8 -0
- data/lib/thinking_sphinx/panes/attributes_pane.rb +9 -0
- data/lib/thinking_sphinx/panes/distance_pane.rb +13 -0
- data/lib/thinking_sphinx/panes/excerpts_pane.rb +37 -0
- data/lib/thinking_sphinx/panes/weight_pane.rb +9 -0
- data/lib/thinking_sphinx/railtie.rb +6 -40
- data/lib/thinking_sphinx/rake_interface.rb +47 -0
- data/lib/thinking_sphinx/real_time.rb +11 -0
- data/lib/thinking_sphinx/real_time/attribute.rb +5 -0
- data/lib/thinking_sphinx/real_time/callbacks/real_time_callbacks.rb +48 -0
- data/lib/thinking_sphinx/real_time/field.rb +3 -0
- data/lib/thinking_sphinx/real_time/index.rb +47 -0
- data/lib/thinking_sphinx/real_time/index/template.rb +33 -0
- data/lib/thinking_sphinx/real_time/interpreter.rb +23 -0
- data/lib/thinking_sphinx/real_time/property.rb +16 -0
- data/lib/thinking_sphinx/scopes.rb +22 -0
- data/lib/thinking_sphinx/search.rb +90 -1028
- data/lib/thinking_sphinx/search/batch_inquirer.rb +27 -0
- data/lib/thinking_sphinx/search/context.rb +26 -0
- data/lib/thinking_sphinx/search/glaze.rb +32 -0
- data/lib/thinking_sphinx/search/merger.rb +24 -0
- data/lib/thinking_sphinx/search/query.rb +43 -0
- data/lib/thinking_sphinx/search/stale_ids_exception.rb +11 -0
- data/lib/thinking_sphinx/search/translator.rb +50 -0
- data/lib/thinking_sphinx/tasks.rb +22 -125
- data/lib/thinking_sphinx/test.rb +9 -19
- data/sketchpad.rb +58 -0
- data/spec/acceptance/association_scoping_spec.rb +23 -0
- data/spec/acceptance/attribute_access_spec.rb +39 -0
- data/spec/acceptance/attribute_updates_spec.rb +16 -0
- data/spec/acceptance/batch_searching_spec.rb +21 -0
- data/spec/acceptance/big_integers_spec.rb +27 -0
- data/spec/acceptance/excerpts_spec.rb +14 -0
- data/spec/acceptance/facets_spec.rb +122 -0
- data/spec/acceptance/geosearching_spec.rb +39 -0
- data/spec/acceptance/grouping_by_attributes_spec.rb +77 -0
- data/spec/acceptance/paginating_search_results_spec.rb +24 -0
- data/spec/acceptance/remove_deleted_records_spec.rb +23 -0
- data/spec/acceptance/search_counts_spec.rb +18 -0
- data/spec/acceptance/search_for_just_ids_spec.rb +19 -0
- data/spec/acceptance/searching_across_models_spec.rb +28 -0
- data/spec/acceptance/searching_on_fields_spec.rb +56 -0
- data/spec/acceptance/searching_with_filters_spec.rb +109 -0
- data/spec/acceptance/searching_with_sti_spec.rb +55 -0
- data/spec/acceptance/searching_within_a_model_spec.rb +52 -0
- data/spec/acceptance/sorting_search_results_spec.rb +41 -0
- data/spec/acceptance/spec_helper.rb +4 -0
- data/spec/acceptance/specifying_sql_spec.rb +62 -0
- data/spec/acceptance/sphinx_scopes_spec.rb +49 -0
- data/spec/acceptance/sql_deltas_spec.rb +43 -0
- data/spec/acceptance/support/database_cleaner.rb +11 -0
- data/spec/acceptance/support/sphinx_controller.rb +39 -0
- data/spec/acceptance/support/sphinx_helpers.rb +24 -0
- data/spec/acceptance/suspended_deltas_spec.rb +20 -0
- data/spec/internal/.gitignore +1 -0
- data/spec/internal/app/indices/animal_index.rb +3 -0
- data/spec/internal/app/indices/article_index.rb +24 -0
- data/spec/internal/app/indices/book_index.rb +8 -0
- data/spec/internal/app/indices/city_index.rb +6 -0
- data/spec/internal/app/indices/product_index.rb +3 -0
- data/spec/internal/app/indices/tee_index.rb +4 -0
- data/spec/internal/app/indices/user_index.rb +5 -0
- data/spec/internal/app/models/animal.rb +2 -0
- data/spec/internal/app/models/article.rb +5 -0
- data/spec/internal/app/models/bird.rb +2 -0
- data/spec/internal/app/models/book.rb +11 -0
- data/spec/internal/app/models/city.rb +2 -0
- data/spec/internal/app/models/colour.rb +3 -0
- data/spec/internal/app/models/flightless_bird.rb +2 -0
- data/spec/internal/app/models/mammal.rb +2 -0
- data/spec/internal/app/models/product.rb +3 -0
- data/spec/internal/app/models/tag.rb +4 -0
- data/{features/thinking_sphinx → spec/internal/app}/models/tagging.rb +1 -1
- data/spec/internal/app/models/tee.rb +3 -0
- data/spec/internal/app/models/tweet.rb +3 -0
- data/spec/internal/app/models/user.rb +3 -0
- data/spec/internal/config/database.yml +5 -0
- data/spec/internal/db/schema.rb +65 -0
- data/spec/internal/log/.gitignore +1 -0
- data/spec/spec_helper.rb +8 -49
- data/spec/support/sphinx_yaml_helpers.rb +9 -0
- data/spec/thinking_sphinx/active_record/association_spec.rb +12 -0
- data/spec/thinking_sphinx/active_record/associations_spec.rb +184 -0
- data/spec/thinking_sphinx/active_record/attribute/type_spec.rb +147 -0
- data/spec/thinking_sphinx/active_record/base_spec.rb +61 -0
- data/spec/thinking_sphinx/active_record/callbacks/delete_callbacks_spec.rb +80 -0
- data/spec/thinking_sphinx/active_record/callbacks/delta_callbacks_spec.rb +147 -0
- data/spec/thinking_sphinx/active_record/callbacks/update_callbacks_spec.rb +69 -0
- data/spec/thinking_sphinx/active_record/column_spec.rb +47 -0
- data/spec/thinking_sphinx/active_record/database_adapters/abstract_adapter_spec.rb +31 -0
- data/spec/thinking_sphinx/active_record/database_adapters/mysql_adapter_spec.rb +43 -0
- data/spec/thinking_sphinx/active_record/database_adapters/postgresql_adapter_spec.rb +45 -0
- data/spec/thinking_sphinx/active_record/database_adapters_spec.rb +108 -0
- data/spec/thinking_sphinx/active_record/field_spec.rb +36 -0
- data/spec/thinking_sphinx/active_record/index_spec.rb +208 -0
- data/spec/thinking_sphinx/active_record/interpreter_spec.rb +293 -0
- data/spec/thinking_sphinx/active_record/property_sql_presenter_spec.rb +162 -0
- data/spec/thinking_sphinx/active_record/sql_builder_spec.rb +666 -0
- data/spec/thinking_sphinx/active_record/sql_source_spec.rb +401 -0
- data/spec/thinking_sphinx/configuration_spec.rb +264 -171
- data/spec/thinking_sphinx/deltas/default_delta_spec.rb +116 -0
- data/spec/thinking_sphinx/deltas_spec.rb +58 -0
- data/spec/thinking_sphinx/excerpter_spec.rb +40 -38
- data/spec/thinking_sphinx/facet_search_spec.rb +49 -151
- data/spec/thinking_sphinx/index_set_spec.rb +68 -0
- data/spec/thinking_sphinx/index_spec.rb +91 -155
- data/spec/thinking_sphinx/masks/pagination_mask_spec.rb +121 -0
- data/spec/thinking_sphinx/masks/scopes_mask_spec.rb +68 -0
- data/spec/thinking_sphinx/middlewares/active_record_translator_spec.rb +132 -0
- data/spec/thinking_sphinx/middlewares/geographer_spec.rb +89 -0
- data/spec/thinking_sphinx/middlewares/glazier_spec.rb +62 -0
- data/spec/thinking_sphinx/middlewares/inquirer_spec.rb +55 -0
- data/spec/thinking_sphinx/middlewares/sphinxql_spec.rb +271 -0
- data/spec/thinking_sphinx/middlewares/stale_id_checker_spec.rb +47 -0
- data/spec/thinking_sphinx/middlewares/stale_id_filter_spec.rb +91 -0
- data/spec/thinking_sphinx/panes/attributes_pane_spec.rb +21 -0
- data/spec/thinking_sphinx/panes/distance_pane_spec.rb +41 -0
- data/spec/thinking_sphinx/panes/excerpts_pane_spec.rb +53 -0
- data/spec/thinking_sphinx/panes/weight_pane_spec.rb +20 -0
- data/spec/thinking_sphinx/rake_interface_spec.rb +147 -0
- data/spec/thinking_sphinx/real_time/attribute_spec.rb +62 -0
- data/spec/thinking_sphinx/real_time/callbacks/real_time_callbacks_spec.rb +76 -0
- data/spec/thinking_sphinx/real_time/field_spec.rb +54 -0
- data/spec/thinking_sphinx/real_time/index_spec.rb +154 -0
- data/spec/thinking_sphinx/real_time/interpreter_spec.rb +147 -0
- data/spec/thinking_sphinx/scopes_spec.rb +38 -0
- data/spec/thinking_sphinx/search/glaze_spec.rb +55 -0
- data/spec/thinking_sphinx/search/query_spec.rb +46 -0
- data/spec/thinking_sphinx/search_spec.rb +65 -1357
- data/spec/thinking_sphinx_spec.rb +19 -182
- data/thinking-sphinx.gemspec +33 -0
- metadata +318 -431
- data/features/abstract_inheritance.feature +0 -10
- data/features/alternate_primary_key.feature +0 -27
- data/features/attribute_transformation.feature +0 -22
- data/features/attribute_updates.feature +0 -79
- data/features/deleting_instances.feature +0 -70
- data/features/direct_attributes.feature +0 -11
- data/features/excerpts.feature +0 -21
- data/features/extensible_delta_indexing.feature +0 -9
- data/features/facets.feature +0 -88
- data/features/facets_across_model.feature +0 -29
- data/features/field_sorting.feature +0 -18
- data/features/handling_edits.feature +0 -97
- data/features/retry_stale_indexes.feature +0 -24
- data/features/searching_across_models.feature +0 -20
- data/features/searching_by_index.feature +0 -41
- data/features/searching_by_model.feature +0 -175
- data/features/searching_with_find_arguments.feature +0 -56
- data/features/sphinx_detection.feature +0 -25
- data/features/sphinx_scopes.feature +0 -68
- data/features/step_definitions/alpha_steps.rb +0 -16
- data/features/step_definitions/beta_steps.rb +0 -7
- data/features/step_definitions/common_steps.rb +0 -205
- data/features/step_definitions/extensible_delta_indexing_steps.rb +0 -7
- data/features/step_definitions/facet_steps.rb +0 -96
- data/features/step_definitions/find_arguments_steps.rb +0 -36
- data/features/step_definitions/gamma_steps.rb +0 -15
- data/features/step_definitions/scope_steps.rb +0 -19
- data/features/step_definitions/search_steps.rb +0 -94
- data/features/step_definitions/sphinx_steps.rb +0 -35
- data/features/sti_searching.feature +0 -19
- data/features/support/env.rb +0 -24
- data/features/support/lib/generic_delta_handler.rb +0 -8
- data/features/thinking_sphinx/database.example.yml +0 -3
- data/features/thinking_sphinx/db/.gitignore +0 -1
- data/features/thinking_sphinx/db/fixtures/alphas.rb +0 -8
- data/features/thinking_sphinx/db/fixtures/authors.rb +0 -1
- data/features/thinking_sphinx/db/fixtures/betas.rb +0 -11
- data/features/thinking_sphinx/db/fixtures/boxes.rb +0 -9
- data/features/thinking_sphinx/db/fixtures/categories.rb +0 -1
- data/features/thinking_sphinx/db/fixtures/cats.rb +0 -3
- data/features/thinking_sphinx/db/fixtures/comments.rb +0 -24
- data/features/thinking_sphinx/db/fixtures/developers.rb +0 -31
- data/features/thinking_sphinx/db/fixtures/dogs.rb +0 -3
- data/features/thinking_sphinx/db/fixtures/extensible_betas.rb +0 -10
- data/features/thinking_sphinx/db/fixtures/foxes.rb +0 -3
- data/features/thinking_sphinx/db/fixtures/gammas.rb +0 -10
- data/features/thinking_sphinx/db/fixtures/music.rb +0 -4
- data/features/thinking_sphinx/db/fixtures/people.rb +0 -1001
- data/features/thinking_sphinx/db/fixtures/post_keywords.txt +0 -1
- data/features/thinking_sphinx/db/fixtures/posts.rb +0 -10
- data/features/thinking_sphinx/db/fixtures/robots.rb +0 -8
- data/features/thinking_sphinx/db/fixtures/tags.rb +0 -27
- data/features/thinking_sphinx/db/migrations/create_alphas.rb +0 -8
- data/features/thinking_sphinx/db/migrations/create_animals.rb +0 -5
- data/features/thinking_sphinx/db/migrations/create_authors.rb +0 -3
- data/features/thinking_sphinx/db/migrations/create_authors_posts.rb +0 -6
- data/features/thinking_sphinx/db/migrations/create_betas.rb +0 -5
- data/features/thinking_sphinx/db/migrations/create_boxes.rb +0 -5
- data/features/thinking_sphinx/db/migrations/create_categories.rb +0 -3
- data/features/thinking_sphinx/db/migrations/create_comments.rb +0 -10
- data/features/thinking_sphinx/db/migrations/create_developers.rb +0 -7
- data/features/thinking_sphinx/db/migrations/create_extensible_betas.rb +0 -5
- data/features/thinking_sphinx/db/migrations/create_gammas.rb +0 -3
- data/features/thinking_sphinx/db/migrations/create_genres.rb +0 -3
- data/features/thinking_sphinx/db/migrations/create_music.rb +0 -6
- data/features/thinking_sphinx/db/migrations/create_people.rb +0 -13
- data/features/thinking_sphinx/db/migrations/create_posts.rb +0 -6
- data/features/thinking_sphinx/db/migrations/create_robots.rb +0 -4
- data/features/thinking_sphinx/db/migrations/create_taggings.rb +0 -5
- data/features/thinking_sphinx/db/migrations/create_tags.rb +0 -4
- data/features/thinking_sphinx/models/alpha.rb +0 -23
- data/features/thinking_sphinx/models/andrew.rb +0 -17
- data/features/thinking_sphinx/models/animal.rb +0 -5
- data/features/thinking_sphinx/models/author.rb +0 -3
- data/features/thinking_sphinx/models/beta.rb +0 -13
- data/features/thinking_sphinx/models/box.rb +0 -8
- data/features/thinking_sphinx/models/cat.rb +0 -3
- data/features/thinking_sphinx/models/category.rb +0 -4
- data/features/thinking_sphinx/models/comment.rb +0 -10
- data/features/thinking_sphinx/models/developer.rb +0 -21
- data/features/thinking_sphinx/models/dog.rb +0 -3
- data/features/thinking_sphinx/models/extensible_beta.rb +0 -9
- data/features/thinking_sphinx/models/fox.rb +0 -5
- data/features/thinking_sphinx/models/gamma.rb +0 -5
- data/features/thinking_sphinx/models/genre.rb +0 -3
- data/features/thinking_sphinx/models/medium.rb +0 -5
- data/features/thinking_sphinx/models/music.rb +0 -10
- data/features/thinking_sphinx/models/person.rb +0 -24
- data/features/thinking_sphinx/models/post.rb +0 -22
- data/features/thinking_sphinx/models/robot.rb +0 -12
- data/features/thinking_sphinx/models/tag.rb +0 -3
- data/lib/cucumber/thinking_sphinx/external_world.rb +0 -12
- data/lib/cucumber/thinking_sphinx/internal_world.rb +0 -137
- data/lib/cucumber/thinking_sphinx/sql_logger.rb +0 -28
- data/lib/thinking_sphinx/action_controller.rb +0 -31
- data/lib/thinking_sphinx/active_record/attribute_updates.rb +0 -54
- data/lib/thinking_sphinx/active_record/collection_proxy.rb +0 -47
- data/lib/thinking_sphinx/active_record/collection_proxy_with_scopes.rb +0 -27
- data/lib/thinking_sphinx/active_record/delta.rb +0 -67
- data/lib/thinking_sphinx/active_record/has_many_association.rb +0 -44
- data/lib/thinking_sphinx/active_record/has_many_association_with_scopes.rb +0 -21
- data/lib/thinking_sphinx/active_record/scopes.rb +0 -110
- data/lib/thinking_sphinx/adapters/abstract_adapter.rb +0 -94
- data/lib/thinking_sphinx/adapters/mysql_adapter.rb +0 -62
- data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +0 -188
- data/lib/thinking_sphinx/association.rb +0 -230
- data/lib/thinking_sphinx/attribute.rb +0 -405
- data/lib/thinking_sphinx/auto_version.rb +0 -40
- data/lib/thinking_sphinx/bundled_search.rb +0 -40
- data/lib/thinking_sphinx/class_facet.rb +0 -20
- data/lib/thinking_sphinx/connection.rb +0 -71
- data/lib/thinking_sphinx/context.rb +0 -81
- data/lib/thinking_sphinx/core/string.rb +0 -15
- data/lib/thinking_sphinx/deltas/delete_job.rb +0 -16
- data/lib/thinking_sphinx/deltas/index_job.rb +0 -17
- data/lib/thinking_sphinx/deploy/capistrano.rb +0 -99
- data/lib/thinking_sphinx/field.rb +0 -98
- data/lib/thinking_sphinx/index/builder.rb +0 -315
- data/lib/thinking_sphinx/index/faux_column.rb +0 -118
- data/lib/thinking_sphinx/join.rb +0 -37
- data/lib/thinking_sphinx/property.rb +0 -187
- data/lib/thinking_sphinx/search_methods.rb +0 -439
- data/lib/thinking_sphinx/sinatra.rb +0 -7
- data/lib/thinking_sphinx/source.rb +0 -194
- data/lib/thinking_sphinx/source/internal_properties.rb +0 -51
- data/lib/thinking_sphinx/source/sql.rb +0 -174
- data/spec/fixtures/data.sql +0 -32
- data/spec/fixtures/database.yml.default +0 -3
- data/spec/fixtures/models.rb +0 -164
- data/spec/fixtures/structure.sql +0 -146
- data/spec/sphinx_helper.rb +0 -60
- data/spec/support/rails.rb +0 -25
- data/spec/thinking_sphinx/active_record/delta_spec.rb +0 -123
- data/spec/thinking_sphinx/active_record/has_many_association_spec.rb +0 -173
- data/spec/thinking_sphinx/active_record/scopes_spec.rb +0 -177
- data/spec/thinking_sphinx/active_record_spec.rb +0 -573
- data/spec/thinking_sphinx/adapters/abstract_adapter_spec.rb +0 -163
- data/spec/thinking_sphinx/association_spec.rb +0 -250
- data/spec/thinking_sphinx/attribute_spec.rb +0 -552
- data/spec/thinking_sphinx/auto_version_spec.rb +0 -103
- data/spec/thinking_sphinx/connection_spec.rb +0 -77
- data/spec/thinking_sphinx/context_spec.rb +0 -127
- data/spec/thinking_sphinx/core/array_spec.rb +0 -9
- data/spec/thinking_sphinx/core/string_spec.rb +0 -9
- data/spec/thinking_sphinx/facet_spec.rb +0 -359
- data/spec/thinking_sphinx/field_spec.rb +0 -127
- data/spec/thinking_sphinx/index/builder_spec.rb +0 -532
- data/spec/thinking_sphinx/index/faux_column_spec.rb +0 -36
- data/spec/thinking_sphinx/search_methods_spec.rb +0 -156
- data/spec/thinking_sphinx/source_spec.rb +0 -267
- data/spec/thinking_sphinx/test_spec.rb +0 -20
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
class ThinkingSphinx::RealTime::Callbacks::RealTimeCallbacks <
|
|
2
|
+
ThinkingSphinx::Callbacks
|
|
3
|
+
|
|
4
|
+
callbacks :after_save
|
|
5
|
+
|
|
6
|
+
def after_save
|
|
7
|
+
return unless real_time_indices?
|
|
8
|
+
|
|
9
|
+
real_time_indices.each do |index|
|
|
10
|
+
columns, values = ['id'], [index.document_id_for_key(instance.id)]
|
|
11
|
+
(index.fields + index.attributes).each do |property|
|
|
12
|
+
columns << property.name
|
|
13
|
+
values << property.translate(instance)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
sphinxql = Riddle::Query::Insert.new(index.name, columns, values).replace!
|
|
17
|
+
connection.query sphinxql.to_sql
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def config
|
|
24
|
+
ThinkingSphinx::Configuration.instance
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def connection
|
|
28
|
+
connection = config.connection
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def indices
|
|
32
|
+
@indices ||= config.indices_for_references reference
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def real_time_indices?
|
|
36
|
+
real_time_indices.any?
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def real_time_indices
|
|
40
|
+
@real_time_indices ||= indices.select { |index|
|
|
41
|
+
index.is_a? ThinkingSphinx::RealTime::Index
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def reference
|
|
46
|
+
instance.class.name.underscore.to_sym
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
class ThinkingSphinx::RealTime::Index < Riddle::Configuration::RealtimeIndex
|
|
2
|
+
include ThinkingSphinx::Core::Index
|
|
3
|
+
|
|
4
|
+
attr_accessor :fields, :attributes
|
|
5
|
+
|
|
6
|
+
def initialize(reference, options = {})
|
|
7
|
+
@fields = []
|
|
8
|
+
@attributes = []
|
|
9
|
+
|
|
10
|
+
Template.new(self).apply
|
|
11
|
+
|
|
12
|
+
super reference, options
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def unique_attribute_names
|
|
16
|
+
attributes.collect(&:name)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def interpreter
|
|
22
|
+
ThinkingSphinx::RealTime::Interpreter
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def pre_render
|
|
26
|
+
super
|
|
27
|
+
|
|
28
|
+
@rt_field = fields.collect &:name
|
|
29
|
+
|
|
30
|
+
attributes.each do |attribute|
|
|
31
|
+
case attribute.type
|
|
32
|
+
when :integer, :boolean
|
|
33
|
+
@rt_attr_uint << attribute.name unless @rt_attr_uint.include?(attribute.name)
|
|
34
|
+
when :string
|
|
35
|
+
@rt_attr_string << attribute.name unless @rt_attr_string.include?(attribute.name)
|
|
36
|
+
when :timestamp
|
|
37
|
+
@rt_attr_timestamp << attribute.name unless @rt_attr_timestamp.include?(attribute.name)
|
|
38
|
+
when :float
|
|
39
|
+
@rt_attr_float << attribute.name unless @rt_attr_float.include?(attribute.name)
|
|
40
|
+
else
|
|
41
|
+
raise "Unknown attribute type '#{attribute.type(model)}'"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
require 'thinking_sphinx/real_time/index/template'
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
class ThinkingSphinx::RealTime::Index::Template
|
|
2
|
+
attr_reader :index
|
|
3
|
+
|
|
4
|
+
def initialize(index)
|
|
5
|
+
@index = index
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def apply
|
|
9
|
+
add_field class_column, :sphinx_internal_class
|
|
10
|
+
|
|
11
|
+
add_attribute :id, :sphinx_internal_id, :integer
|
|
12
|
+
add_attribute 0, :sphinx_deleted, :integer
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def add_attribute(column, name, type)
|
|
18
|
+
index.attributes << ThinkingSphinx::RealTime::Attribute.new(
|
|
19
|
+
ThinkingSphinx::ActiveRecord::Column.new(*column),
|
|
20
|
+
:as => name, :type => type
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def add_field(column, name)
|
|
25
|
+
index.fields << ThinkingSphinx::RealTime::Field.new(
|
|
26
|
+
ThinkingSphinx::ActiveRecord::Column.new(*column), :as => name
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def class_column
|
|
31
|
+
[:class, :name]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class ThinkingSphinx::RealTime::Interpreter <
|
|
2
|
+
::ThinkingSphinx::Core::Interpreter
|
|
3
|
+
|
|
4
|
+
def has(*columns)
|
|
5
|
+
options = columns.extract_options!
|
|
6
|
+
@index.attributes += columns.collect { |column|
|
|
7
|
+
::ThinkingSphinx::RealTime::Attribute.new column, options
|
|
8
|
+
}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def indexes(*columns)
|
|
12
|
+
options = columns.extract_options!
|
|
13
|
+
@index.fields += columns.collect { |column|
|
|
14
|
+
::ThinkingSphinx::RealTime::Field.new column, options
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def set_property(properties)
|
|
19
|
+
properties.each do |key, value|
|
|
20
|
+
@index.send("#{key}=", value) if @index.class.settings.include?(key)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class ThinkingSphinx::RealTime::Property
|
|
2
|
+
def initialize(column, options = {})
|
|
3
|
+
@column, @options = column, options
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def name
|
|
7
|
+
(@options[:as] || @column.__name).to_s
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def translate(object)
|
|
11
|
+
return @column.__name unless @column.__name.is_a?(Symbol)
|
|
12
|
+
|
|
13
|
+
base = @column.__stack.inject(object) { |base, node| base.try(node) }
|
|
14
|
+
base.try(@column.__name)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module ThinkingSphinx::Scopes
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
module ClassMethods
|
|
5
|
+
def sphinx_scope(name, &block)
|
|
6
|
+
sphinx_scopes[name] = block
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def sphinx_scopes
|
|
10
|
+
@sphinx_scopes ||= {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def method_missing(method, *args, &block)
|
|
16
|
+
return super unless sphinx_scopes.keys.include?(method)
|
|
17
|
+
|
|
18
|
+
query, options = sphinx_scopes[method].call(*args)
|
|
19
|
+
search query, (options || {})
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -1,1054 +1,116 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def self.search(*args)
|
|
32
|
-
warn 'ThinkingSphinx::Search.search is deprecated. Please use ThinkingSphinx.search instead.'
|
|
33
|
-
ThinkingSphinx.search(*args)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# Deprecated. Use ThinkingSphinx.search_for_ids
|
|
37
|
-
def self.search_for_ids(*args)
|
|
38
|
-
warn 'ThinkingSphinx::Search.search_for_ids is deprecated. Please use ThinkingSphinx.search_for_ids instead.'
|
|
39
|
-
ThinkingSphinx.search_for_ids(*args)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# Deprecated. Use ThinkingSphinx.search_for_ids
|
|
43
|
-
def self.search_for_id(*args)
|
|
44
|
-
warn 'ThinkingSphinx::Search.search_for_id is deprecated. Please use ThinkingSphinx.search_for_id instead.'
|
|
45
|
-
ThinkingSphinx.search_for_id(*args)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# Deprecated. Use ThinkingSphinx.count
|
|
49
|
-
def self.count(*args)
|
|
50
|
-
warn 'ThinkingSphinx::Search.count is deprecated. Please use ThinkingSphinx.count instead.'
|
|
51
|
-
ThinkingSphinx.count(*args)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Deprecated. Use ThinkingSphinx.facets
|
|
55
|
-
def self.facets(*args)
|
|
56
|
-
warn 'ThinkingSphinx::Search.facets is deprecated. Please use ThinkingSphinx.facets instead.'
|
|
57
|
-
ThinkingSphinx.facets(*args)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def self.warn(message)
|
|
61
|
-
::ActiveSupport::Deprecation.warn message
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def self.bundle_searches(enum = nil)
|
|
65
|
-
bundle = ThinkingSphinx::BundledSearch.new
|
|
66
|
-
|
|
67
|
-
if enum.nil?
|
|
68
|
-
yield bundle
|
|
69
|
-
else
|
|
70
|
-
enum.each { |item| yield bundle, item }
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
bundle.searches
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def self.matching_fields(fields, bitmask)
|
|
77
|
-
matches = []
|
|
78
|
-
bitstring = bitmask.to_s(2).rjust(32, '0').reverse
|
|
79
|
-
|
|
80
|
-
fields.each_with_index do |field, index|
|
|
81
|
-
matches << field if bitstring[index, 1] == '1'
|
|
82
|
-
end
|
|
83
|
-
matches
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def initialize(*args)
|
|
87
|
-
ThinkingSphinx.context.define_indexes
|
|
88
|
-
|
|
89
|
-
@array = []
|
|
90
|
-
@options = args.extract_options!
|
|
91
|
-
@args = args
|
|
92
|
-
|
|
93
|
-
add_default_scope unless options[:ignore_default]
|
|
94
|
-
|
|
95
|
-
populate if @options[:populate]
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def ==(object)
|
|
99
|
-
populate
|
|
100
|
-
super
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def to_a
|
|
104
|
-
populate
|
|
105
|
-
@array
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
# Populates the search result set
|
|
109
|
-
def all
|
|
110
|
-
populate
|
|
111
|
-
self
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def freeze
|
|
115
|
-
populate
|
|
116
|
-
@array.freeze
|
|
117
|
-
self
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
def as_json(*args)
|
|
121
|
-
populate
|
|
122
|
-
@array.as_json(*args)
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
# Indication of whether the request has been made to Sphinx for the search
|
|
126
|
-
# query.
|
|
127
|
-
#
|
|
128
|
-
# @return [Boolean] true if the results have been requested.
|
|
129
|
-
#
|
|
130
|
-
def populated?
|
|
131
|
-
!!@populated
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
# Indication of whether the request resulted in an error from Sphinx.
|
|
135
|
-
#
|
|
136
|
-
# @return [Boolean] true if Sphinx reports query error
|
|
137
|
-
#
|
|
138
|
-
def error?
|
|
139
|
-
!!error
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
# The Sphinx-reported error, if any.
|
|
143
|
-
#
|
|
144
|
-
# @return [String, nil]
|
|
145
|
-
#
|
|
146
|
-
def error
|
|
147
|
-
populate
|
|
148
|
-
@results[:error]
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
# Indication of whether the request resulted in a warning from Sphinx.
|
|
152
|
-
#
|
|
153
|
-
# @return [Boolean] true if Sphinx reports query warning
|
|
154
|
-
#
|
|
155
|
-
def warning?
|
|
156
|
-
!!warning
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
# The Sphinx-reported warning, if any.
|
|
160
|
-
#
|
|
161
|
-
# @return [String, nil]
|
|
162
|
-
#
|
|
163
|
-
def warning
|
|
164
|
-
populate
|
|
165
|
-
@results[:warning]
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
# The query result hash from Riddle.
|
|
169
|
-
#
|
|
170
|
-
# @return [Hash] Raw Sphinx results
|
|
171
|
-
#
|
|
172
|
-
def results
|
|
173
|
-
populate
|
|
174
|
-
@results
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
def method_missing(method, *args, &block)
|
|
178
|
-
if is_scope?(method)
|
|
179
|
-
add_scope(method, *args, &block)
|
|
180
|
-
return self
|
|
181
|
-
elsif method == :search_count
|
|
182
|
-
merge_search one_class.search(*args), self.args, options
|
|
183
|
-
return scoped_count
|
|
184
|
-
elsif method.to_s[/^each_with_.*/].nil? && !@array.respond_to?(method)
|
|
185
|
-
super
|
|
186
|
-
elsif !SafeMethods.include?(method.to_s)
|
|
187
|
-
populate
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
if method.to_s[/^each_with_.*/] && !@array.respond_to?(method)
|
|
191
|
-
each_with_attribute method.to_s.gsub(/^each_with_/, ''), &block
|
|
192
|
-
else
|
|
193
|
-
@array.send(method, *args, &block)
|
|
194
|
-
end
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
# Returns true if the Search object or the underlying Array object respond
|
|
198
|
-
# to the requested method.
|
|
199
|
-
#
|
|
200
|
-
# @param [Symbol] method The method name
|
|
201
|
-
# @return [Boolean] true if either Search or Array responds to the method.
|
|
202
|
-
#
|
|
203
|
-
def respond_to?(method, include_private = false)
|
|
204
|
-
super || @array.respond_to?(method, include_private)
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
# The current page number of the result set. Defaults to 1 if no page was
|
|
208
|
-
# explicitly requested.
|
|
209
|
-
#
|
|
210
|
-
# @return [Integer]
|
|
211
|
-
#
|
|
212
|
-
def current_page
|
|
213
|
-
@options[:page].blank? ? 1 : @options[:page].to_i
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
def first_page?
|
|
217
|
-
current_page == 1
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
# Kaminari support
|
|
221
|
-
def page(page_number)
|
|
222
|
-
@options[:page] = page_number
|
|
223
|
-
self
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
# The next page number of the result set. If there are no more pages
|
|
227
|
-
# available, nil is returned.
|
|
228
|
-
#
|
|
229
|
-
# @return [Integer, nil]
|
|
230
|
-
#
|
|
231
|
-
def next_page
|
|
232
|
-
current_page >= total_pages ? nil : current_page + 1
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
def next_page?
|
|
236
|
-
!next_page.nil?
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
def last_page?
|
|
240
|
-
next_page.nil?
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
# The previous page number of the result set. If this is the first page,
|
|
244
|
-
# then nil is returned.
|
|
245
|
-
#
|
|
246
|
-
# @return [Integer, nil]
|
|
247
|
-
#
|
|
248
|
-
def previous_page
|
|
249
|
-
current_page == 1 ? nil : current_page - 1
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
# The amount of records per set of paged results. Defaults to 20 unless a
|
|
253
|
-
# specific page size is requested.
|
|
254
|
-
#
|
|
255
|
-
# @return [Integer]
|
|
256
|
-
#
|
|
257
|
-
def per_page
|
|
258
|
-
@options[:limit] ||= @options[:per_page]
|
|
259
|
-
@options[:limit] ||= 20
|
|
260
|
-
@options[:limit].to_i
|
|
261
|
-
end
|
|
262
|
-
# Kaminari support
|
|
263
|
-
alias_method :limit_value, :per_page
|
|
264
|
-
|
|
265
|
-
# Kaminari support
|
|
266
|
-
def per(limit)
|
|
267
|
-
@options[:limit] = limit
|
|
268
|
-
self
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
# The total number of pages available if the results are paginated.
|
|
272
|
-
#
|
|
273
|
-
# @return [Integer]
|
|
274
|
-
#
|
|
275
|
-
def total_pages
|
|
276
|
-
populate
|
|
277
|
-
return 0 if @results.nil? || @results[:total].nil?
|
|
278
|
-
|
|
279
|
-
@total_pages ||= (@results[:total] / per_page.to_f).ceil
|
|
280
|
-
end
|
|
281
|
-
# Compatibility with kaminari and older versions of will_paginate
|
|
282
|
-
alias_method :page_count, :total_pages
|
|
283
|
-
alias_method :num_pages, :total_pages
|
|
284
|
-
|
|
285
|
-
# Query time taken
|
|
286
|
-
#
|
|
287
|
-
# @return [Integer]
|
|
288
|
-
#
|
|
289
|
-
def query_time
|
|
290
|
-
populate
|
|
291
|
-
return 0 if @results[:time].nil?
|
|
292
|
-
|
|
293
|
-
@query_time ||= @results[:time]
|
|
294
|
-
end
|
|
295
|
-
|
|
296
|
-
# The total number of search results available.
|
|
297
|
-
#
|
|
298
|
-
# @return [Integer]
|
|
299
|
-
#
|
|
300
|
-
def total_entries
|
|
301
|
-
populate
|
|
302
|
-
return 0 if @results.nil? || @results[:total_found].nil?
|
|
303
|
-
|
|
304
|
-
@total_entries ||= @results[:total_found]
|
|
305
|
-
end
|
|
306
|
-
|
|
307
|
-
# Compatibility with kaminari
|
|
308
|
-
alias_method :total_count, :total_entries
|
|
309
|
-
|
|
310
|
-
# The current page's offset, based on the number of records per page.
|
|
311
|
-
# Or explicit :offset if given.
|
|
312
|
-
#
|
|
313
|
-
# @return [Integer]
|
|
314
|
-
#
|
|
315
|
-
def offset
|
|
316
|
-
@options[:offset] || ((current_page - 1) * per_page)
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
alias_method :offset_value, :offset
|
|
320
|
-
|
|
321
|
-
def indexes
|
|
322
|
-
return options[:index] if options[:index]
|
|
323
|
-
return '*' if classes.empty?
|
|
324
|
-
|
|
325
|
-
classes.collect { |klass|
|
|
326
|
-
klass.sphinx_index_names
|
|
327
|
-
}.flatten.uniq.join(',')
|
|
328
|
-
end
|
|
329
|
-
|
|
330
|
-
def each_with_groupby_and_count(&block)
|
|
331
|
-
populate
|
|
332
|
-
results[:matches].each_with_index do |match, index|
|
|
333
|
-
yield self[index],
|
|
334
|
-
match[:attributes]["@groupby"],
|
|
335
|
-
match[:attributes]["@count"]
|
|
336
|
-
end
|
|
337
|
-
end
|
|
338
|
-
alias_method :each_with_group_and_count, :each_with_groupby_and_count
|
|
339
|
-
|
|
340
|
-
def each_with_weighting(&block)
|
|
341
|
-
populate
|
|
342
|
-
results[:matches].each_with_index do |match, index|
|
|
343
|
-
yield self[index], match[:weight]
|
|
344
|
-
end
|
|
345
|
-
end
|
|
346
|
-
|
|
347
|
-
def each_with_match(&block)
|
|
348
|
-
populate
|
|
349
|
-
results[:matches].each_with_index do |match, index|
|
|
350
|
-
yield self[index], match
|
|
351
|
-
end
|
|
352
|
-
end
|
|
353
|
-
|
|
354
|
-
def excerpt_for(string, model = nil)
|
|
355
|
-
if model.nil? && one_class
|
|
356
|
-
model ||= one_class
|
|
357
|
-
end
|
|
358
|
-
|
|
359
|
-
populate
|
|
360
|
-
|
|
361
|
-
index = options[:index] || "#{model.core_index_names.first}"
|
|
362
|
-
take_client do |client|
|
|
363
|
-
client.excerpts(
|
|
364
|
-
{
|
|
365
|
-
:docs => [string.to_s],
|
|
366
|
-
:words => query,
|
|
367
|
-
:index => index.split(',').first.strip
|
|
368
|
-
}.merge(options[:excerpt_options] || {})
|
|
369
|
-
).first
|
|
370
|
-
end
|
|
371
|
-
end
|
|
372
|
-
|
|
373
|
-
def search(*args)
|
|
374
|
-
args << args.extract_options!.merge(:ignore_default => true)
|
|
375
|
-
merge_search ThinkingSphinx::Search.new(*args), self.args, options
|
|
376
|
-
self
|
|
377
|
-
end
|
|
378
|
-
|
|
379
|
-
def search_for_ids(*args)
|
|
380
|
-
args << args.extract_options!.merge(
|
|
381
|
-
:ignore_default => true,
|
|
382
|
-
:ids_only => true
|
|
383
|
-
)
|
|
384
|
-
merge_search ThinkingSphinx::Search.new(*args), self.args, options
|
|
385
|
-
self
|
|
386
|
-
end
|
|
387
|
-
|
|
388
|
-
def facets(*args)
|
|
389
|
-
options = args.extract_options!
|
|
390
|
-
merge_search self, args, options
|
|
391
|
-
args << options
|
|
392
|
-
|
|
393
|
-
ThinkingSphinx::FacetSearch.new(*args)
|
|
394
|
-
end
|
|
395
|
-
|
|
396
|
-
def take_client
|
|
397
|
-
if options[:client]
|
|
398
|
-
prepare options[:client]
|
|
399
|
-
yield options[:client]
|
|
400
|
-
else
|
|
401
|
-
ThinkingSphinx::Connection.take do |client|
|
|
402
|
-
prepare client
|
|
403
|
-
yield client
|
|
404
|
-
end
|
|
405
|
-
end
|
|
406
|
-
end
|
|
407
|
-
|
|
408
|
-
def append_to(client)
|
|
409
|
-
prepare client
|
|
410
|
-
client.append_query query, indexes, comment
|
|
411
|
-
client.reset
|
|
412
|
-
end
|
|
413
|
-
|
|
414
|
-
def populate_from_queue(results)
|
|
415
|
-
return if @populated
|
|
416
|
-
@populated = true
|
|
417
|
-
@results = results
|
|
418
|
-
|
|
419
|
-
compose_results
|
|
420
|
-
end
|
|
421
|
-
|
|
422
|
-
private
|
|
1
|
+
class ThinkingSphinx::Search < Array
|
|
2
|
+
CORE_METHODS = %w( == class class_eval extend frozen? id instance_eval
|
|
3
|
+
instance_of? instance_values instance_variable_defined?
|
|
4
|
+
instance_variable_get instance_variable_set instance_variables is_a?
|
|
5
|
+
kind_of? member? method methods nil? object_id respond_to?
|
|
6
|
+
respond_to_missing? send should should_not type )
|
|
7
|
+
SAFE_METHODS = %w( partition private_methods protected_methods public_methods
|
|
8
|
+
send class )
|
|
9
|
+
DEFAULT_MASKS = [
|
|
10
|
+
ThinkingSphinx::Masks::PaginationMask,
|
|
11
|
+
ThinkingSphinx::Masks::ScopesMask
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
instance_methods.select { |method|
|
|
15
|
+
method.to_s[/^__/].nil? && !CORE_METHODS.include?(method.to_s)
|
|
16
|
+
}.each { |method|
|
|
17
|
+
undef_method method
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
attr_reader :options, :masks
|
|
21
|
+
attr_accessor :query
|
|
22
|
+
|
|
23
|
+
def initialize(query = nil, options = {})
|
|
24
|
+
query, options = nil, query if query.is_a?(Hash)
|
|
25
|
+
@query, @options = query, options
|
|
26
|
+
@masks = @options.delete(:masks) || DEFAULT_MASKS
|
|
27
|
+
@middleware = @options.delete(:middleware)
|
|
28
|
+
|
|
29
|
+
populate if options[:populate]
|
|
30
|
+
end
|
|
423
31
|
|
|
424
|
-
|
|
32
|
+
def context
|
|
33
|
+
@context ||= ThinkingSphinx::Search::Context.new self,
|
|
425
34
|
ThinkingSphinx::Configuration.instance
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
def populate
|
|
429
|
-
return if @populated
|
|
430
|
-
@populated = true
|
|
431
|
-
retries = hard_retries
|
|
432
|
-
|
|
433
|
-
begin
|
|
434
|
-
retry_on_stale_index do
|
|
435
|
-
begin
|
|
436
|
-
@results = nil
|
|
437
|
-
log query do
|
|
438
|
-
take_client do |client|
|
|
439
|
-
@results = client.query query, indexes, comment
|
|
440
|
-
end
|
|
441
|
-
end
|
|
442
|
-
total = @results[:total_found].to_i
|
|
443
|
-
log "Found #{total} result#{'s' unless total == 1}"
|
|
444
|
-
log "Sphinx Daemon returned warning: #{warning}" if warning?
|
|
445
|
-
|
|
446
|
-
if error?
|
|
447
|
-
log "Sphinx Daemon returned error: #{error}"
|
|
448
|
-
raise SphinxError.new(error, @results) unless options[:ignore_errors]
|
|
449
|
-
end
|
|
450
|
-
rescue Errno::ECONNREFUSED => err
|
|
451
|
-
raise ThinkingSphinx::ConnectionError,
|
|
452
|
-
'Connection to Sphinx Daemon (searchd) failed.'
|
|
453
|
-
end
|
|
454
|
-
|
|
455
|
-
compose_results
|
|
456
|
-
end
|
|
457
|
-
rescue => e
|
|
458
|
-
log 'Caught Sphinx exception: %s (%s %s left)' % [
|
|
459
|
-
e.message, retries, (retries == 1 ? 'try' : 'tries')
|
|
460
|
-
]
|
|
461
|
-
retries -= 1
|
|
462
|
-
if retries >= 0
|
|
463
|
-
retry
|
|
464
|
-
else
|
|
465
|
-
raise e
|
|
466
|
-
end
|
|
467
|
-
end
|
|
468
|
-
end
|
|
469
|
-
|
|
470
|
-
def compose_results
|
|
471
|
-
if options[:ids_only]
|
|
472
|
-
compose_ids_results
|
|
473
|
-
elsif options[:attributes_only]
|
|
474
|
-
compose_attributes_results
|
|
475
|
-
elsif options[:only]
|
|
476
|
-
compose_only_results
|
|
477
|
-
else
|
|
478
|
-
replace instances_from_matches
|
|
479
|
-
add_excerpter
|
|
480
|
-
add_sphinx_attributes
|
|
481
|
-
add_matching_fields if options[:rank_mode] == :fieldmask
|
|
482
|
-
end
|
|
483
|
-
end
|
|
484
|
-
|
|
485
|
-
def compose_ids_results
|
|
486
|
-
replace @results[:matches].collect { |match|
|
|
487
|
-
match[:attributes]['sphinx_internal_id']
|
|
488
|
-
}
|
|
489
|
-
end
|
|
490
|
-
|
|
491
|
-
def compose_attributes_results
|
|
492
|
-
replace @results[:matches].collect { |match|
|
|
493
|
-
attributes = {}
|
|
494
|
-
match[:attributes].each do |name, value|
|
|
495
|
-
attributes[name.to_sym] = match[:attributes][name]
|
|
496
|
-
end
|
|
497
|
-
attributes
|
|
498
|
-
}
|
|
499
|
-
end
|
|
500
|
-
|
|
501
|
-
def compose_only_results
|
|
502
|
-
replace @results[:matches].collect { |match|
|
|
503
|
-
case only = options[:only]
|
|
504
|
-
when String, Symbol
|
|
505
|
-
match[:attributes][only.to_s]
|
|
506
|
-
when Array
|
|
507
|
-
only.inject({}) do |hash, attribute|
|
|
508
|
-
hash[attribute.to_sym] = match[:attributes][attribute.to_s]
|
|
509
|
-
hash
|
|
510
|
-
end
|
|
511
|
-
else
|
|
512
|
-
raise "Unexpected object for :only argument. String or Array is expected, #{only.class} was received."
|
|
513
|
-
end
|
|
514
|
-
}
|
|
515
|
-
end
|
|
516
|
-
|
|
517
|
-
def add_excerpter
|
|
518
|
-
each do |object|
|
|
519
|
-
next if object.nil?
|
|
520
|
-
|
|
521
|
-
object.excerpts = ThinkingSphinx::Excerpter.new self, object
|
|
522
|
-
end
|
|
523
|
-
end
|
|
524
|
-
|
|
525
|
-
def add_sphinx_attributes
|
|
526
|
-
each do |object|
|
|
527
|
-
next if object.nil?
|
|
528
|
-
|
|
529
|
-
match = match_hash object
|
|
530
|
-
next if match.nil?
|
|
531
|
-
|
|
532
|
-
object.sphinx_attributes = match[:attributes]
|
|
533
|
-
end
|
|
534
|
-
end
|
|
535
|
-
|
|
536
|
-
def add_matching_fields
|
|
537
|
-
each do |object|
|
|
538
|
-
next if object.nil?
|
|
539
|
-
|
|
540
|
-
match = match_hash object
|
|
541
|
-
next if match.nil?
|
|
542
|
-
object.matching_fields = ThinkingSphinx::Search.matching_fields(
|
|
543
|
-
@results[:fields], match[:weight]
|
|
544
|
-
)
|
|
545
|
-
end
|
|
546
|
-
end
|
|
547
|
-
|
|
548
|
-
def match_hash(object)
|
|
549
|
-
@results[:matches].detect { |match|
|
|
550
|
-
class_crc = object.class.name
|
|
551
|
-
class_crc = object.class.to_crc32 if Riddle.loaded_version.to_i < 2
|
|
552
|
-
|
|
553
|
-
match[:attributes]['sphinx_internal_id'] == object.
|
|
554
|
-
primary_key_for_sphinx &&
|
|
555
|
-
match[:attributes][crc_attribute] == class_crc
|
|
556
|
-
}
|
|
557
|
-
end
|
|
558
|
-
|
|
559
|
-
def self.log(message, &block)
|
|
560
|
-
if ThinkingSphinx::ActiveRecord::LogSubscriber.logger.nil?
|
|
561
|
-
yield if block_given?
|
|
562
|
-
return
|
|
563
|
-
end
|
|
564
|
-
|
|
565
|
-
if block_given?
|
|
566
|
-
::ActiveSupport::Notifications.
|
|
567
|
-
instrument('query.thinking_sphinx', :query => message, &block)
|
|
568
|
-
else
|
|
569
|
-
::ActiveSupport::Notifications.
|
|
570
|
-
instrument('message.thinking_sphinx', :message => message)
|
|
571
|
-
end
|
|
572
|
-
end
|
|
573
|
-
|
|
574
|
-
def log(query, &block)
|
|
575
|
-
self.class.log(query, &block)
|
|
576
|
-
end
|
|
577
|
-
|
|
578
|
-
def prepare(client)
|
|
579
|
-
index_options = {}
|
|
580
|
-
if one_class && one_class.sphinx_indexes && one_class.sphinx_indexes.first
|
|
581
|
-
index_options = one_class.sphinx_indexes.first.local_options
|
|
582
|
-
end
|
|
583
|
-
|
|
584
|
-
[
|
|
585
|
-
:max_matches, :group_by, :group_function, :group_clause,
|
|
586
|
-
:group_distinct, :id_range, :cut_off, :retry_count, :retry_delay,
|
|
587
|
-
:rank_mode, :rank_expr, :max_query_time, :field_weights
|
|
588
|
-
].each do |key|
|
|
589
|
-
value = options[key] || index_options[key]
|
|
590
|
-
client.send("#{key}=", value) if value
|
|
591
|
-
end
|
|
592
|
-
|
|
593
|
-
# treated non-standard as :select is already used for AR queries
|
|
594
|
-
client.select = options[:sphinx_select] || '*'
|
|
595
|
-
|
|
596
|
-
client.limit = per_page
|
|
597
|
-
client.offset = offset
|
|
598
|
-
client.match_mode = match_mode
|
|
599
|
-
client.filters = filters
|
|
600
|
-
client.sort_mode = sort_mode
|
|
601
|
-
client.sort_by = sort_by
|
|
602
|
-
client.group_by = group_by if group_by
|
|
603
|
-
client.group_function = group_function if group_function
|
|
604
|
-
client.index_weights = index_weights
|
|
605
|
-
client.anchor = anchor
|
|
606
|
-
|
|
607
|
-
client
|
|
608
|
-
end
|
|
609
|
-
|
|
610
|
-
def retry_on_stale_index(&block)
|
|
611
|
-
stale_ids = []
|
|
612
|
-
retries = stale_retries
|
|
613
|
-
|
|
614
|
-
begin
|
|
615
|
-
options[:raise_on_stale] = retries > 0
|
|
616
|
-
block.call
|
|
617
|
-
|
|
618
|
-
# If ThinkingSphinx::Search#instances_from_matches found records in
|
|
619
|
-
# Sphinx but not in the DB and the :raise_on_stale option is set, this
|
|
620
|
-
# exception is raised. We retry a limited number of times, excluding the
|
|
621
|
-
# stale ids from the search.
|
|
622
|
-
rescue StaleIdsException => err
|
|
623
|
-
retries -= 1
|
|
624
|
-
|
|
625
|
-
# For logging
|
|
626
|
-
stale_ids |= err.ids
|
|
627
|
-
# ID exclusion
|
|
628
|
-
options[:without_ids] = Array(options[:without_ids]) | err.ids
|
|
629
|
-
|
|
630
|
-
log 'Stale Ids (%s %s left): %s' % [
|
|
631
|
-
retries, (retries == 1 ? 'try' : 'tries'), stale_ids.join(', ')
|
|
632
|
-
]
|
|
633
|
-
retry
|
|
634
|
-
end
|
|
635
|
-
end
|
|
636
|
-
|
|
637
|
-
def classes
|
|
638
|
-
@classes ||= options[:classes] || []
|
|
639
|
-
end
|
|
640
|
-
|
|
641
|
-
def one_class
|
|
642
|
-
@one_class ||= classes.length != 1 ? nil : classes.first
|
|
643
|
-
end
|
|
644
|
-
|
|
645
|
-
def query
|
|
646
|
-
@query ||= begin
|
|
647
|
-
q = @args.join(' ') << conditions_as_query
|
|
648
|
-
(options[:star] ? star_query(q) : q).strip
|
|
649
|
-
end
|
|
650
|
-
end
|
|
651
|
-
|
|
652
|
-
def conditions_as_query
|
|
653
|
-
return '' if @options[:conditions].blank?
|
|
654
|
-
|
|
655
|
-
' ' + @options[:conditions].keys.collect { |key|
|
|
656
|
-
search_key = key.is_a?(::Array) ? "(#{key.join(',')})" : key
|
|
657
|
-
"@#{search_key} #{options[:conditions][key]}"
|
|
658
|
-
}.join(' ')
|
|
659
|
-
end
|
|
660
|
-
|
|
661
|
-
def star_query(query)
|
|
662
|
-
token = options[:star].is_a?(Regexp) ? options[:star] : default_star_token
|
|
663
|
-
|
|
664
|
-
query.gsub(/("#{token}(.*?#{token})?"|(?![!-])#{token})/u) do
|
|
665
|
-
pre, proper, post = $`, $&, $'
|
|
666
|
-
# E.g. "@foo", "/2", "~3", but not as part of a token
|
|
667
|
-
is_operator = pre.match(%r{(\W|^)[@~/]\Z}) ||
|
|
668
|
-
pre.match(%r{(\W|^)@\([^\)]*$})
|
|
669
|
-
# E.g. "foo bar", with quotes
|
|
670
|
-
is_quote = proper.starts_with?('"') && proper.ends_with?('"')
|
|
671
|
-
has_star = pre.ends_with?("*") || post.starts_with?("*")
|
|
672
|
-
if is_operator || is_quote || has_star
|
|
673
|
-
proper
|
|
674
|
-
else
|
|
675
|
-
"*#{proper}*"
|
|
676
|
-
end
|
|
677
|
-
end
|
|
678
|
-
end
|
|
679
|
-
|
|
680
|
-
if Regexp.instance_methods.include?(:encoding)
|
|
681
|
-
DefaultStarToken = Regexp.new('\p{Word}+')
|
|
682
|
-
else
|
|
683
|
-
DefaultStarToken = Regexp.new('\w+', nil, 'u')
|
|
684
|
-
end
|
|
685
|
-
|
|
686
|
-
def default_star_token
|
|
687
|
-
DefaultStarToken
|
|
688
|
-
end
|
|
689
|
-
|
|
690
|
-
def comment
|
|
691
|
-
options[:comment] || ''
|
|
692
|
-
end
|
|
693
|
-
|
|
694
|
-
def match_mode
|
|
695
|
-
options[:match_mode] || (options[:conditions].blank? ? :all : :extended)
|
|
696
|
-
end
|
|
697
|
-
|
|
698
|
-
def sort_mode
|
|
699
|
-
@sort_mode ||= case options[:sort_mode]
|
|
700
|
-
when :asc
|
|
701
|
-
:attr_asc
|
|
702
|
-
when :desc
|
|
703
|
-
:attr_desc
|
|
704
|
-
when nil
|
|
705
|
-
case options[:order]
|
|
706
|
-
when String
|
|
707
|
-
:extended
|
|
708
|
-
when Symbol
|
|
709
|
-
:attr_asc
|
|
710
|
-
else
|
|
711
|
-
:relevance
|
|
712
|
-
end
|
|
713
|
-
else
|
|
714
|
-
options[:sort_mode]
|
|
715
|
-
end
|
|
716
|
-
end
|
|
717
|
-
|
|
718
|
-
def sort_by
|
|
719
|
-
case @sort_by = (options[:sort_by] || options[:order])
|
|
720
|
-
when String
|
|
721
|
-
sorted_fields_to_attributes(@sort_by.clone)
|
|
722
|
-
when Symbol
|
|
723
|
-
field_names.include?(@sort_by) ?
|
|
724
|
-
@sort_by.to_s.concat('_sort') : @sort_by.to_s
|
|
725
|
-
else
|
|
726
|
-
''
|
|
727
|
-
end
|
|
728
|
-
end
|
|
729
|
-
|
|
730
|
-
def field_names
|
|
731
|
-
return [] unless one_class
|
|
732
|
-
|
|
733
|
-
one_class.sphinx_indexes.collect { |index|
|
|
734
|
-
index.fields.collect { |field| field.unique_name }
|
|
735
|
-
}.flatten
|
|
736
|
-
end
|
|
737
|
-
|
|
738
|
-
def sorted_fields_to_attributes(order_string)
|
|
739
|
-
field_names.each { |field|
|
|
740
|
-
order_string.gsub!(/(^|\s)#{field}(,?\s|$)/) { |match|
|
|
741
|
-
match.gsub field.to_s, field.to_s.concat("_sort")
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
order_string
|
|
746
|
-
end
|
|
747
|
-
|
|
748
|
-
# Turn :index_weights => { "foo" => 2, User => 1 } into :index_weights =>
|
|
749
|
-
# { "foo" => 2, "user_core" => 1, "user_delta" => 1 }
|
|
750
|
-
#
|
|
751
|
-
def index_weights
|
|
752
|
-
weights = options[:index_weights] || {}
|
|
753
|
-
weights.keys.inject({}) do |hash, key|
|
|
754
|
-
if key.is_a?(Class)
|
|
755
|
-
name = ThinkingSphinx::Index.name_for(key)
|
|
756
|
-
hash["#{name}_core"] = weights[key]
|
|
757
|
-
hash["#{name}_delta"] = weights[key]
|
|
758
|
-
else
|
|
759
|
-
hash[key] = weights[key]
|
|
760
|
-
end
|
|
761
|
-
|
|
762
|
-
hash
|
|
763
|
-
end
|
|
764
|
-
end
|
|
765
|
-
|
|
766
|
-
def group_by
|
|
767
|
-
options[:group] ? options[:group].to_s : nil
|
|
768
|
-
end
|
|
769
|
-
|
|
770
|
-
def group_function
|
|
771
|
-
options[:group] ? :attr : nil
|
|
772
|
-
end
|
|
773
|
-
|
|
774
|
-
def internal_filters
|
|
775
|
-
filters = [Riddle::Client::Filter.new('sphinx_deleted', [0])]
|
|
776
|
-
|
|
777
|
-
class_crcs = classes.collect { |klass|
|
|
778
|
-
klass.to_crc32s
|
|
779
|
-
}.flatten
|
|
780
|
-
|
|
781
|
-
unless class_crcs.empty?
|
|
782
|
-
filters << Riddle::Client::Filter.new('class_crc', class_crcs)
|
|
783
|
-
end
|
|
784
|
-
|
|
785
|
-
filters << Riddle::Client::Filter.new(
|
|
786
|
-
'sphinx_internal_id', filter_value(options[:without_ids]), true
|
|
787
|
-
) unless options[:without_ids].nil? || options[:without_ids].empty?
|
|
788
|
-
|
|
789
|
-
filters
|
|
790
|
-
end
|
|
791
|
-
|
|
792
|
-
def filters
|
|
793
|
-
internal_filters +
|
|
794
|
-
(options[:with] || {}).collect { |attrib, value|
|
|
795
|
-
Riddle::Client::Filter.new attrib.to_s, filter_value(value)
|
|
796
|
-
} +
|
|
797
|
-
(options[:without] || {}).collect { |attrib, value|
|
|
798
|
-
Riddle::Client::Filter.new attrib.to_s, filter_value(value), true
|
|
799
|
-
} +
|
|
800
|
-
(options[:with_all] || {}).collect { |attrib, values|
|
|
801
|
-
Array(values).collect { |value|
|
|
802
|
-
Riddle::Client::Filter.new attrib.to_s, filter_value(value)
|
|
803
|
-
}
|
|
804
|
-
}.flatten +
|
|
805
|
-
(options[:without_any] || {}).collect { |attrib, values|
|
|
806
|
-
Array(values).collect { |value|
|
|
807
|
-
Riddle::Client::Filter.new attrib.to_s, filter_value(value), true
|
|
808
|
-
}
|
|
809
|
-
}.flatten
|
|
810
|
-
end
|
|
811
|
-
|
|
812
|
-
# When passed a Time instance, returns the integer timestamp.
|
|
813
|
-
def filter_value(value)
|
|
814
|
-
case value
|
|
815
|
-
when Range
|
|
816
|
-
filter_value(value.first).first..filter_value(value.last).first
|
|
817
|
-
when Array
|
|
818
|
-
value.collect { |v| filter_value(v) }.flatten
|
|
819
|
-
when Time
|
|
820
|
-
[value.to_i]
|
|
821
|
-
when Date
|
|
822
|
-
[Time.utc(value.year, value.month, value.day).to_i]
|
|
823
|
-
when NilClass
|
|
824
|
-
0
|
|
825
|
-
else
|
|
826
|
-
Array(value)
|
|
827
|
-
end
|
|
828
|
-
end
|
|
829
|
-
|
|
830
|
-
def anchor
|
|
831
|
-
return {} unless options[:geo] || (options[:lat] && options[:lng])
|
|
832
|
-
|
|
833
|
-
{
|
|
834
|
-
:latitude => options[:geo] ? options[:geo].first : options[:lat],
|
|
835
|
-
:longitude => options[:geo] ? options[:geo].last : options[:lng],
|
|
836
|
-
:latitude_attribute => latitude_attr.to_s,
|
|
837
|
-
:longitude_attribute => longitude_attr.to_s
|
|
838
|
-
}
|
|
839
|
-
end
|
|
840
|
-
|
|
841
|
-
def latitude_attr
|
|
842
|
-
options[:latitude_attr] ||
|
|
843
|
-
index_option(:latitude_attr) ||
|
|
844
|
-
attribute(:lat, :latitude)
|
|
845
|
-
end
|
|
846
|
-
|
|
847
|
-
def longitude_attr
|
|
848
|
-
options[:longitude_attr] ||
|
|
849
|
-
index_option(:longitude_attr) ||
|
|
850
|
-
attribute(:lon, :lng, :longitude)
|
|
851
|
-
end
|
|
35
|
+
end
|
|
852
36
|
|
|
853
|
-
|
|
854
|
-
|
|
37
|
+
def meta
|
|
38
|
+
populate
|
|
39
|
+
context[:meta]
|
|
40
|
+
end
|
|
855
41
|
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
end
|
|
42
|
+
def offset
|
|
43
|
+
@options[:offset] || ((current_page - 1) * per_page)
|
|
44
|
+
end
|
|
860
45
|
|
|
861
|
-
|
|
862
|
-
return nil unless one_class
|
|
46
|
+
alias_method :offset_value, :offset
|
|
863
47
|
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
48
|
+
def per_page
|
|
49
|
+
@options[:limit] ||= (@options[:per_page] || 20)
|
|
50
|
+
@options[:limit].to_i
|
|
51
|
+
end
|
|
868
52
|
|
|
869
|
-
|
|
870
|
-
return [] unless one_class
|
|
53
|
+
alias_method :limit_value, :per_page
|
|
871
54
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
}.flatten
|
|
875
|
-
end
|
|
55
|
+
def populate
|
|
56
|
+
return self if @populated
|
|
876
57
|
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
when TrueClass
|
|
880
|
-
3
|
|
881
|
-
when nil, FalseClass
|
|
882
|
-
0
|
|
883
|
-
else
|
|
884
|
-
options[:retry_stale].to_i
|
|
885
|
-
end
|
|
886
|
-
end
|
|
887
|
-
|
|
888
|
-
def hard_retries
|
|
889
|
-
options[:hard_retry_count] || config.hard_retry_count
|
|
890
|
-
end
|
|
58
|
+
middleware.call [context]
|
|
59
|
+
@populated = true
|
|
891
60
|
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
case includes
|
|
896
|
-
when NilClass
|
|
897
|
-
nil
|
|
898
|
-
when Array
|
|
899
|
-
include_from_array includes, klass
|
|
900
|
-
when Symbol
|
|
901
|
-
klass.reflections[includes].nil? ? nil : includes
|
|
902
|
-
when Hash
|
|
903
|
-
include_from_hash includes, klass
|
|
904
|
-
else
|
|
905
|
-
includes
|
|
906
|
-
end
|
|
907
|
-
end
|
|
61
|
+
self
|
|
62
|
+
end
|
|
908
63
|
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
case value
|
|
913
|
-
when Hash
|
|
914
|
-
scoped_hash = include_from_hash(value, klass)
|
|
915
|
-
scoped_array << scoped_hash unless scoped_hash.nil?
|
|
916
|
-
else
|
|
917
|
-
scoped_array << value unless klass.reflections[value].nil?
|
|
918
|
-
end
|
|
919
|
-
end
|
|
920
|
-
scoped_array.empty? ? nil : scoped_array
|
|
921
|
-
end
|
|
64
|
+
def populated!
|
|
65
|
+
@populated = true
|
|
66
|
+
end
|
|
922
67
|
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
end
|
|
928
|
-
scoped_hash.empty? ? nil : scoped_hash
|
|
929
|
-
end
|
|
68
|
+
def raw
|
|
69
|
+
populate
|
|
70
|
+
context[:raw]
|
|
71
|
+
end
|
|
930
72
|
|
|
931
|
-
|
|
932
|
-
|
|
73
|
+
def respond_to?(method, include_private = false)
|
|
74
|
+
super || context[:results].respond_to?(method, include_private)
|
|
75
|
+
end
|
|
933
76
|
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
:select => (options[:select] || index_options[:select]),
|
|
941
|
-
:order => (options[:sql_order] || index_options[:sql_order])
|
|
942
|
-
) : []
|
|
77
|
+
def to_a
|
|
78
|
+
populate
|
|
79
|
+
context[:results].collect { |result|
|
|
80
|
+
result.respond_to?(:unglazed) ? result.unglazed : result
|
|
81
|
+
}
|
|
82
|
+
end
|
|
943
83
|
|
|
944
|
-
|
|
945
|
-
# the search method can retry without them. See
|
|
946
|
-
# ThinkingSphinx::Search.retry_search_on_stale_index.
|
|
947
|
-
if options[:raise_on_stale] && instances.length < ids.length
|
|
948
|
-
stale_ids = ids - instances.map { |i| i.id }
|
|
949
|
-
raise StaleIdsException, stale_ids
|
|
950
|
-
end
|
|
84
|
+
private
|
|
951
85
|
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
86
|
+
def mask_stack
|
|
87
|
+
@mask_stack ||= masks.collect { |klass| klass.new self }
|
|
88
|
+
end
|
|
955
89
|
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
end
|
|
960
|
-
}
|
|
90
|
+
def method_missing(method, *args, &block)
|
|
91
|
+
mask_stack.each do |mask|
|
|
92
|
+
return mask.send(method, *args, &block) if mask.respond_to?(method)
|
|
961
93
|
end
|
|
962
94
|
|
|
963
|
-
|
|
964
|
-
# the number of #find's in multi-model searches.
|
|
965
|
-
#
|
|
966
|
-
def instances_from_matches
|
|
967
|
-
return single_class_results if one_class
|
|
95
|
+
populate if !SAFE_METHODS.include?(method.to_s)
|
|
968
96
|
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
}
|
|
972
|
-
groups.each do |crc, group|
|
|
973
|
-
group.replace(
|
|
974
|
-
instances_from_class(class_from_crc(crc), group)
|
|
975
|
-
)
|
|
976
|
-
end
|
|
977
|
-
|
|
978
|
-
results[:matches].collect do |match|
|
|
979
|
-
groups.detect { |crc, group|
|
|
980
|
-
crc == match[:attributes][crc_attribute]
|
|
981
|
-
}[1].compact.detect { |obj|
|
|
982
|
-
obj.primary_key_for_sphinx == match[:attributes]["sphinx_internal_id"]
|
|
983
|
-
}
|
|
984
|
-
end
|
|
985
|
-
end
|
|
986
|
-
|
|
987
|
-
def single_class_results
|
|
988
|
-
instances_from_class one_class, results[:matches]
|
|
989
|
-
end
|
|
97
|
+
context[:results].send(method, *args, &block)
|
|
98
|
+
end
|
|
990
99
|
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
100
|
+
def middleware
|
|
101
|
+
@middleware ||= begin
|
|
102
|
+
if options[:ids_only]
|
|
103
|
+
ThinkingSphinx::Middlewares::IDS_ONLY
|
|
994
104
|
else
|
|
995
|
-
|
|
996
|
-
end
|
|
997
|
-
end
|
|
998
|
-
|
|
999
|
-
def each_with_attribute(attribute, &block)
|
|
1000
|
-
populate
|
|
1001
|
-
results[:matches].each_with_index do |match, index|
|
|
1002
|
-
yield self[index],
|
|
1003
|
-
(match[:attributes][attribute] || match[:attributes]["@#{attribute}"])
|
|
105
|
+
ThinkingSphinx::Middlewares::DEFAULT
|
|
1004
106
|
end
|
|
1005
107
|
end
|
|
1006
|
-
|
|
1007
|
-
def is_scope?(method)
|
|
1008
|
-
one_class && one_class.sphinx_scopes.include?(method)
|
|
1009
|
-
end
|
|
1010
|
-
|
|
1011
|
-
# Adds the default_sphinx_scope if set.
|
|
1012
|
-
def add_default_scope
|
|
1013
|
-
return unless one_class && one_class.has_default_sphinx_scope?
|
|
1014
|
-
add_scope(one_class.get_default_sphinx_scope.to_sym)
|
|
1015
|
-
end
|
|
1016
|
-
|
|
1017
|
-
def add_scope(method, *args, &block)
|
|
1018
|
-
method = "#{method}_without_default".to_sym
|
|
1019
|
-
merge_search one_class.send(method, *args, &block), self.args, options
|
|
1020
|
-
end
|
|
1021
|
-
|
|
1022
|
-
def merge_search(search, args, options)
|
|
1023
|
-
search.args.each { |arg| args << arg }
|
|
1024
|
-
|
|
1025
|
-
search.options.keys.each do |key|
|
|
1026
|
-
if HashOptions.include?(key)
|
|
1027
|
-
options[key] ||= {}
|
|
1028
|
-
options[key].merge! search.options[key]
|
|
1029
|
-
elsif ArrayOptions.include?(key)
|
|
1030
|
-
options[key] ||= []
|
|
1031
|
-
options[key] += search.options[key]
|
|
1032
|
-
options[key].uniq!
|
|
1033
|
-
else
|
|
1034
|
-
options[key] = search.options[key]
|
|
1035
|
-
end
|
|
1036
|
-
end
|
|
1037
|
-
end
|
|
1038
|
-
|
|
1039
|
-
def scoped_count
|
|
1040
|
-
return self.total_entries if(@options[:ids_only] || @options[:only])
|
|
1041
|
-
|
|
1042
|
-
@options[:ids_only] = true
|
|
1043
|
-
results_count = self.total_entries
|
|
1044
|
-
@options[:ids_only] = false
|
|
1045
|
-
@populated = false
|
|
1046
|
-
|
|
1047
|
-
results_count
|
|
1048
|
-
end
|
|
1049
|
-
|
|
1050
|
-
def crc_attribute
|
|
1051
|
-
Riddle.loaded_version.to_i < 2 ? 'class_crc' : 'sphinx_internal_class'
|
|
1052
|
-
end
|
|
1053
108
|
end
|
|
1054
109
|
end
|
|
110
|
+
|
|
111
|
+
require 'thinking_sphinx/search/batch_inquirer'
|
|
112
|
+
require 'thinking_sphinx/search/context'
|
|
113
|
+
require 'thinking_sphinx/search/glaze'
|
|
114
|
+
require 'thinking_sphinx/search/merger'
|
|
115
|
+
require 'thinking_sphinx/search/query'
|
|
116
|
+
require 'thinking_sphinx/search/stale_ids_exception'
|