Question

I have the following entities:

@Entity
public class Article{
    @Id
    private String id;

    @ManyToMany(cascade = CascadeType.PERSIST)
    private Set<Tag> tags;

//non-relevant code
}

@Entity 
public class Tag{
    @Id
    private String id;

    @Basic
    @Column(nullable = false, unique = true, length = 32)
    private String name;

//non-relevant code
}

How can I efficiently find all Article entities that have a common set of tags?

The naive approach is to find all the articles that belong to each tag and then return the intersection of all the article sets. Something like:

public Set<Article> findByTags(Set<Tag> tags){
    Set<Article> result = new HashSet<>();

    if(tags.isEmpty()){
        return result;
    }

    Iterator<Tag> i = tags.iterator();
    result.addAll(i.next().getArticles());

    while(i.hasNext() && !result.isEmpty()){
        result.retainAll(i.next());
    }

    return result;
}

My question is "Is there more efficient way of doing this, that does not require to fetch possibly all the articles from the DB, like this one ? Maybe through a JPQL query or using the CriteriaBuilder (I've never used it before)"

Was it helpful?

Solution

select a from Article a where :numberOfTags = 
    (select count(distinct tag.id) from Article a2
     inner join a2.tags tag
     where tag in :tags
     and a = a2)

This basically counts the tags of the articles that are tags in the accepted set of tags, and if the number of such tags is eaqual to the size of the accepted set of tags (meaning that all tags in the set are tags of the article), returns the article.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top