# A noob with a HABTM doubt

**URL:** <https://www.ruby-forum.com/t/a-noob-with-a-habtm-doubt/135649>\
**Category:** Rails\
**Created:** [April 23, 2008, 9:53pm UTC](https://www.ruby-forum.com/t/a-noob-with-a-habtm-doubt/135649 "2008-04-23T21:53:00Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![gattone](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/898d66/32.png) [@gattone](https://www.ruby-forum.com/u/gattone)\
**Post date:** [April 23, 2008, 9:53pm UTC](https://www.ruby-forum.com/t/a-noob-with-a-habtm-doubt/135649/1 "2008-04-23T21:53:00Z")

</div>

If I have the following tables:

newsletters - with id(int), title(string), content(string),  
created\_at(date)  
tags - id(int), name(string), kind(int)  
newsletters\_tags - newsletter\_id(ind), tag\_id(int)

And also the models Newsletter and Tag, each in a HABTM relationship  
with each other.

Ok, so I want to find all newsletters that share a collection of tags,  
using the tag ids.  
I tried to make the following query but it didn’t work:

Newsletter.find(:all, :conditions =\> [“tag\_id IN(?)”, “12, 16”])

I would like to know whats going wrong and how could I do it.

Can you help me?

Thank you

---

<div class="post-metadata">

**Author:** ![gattone](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/898d66/32.png) [@gattone](https://www.ruby-forum.com/u/gattone)\
**Post date:** [April 23, 2008, 10:56pm UTC](https://www.ruby-forum.com/t/a-noob-with-a-habtm-doubt/135649/2 "2008-04-23T22:56:45Z")

</div>

The best bet would be to find the tag, then bring up the related  
newsletters…

@newsletters = Tag.find(params[:tag\_id]).newsletters

---

<div class="post-metadata">

**Author:** ![gattone](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/898d66/32.png) [@gattone](https://www.ruby-forum.com/u/gattone)\
**Post date:** [April 24, 2008, 12:35am UTC](https://www.ruby-forum.com/t/a-noob-with-a-habtm-doubt/135649/3 "2008-04-24T00:35:57Z")

</div>

Even thought I apreciate your will to help, I must say that I already  
did what you said and the result was different than what I really  
need.

Let me put it in a better way

what I really need is to retrieve the newsletters that has BOTH tags,  
not all newsletters that has one or another tag.

How would I do something like this?

Thank you.

---

<div class="post-metadata">

**Author:** ![gattone](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/898d66/32.png) [@gattone](https://www.ruby-forum.com/u/gattone)\
**Post date:** [April 24, 2008, 7:59am UTC](https://www.ruby-forum.com/t/a-noob-with-a-habtm-doubt/135649/4 "2008-04-24T07:59:22Z")

</div>

Hmmm… not sure… could do something like

newsletters = Tag.find(params[:tag\_id]).newsletters  
newsletters = newsletters.find\_all\_by\_tag\_id( tag\_id )

That should work?

---

<div class="post-metadata">

**Author:** ![gattone](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/898d66/32.png) [@gattone](https://www.ruby-forum.com/u/gattone)\
**Post date:** [April 24, 2008, 3:51pm UTC](https://www.ruby-forum.com/t/a-noob-with-a-habtm-doubt/135649/5 "2008-04-24T15:51:42Z")

</div>

Well, efficiency aside, here’s the logic I’d use (at least initially)

Let’s say you have three tags with IDs 1,2 and 3. And you wanted to  
find all newsletters that had all three of those:

newsletters\_one = Tag.find(1).newsletters  
newsletters\_two = Tag.find(2).newsletters  
newsletters\_three = Tag.find(3).newsletters

So now you have three arrays of newsletters that have each of the IDs.  
Then, run a set intersection on them:

has\_all\_three = newsletters\_one & newsletters\_two & newsletters\_three

That will return an array of newsletters that are in all three.

Of course, that’s just to be sure the logic is right. Then, you can  
refactor it for efficiency. For example, ditching the intermediary  
variables.

I hope that gets you going.

-Danimal

---

<div class="post-metadata">

**Author:** ![gattone](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/898d66/32.png) [@gattone](https://www.ruby-forum.com/u/gattone)\
**Post date:** [April 28, 2008, 3:23pm UTC](https://www.ruby-forum.com/t/a-noob-with-a-habtm-doubt/135649/6 "2008-04-28T15:23:47Z")

</div>

The reason your first try didn’t work is you’re passing a string that  
isn’t going to be parsed by #find, when what you need is an array.  
This is closer to the correct syntax:

Newsletter.find(:all, :conditions =\> [“tag\_id IN(?)”, [12, 16]])

---

<div class="post-metadata">

**Author:** ![gattone](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/g/898d66/32.png) [@gattone](https://www.ruby-forum.com/u/gattone)\
**Post date:** [April 28, 2008, 5:30pm UTC](https://www.ruby-forum.com/t/a-noob-with-a-habtm-doubt/135649/7 "2008-04-28T17:30:26Z")

</div>

Danimal wrote:

> So now you have three arrays of newsletters that have each of the IDs.  
> Then, run a set intersection on them:

That is the logic I’d normally start with for something like this.

Fortunately, there are some useful plugins which can solve this sort of  
problem for you. One is squirrel:

> **[thoughtbot case studies featuring design and development projects for...](https://thoughtbot.com/case-studies)**
>
> Check out some thoughtbot case studies featuring design and development projects for websites, mobile and web applications across HealthTech, EdTech, FinTech B2B and B2C.

To do the intersection in SQL, you need to use a HAVING clause to match  
the number of entries with the number of items checked against. In this  
case, using squirrel:

required\_tags = [12, 16]  
amount = required\_tags.length  
items = Newsletter.find(:all,  
:group =\> “newsletters.id HAVING count(\*) = #{amount}”)  
do  
tags.id === required\_tags  
end
