How to implement post tags in Mongo?
Asked Answered
L

1

25

I'm toying with Mongo to make a SO-like pet project, and I want to implement post tags. Each tag has a name and a slug (string to be used as an id in the URL), and a post has multiple tags. I'd like to be able to create queries like "find posts, which have tag A, don't have tag B", and I'm wondering what's the mongo-way to do this.

One way is to store an array of tag ids with each post - this will make the said query easy, but will require an extra one for each post to get tag name and a slug. Another way is to store an array of [tag name, tag slug] with each post, but I'm not sure I'll be able to use that info in a find.

Is there some other method, which will work better for mongo? I'm new to NoSQL, so I'd appreciate any advise on how this can be accomplished. Also, I'm using PHP binding, but that shouldn't matter probably.

Lounge answered 10/12, 2011 at 9:53 Comment(0)
A
47

If the tags you use and their respective slugs are unlikely to change, I think your second approach is the better one. However I would suggest a small change - rather than storing an array of [name, slug], make the fields explicit by creating a tag subdocument as in this example post document:

{
    "_id" : ObjectId("4ee33229d8854784468cda7e"),
    "title" : "My Post",
    "content" : "This is a post with some tags",
    "tags" : [
        {
            "name" : "meta",
            "slug" : "34589734"
        },
        {
            "name" : "post",
            "slug" : "34asd97x"
        },
    ]
}

You can then query for posts with a particular tag using dot notation like this:

db.test.find({ "tags.name" : "meta"})

Because tags is an array, mongo is clever enough to match the query against any element of the array rather than the array as a whole, and dot-notation allows you to match against a particular field.

To query for posts not containing a specific tag, use $ne:

db.test.find({ "tags.name" : { $ne : "fish" }})

And to query for posts containing one tag but not the other, use $and:

db.test.find({ $and : [{ "tags.name" : { $ne : "fish"}}, {"tags.name" : "meta"}]})

Hope this helps!

Arillode answered 10/12, 2011 at 10:31 Comment(7)
is it necessary to add the slug in the tags? or is it okay to just have an array of values?Wareroom
I think the slug just happened to be an implementation detail of the OPs approach. You don't need it.Arillode
Is there a query that would return all tags (like if you wanted to make a tag cloud)? EDIT: Looks like this might work: https://mcmap.net/q/538704/-get-a-list-of-all-unique-tags-in-mongodbHeadward
What about the index?Constitutive
Slugs are important to search in database, its remove the necessity to work with regex (accents, lower/upper case, spaces, dashes, etc).Mardis
what if I want to rename a tag? I need to update all the documents that are using the tag?Fredric
@Fredric yes, you have to. There is no general advice how you should implement a tagging system. It really depends on your requirements. As suggested a slug or an extra schema can solve this issue for you. But the drawback is you have to query the database again, to get the actual name of those tags. So it really depends on your needs and which actions will be the most.Amalgam

© 2022 - 2024 — McMap. All rights reserved.