1

On Facebook statues, you can start typing an @ and tag a user in a status. This question is not about the frontend, but rather how to store the data for that feature. What is the best way to achieve this functionality in a generic way for representing any mongodb entity in a string in a dynamic way. The goal being if the entity changes, it representation in the stored string also changes. For example one idea I had was this:

Post: {
    _id: "48ajsdlfhsdjfkjsljsd"
    name: "Post One",
    text: "@user is the best for liking @thing, and @thing"
    tags: [user:1234, thing:456, thing:789]
}

So I would load this post, then look at the tags, load the models for each tag type and id, then rewrite the string to be: "Chris is the best for liking StackOverflow, and Mongo". This seems inefficient, any better ideas?

4

2 回答 2

1

Your answer works Geoff, but I was looking for something a little more efficient. What I have done is store a document in Mongo like this

{
    _id: "4bdslakjghjdgkjsh123",
    title: "Post One",
    text: "@ is cool for liking @",
    tags: ["user:4bcasdkasd89", "product:4basfkjafkjlfl"]
}

And the code to translate the tags is run after loading a message. It basically uses the tag to know which type of model to use, and the id to load it. Then it creates an array of values using the as_tag method of a model, or to_s. This array is then substituted into the original string in order replacing the @ signs. A lot like sprintf. So it translates to "Chris is cool for liking bicycles"

def after_initialize
    tags = self.collect_tags
    tags.each do |value|
      self.text.sub!(/@/, value)
    end
end

def collect_tags
    self.tags.collect do |tag|
        model, id = tag.split(':')
        model[0] = model[0,1].upcase
        m = Models.const_get(model).find(id)
        if(m.respond_to? 'as_tag')
            m.as_tag
        else
            m.to_s
        end
    end
end

Any way to improve the efficiency here?

于 2012-04-22T23:53:43.930 回答
0

Whilst not being quite sure what you are trying to do, I suggest the following:

  1. Create a document that acts as your mapping between the "@" tags in the string and fields within the MongoDB document.

  2. Modify your Post document, so that the tags array contains standardized documents that both identify the _id values of the documents in which the real data is stored, and the field from which the data should be taken in these documents.

  3. (Optionally) Modify your Post document, to explicitly contain the mapping document _id. If there is a global mapping document this would be unnecessary, but

In your example, the mapping document might look like:

Mapping: { _id: "abc..", maps: [{tag:"user", field:"user.person.nickname"}, {tag:"thing", field:"object.name"}] }

And the post document would look like:

Post: { _id: "48ajsdlfhsdjfkjsljsd",
  name: "Post One",
  text: "@user is the best for liking @thing, and @thing",
  tags: [{tag:"user", docId:"pqr..."}, {tag:"thing", docId:"xzy..."}, {tag:"thing", docId:"mno..."}],
  mapping: "abc..."
}

You will then need to do the following to return the text string:

  1. Load the Post document
  2. Extract and load the Mapping document (if this mapping is global, this step is not required)
  3. Load all the documents corresponding to underlying data in one load:

    db.posts.find( { _id: { $in : [ "pqr...", "xyz...", "mno..." ] } })

  4. Retrieve the value from required document field from each of the located documents

  5. Replace the tag with the field value

This approach allows you to both change the meaning of the tag in the string (by changing the Mapping document - for example you could change @user from nickname to realname), and the value of the tag (by changing the docIds stored in the tags list within the Post document), but there are still a minimum of 4 steps to the string generation.

The approach does not handle the conditions where the Mapping document does not contain the tag, or when the tags list is not the same size as the number of tags in the text string.

于 2012-04-22T02:22:30.703 回答