flex icon indicating copy to clipboard operation
flex copied to clipboard

Include Flex::Scopes in module throws "uninitialized constant ModelName::Flex::Scopes"

Open BartlomiejSkwira opened this issue 11 years ago • 6 comments

Example:

class Project < ActiveRecord::Base

  include Flex::ModelIndexer
  flex.sync self

  def flex_source
    { :name => name }
  end

  module Flex
    include Flex::Scopes
    flex.context = Project
    scope :search_by_name, ->(name) { terms(name: name) }
  end
end

This throws NameError: uninitialized constant Project::Flex::Scopes on server start. I had to do

 module Flex
    include ::Flex::Scopes
    ...
 end

BartlomiejSkwira avatar Oct 30 '13 11:10 BartlomiejSkwira

True! Thanks for pointing that out: I will fix the example in the doc.

ddnexus avatar Oct 30 '13 12:10 ddnexus

BTW: how do I chain a Flex scope (which is in a module) with an ActiveRecord scope/relation? Example:

#search_by_name is in Flex module like in first post
@projects = @user.projects.search_by_name('Tom')

#I can get the indexer, but don't know how can I use it
#@projects = @user.projects.flex ...

BartlomiejSkwira avatar Oct 30 '13 12:10 BartlomiejSkwira

Not sure to understand what you need. A Flex scope and a AR scope are 2 different things: the first is defining a search criteria in the index, the second in the DB. Index and DB have usually quite different structures and it's practically impossible to integrate them.

If you need to search the index, then use Project::Flex.search_by_name('Tom') and you will get the elasticsearch documents with the term 'Tom'. If you want to restrict the search to a specific subset of projects (like the projects of a user) then you should create add the criteria to restrict to that use.

For example, supposing you have a :user_id field in the projects:

def flex_source
    { :name => name, :user_id => user_id }
  end

module Flex
  ...
  scope :search_by_user_id_and_name, ->(user_id, name) { terms(user_id: user_id, name: name) }
  ...
end

Or you can write 2 scopes one for the user_id and another for the project name, then chain them together when you need both or use them separately at will.

ddnexus avatar Oct 30 '13 12:10 ddnexus

Yeah, I thought so, this was actually my first solution - I was just curious if maybe there is a way. I'm having a problem actually with a basic search, just by :name

class Project < ActiveRecord::Base

  include Flex::ModelIndexer
  flex.sync self

  def flex_source
    { :name => name }
  end

  module Flex
    include ::Flex::Scopes
    flex.context = Project
    scope :search_by_name, ->(name) { terms(name: name) }
  end
end

Flex has the data

Flex.count => 11
Flex.all[8] => 
=> {"_index"=>"karma_tracker_development",
 "_type"=>"project",
 "_id"=>"825",
 "_score"=>1.0,
 "_source"=>{"name"=>"Public test"}}

But when I do:

Project::Flex.search_by_name('Public').all
=> []

Nada, any ideas?

BartlomiejSkwira avatar Oct 30 '13 14:10 BartlomiejSkwira

If you enter the complete content of the field ("Public test") you should get it. Please, refer to the elasticsearch doc for the term query.

ddnexus avatar Oct 30 '13 14:10 ddnexus

Well, actually it doesn't work for complete fraze because the words need to be seperate (and downcase as well). I ended up with custom filters (to get prefixes and filter ids)

scope :search_by_id_and_name, ->(names, ids) { filters(prefix: { name: names }).
                                                   filters(ids: { values: ids } ) }

Thanks for the help!

BartlomiejSkwira avatar Oct 31 '13 13:10 BartlomiejSkwira