Search Multiple Models

Hello folks,

I have four models, each with their own separate index.

Models = Articles, Blogs, MusicTracks, and MediaFiles

I have individual searches within each section of the site working just
fine, but I want to have a gloabl search that searches across all of
them at the same time and returns the results ordered by score.

Here’s how far I am now…

def search
query = params[:query]
articles = Article.find_id_by_contents(query)
blogs = Blog.find_id_by_contents(query)
tracks = MusicTrack.find_id_by_contents(query)
files = MediaFile.find_id_by_contents(query)
matches = (articles + blogs + tracks + files).sort_by {|match|
match[:score] }
results = matches.collect {|match| :score => match[:score], :object
=> match[:model].find(match[:id])}
end

The block I’m passing isn’t working and generates an exception at each
=>. Can anyone give me some advice on what I might be doing wrong or if
there is some better approach to doing this. It also seems that doing a
.find on each id in the model is a little inefficient, but I’m not sure
what else to try.

Any help suggestions would be greatly appreciated.

On Thu, Dec 07, 2006 at 04:07:06AM +0100, Mark wrote:

Here’s how far I am now…
=> match[:model].find(match[:id])}
end

The block I’m passing isn’t working and generates an exception at each
=>. Can anyone give me some advice on what I might be doing wrong or if
there is some better approach to doing this. It also seems that doing a
.find on each id in the model is a little inefficient, but I’m not sure
what else to try.

you could retrieve the objects first with find_by_contents, and then
sort by ferret_score (that’s a method acts_as_ferret adds to your
model, at least if you use the aaf trunk). But beware that the scores of
instances of different models aren’t comparable, so your sorting will be
quite useless.

In theory there’s the multi_search method for exactly your scenario:
results = Article.multi_search(query, [ Blog, MusicTrack, MediaFile ])
the big upside of this is that you get comparable scores and therefore a
reasonable sorting by relevance. the downside is that it does a find on
each id just as you do above.

however the biggest problem with multi_search is that it tends to
segfault with recent Ferret versions (I think 0.10.11 was the last
version working).

So if you want consistent sorting across models, and want to use the
most recent Ferret, your best bet is to let all models share a single
index with the :single_index option.

cheers,
Jens


webit! Gesellschaft für neue Medien mbH www.webit.de
Dipl.-Wirtschaftsingenieur Jens Krämer [email protected]
Schnorrstraße 76 Tel +49 351 46766 0
D-01069 Dresden Fax +49 351 46766 66

Jens,

In theory there’s the multi_search method for exactly your scenario:
results = Article.multi_search(query, [ Blog, MusicTrack, MediaFile ])
the big upside of this is that you get comparable scores and therefore a
reasonable sorting by relevance. the downside is that it does a find on
each id just as you do above.

I’m tried the following, which semi-works:

results =Blog.multi_search(@query, [Article, MediaFile, MusicTrack])

However, each on of those models has associations. Additionally, I need
to pass :conditions statements (e.g., only find published articles). For
instance, an article has comments, a MusicTrack has albums, genres, and
artists. Right now, this works, but it is not pre-featching associations
or using conditions. Is there a way to enable that like…

results =Blog.multi_search(@query, [Article(:conditions => [‘published =
?’, yes], :include => [:article_comments], MediaFile, MusicTrack])

Jens,

Answering my own question here after some more research…turns out this
bug is still present.

http://www.ruby-forum.com/topic/81987#148939

– snip fix –
and I just found the culprit…
Line 27 of multi_index.rb

searcher.search_each(query, options={}, &block)

should be

searcher.search_each(query, options, &block)

– end snip –

I updated that and all worked fine again.

On Thu, Dec 07, 2006 at 08:50:21PM +0100, Mark wrote:

Jens,

Answering my own question here after some more research…turns out this
bug is still present.

Acts_as_ferret limit on multi_search not working? - Ferret - Ruby-Forum

it’s been fixed in trunk for a while now, but I didn’t find the time to
make a new version yet.

For the time being, it should be safe to use the current trunk - I do so
in my current project, too.

Jens


webit! Gesellschaft für neue Medien mbH www.webit.de
Dipl.-Wirtschaftsingenieur Jens Krämer [email protected]
Schnorrstraße 76 Tel +49 351 46766 0
D-01069 Dresden Fax +49 351 46766 66

Jens,

One other thing. I can’t seem to get the following working.

options = {:limit => 10, :offset => 20}
total_hits = Blog.id_multi_search(query, [], options)

The result returned seems to ignore the offset for some reason and
always returns the same resultset…am I doing something wrong?

Jens,

In theory there’s the multi_search method for exactly your scenario:
results = Article.multi_search(query, [ Blog, MusicTrack, MediaFile ])
the big upside of this is that you get comparable scores and therefore a
reasonable sorting by relevance. the downside is that it does a find on
each id just as you do above.

I’m tried the following, which semi-works:

results =Blog.multi_search(@query, [Article, MediaFile, MusicTrack])

However, each on of those models has associations. Additionally, I need
to pass :conditions statements (e.g., only find published articles). For
instance, an article has comments, a MusicTrack has albums, genres, and
artists. Right now, this works, but it is not pre-featching associations
or using conditions. Is there a way to enable that like…

results =Blog.multi_search(@query, [Article(:conditions => [‘published =
?’, yes], :include => [:article_comments], MediaFile, MusicTrack])