Skip to content
This repository has been archived by the owner on Jan 15, 2024. It is now read-only.

Text search not working with MongoDB 3.0.0 #358

Closed
glampr opened this issue Mar 13, 2015 · 11 comments
Closed

Text search not working with MongoDB 3.0.0 #358

glampr opened this issue Mar 13, 2015 · 11 comments

Comments

@glampr
Copy link

glampr commented Mar 13, 2015

I get the following error when upgraded to MongoDB 3.0.0 :

Moped::Errors::OperationFailure: The operation: #<Moped::Protocol::Command
  @length=100
  @request_id=3
  @response_to=0
  @op_code=2004
  @flags=[]
  @full_collection_name="geonames.$cmd"
  @skip=0
  @limit=-1
  @selector={:text=>"cities1000", :search=>"athens", :filter=>{}}
  @fields=nil>
failed with error 59: "no such command: text"

This is the command I was using that was working in MongoDB 2.6

GeoName.mongo_session.command(
        text: collection,
        search: query,
        filter: filters,
        # project: { ... },
        limit: 10,
        # language: "english"
)

Tested against moped 2.0.4

@glampr
Copy link
Author

glampr commented Mar 15, 2015

@arthurnn
Copy link
Contributor

Didnt know that 3.0 didnt accept text search anymore.
In that case we will have to remove it from moped/mongoid, but thats only on future versions.
thanks for reporting

@glampr
Copy link
Author

glampr commented Mar 15, 2015

Currently this works: Model.where({"$text" => {"$search" => query}})
but the current Origin API does not allow to include the textScore, neither to sort by the textScore.
It would be good if when you tackle this in a new version, you could allow these.
I'm monkey patching the Original::Optional module to achieve this for now

module Origin
  module Optional
    def with_fields(fields_def = {})
      fields_def ||= {}
      fields_def.merge!({_id: 1})
      option(fields_def) do |options|
        options.store(:fields,
          fields_def.inject(options[:fields] || {}) do |sub, field_def|
            key, val = field_def
            sub.tap { sub[key] = val }
          end
        )
      end
    end

    def include_text_search_score
      with_fields({score: {"$meta" => "textScore"}})
    end

    def sort_by_text_search_score
      option({}) do |options, query|
        add_sort_option(options, :score, {"$meta" => "textScore"})
      end
    end
  end
end

and using it like this:

Model.where({"$text" => {"$search" => query}}).
          include_text_search_score.
          sort_by_text_search_score.
          only(:name, :alternate_names, :coordinates). # you have to manually specify the fields because of including the score field
          limit(10)

@krsyoung
Copy link

Thanks for sharing @glampr! Definitely helps ... seems like there is no official way of doing this.

@glampr
Copy link
Author

glampr commented Dec 10, 2015

When upgrading to mongoid >= 5 which doesn't use moped, you can use directly the ruby driver (>= 2.1) like this:

Model.collection.
  find({"$text" => {"$search" => query}}).
  projection(field1: 1, field2: 1, field3: 1, score: {"$meta" => "textScore"}).
  sort({score: {"$meta" => "textScore"}}).
  limit(some_limit).
  entries.
  map { |g| Model.new(g) } # optional if you want objects and not hashes

@krsyoung
Copy link

Beautiful, thanks @glampr 👍

@shahfaizanali
Copy link

Can anyone tell me how I can add text index in mongoid model?
I tried this
index({company_name: 1, first_name: 1, last_name: 1 })

Model.text_search 'something'
gives Mongo::Error::OperationFailure: text index required for $text query

@lalit-nga
Copy link

lalit-nga commented Oct 16, 2017

hi @glampr, I have used your suggestion. But I am getting below error.

Person.collection.find({"$text" => {"$search" =>"yogesh jain"}}).projection(email: 1).entries
Mongo::Error::OperationFailure: text index required for $text query (27)
	from (irb):8:in `entries'
	from (irb):8

I have also put the below code for above with no luck.

index({ first_name: 'text' })
index({ email: 'text' }, { unique: true, background: true })

How I reindex the existing data? Thanks :)

@glampr
Copy link
Author

glampr commented Nov 9, 2017

@lalit-nga just adding the index statements in your model does not add the text index to the database. you also need to run rake db:mongoid:create_indexes

@hartator
Copy link

hartator commented May 30, 2018

Is @glampr answer still the best way to handle this?

Running with success:

def self.search query, limit=0
  Location.collection.
    find({"$text" => {"$search" => query}}).
    projection(score: {"$meta" => "textScore"}).
    sort({score: {"$meta" => "textScore"}}).
    limit(limit).
    entries.
    map { |e| Location.new(e) }
end

Just kind of wish I can chain search with regular Mongoid::Criteria, but that's a detail.

@hartator
Copy link

On a side note, beware of both mongoid_search and mongoid_fulltext gems as they seem to reimplement their own full text indexes that might be overkill in your use cases.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants