質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.13%

DB内のキーワードとテキストの照合について

受付中

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,050

score 35

はじめまして、ruby on rails初めて1ヶ月と1日がたちました。

現在制作している掲示板もなんとか形になってきました。
今回は投稿されたテキスト内にDBに保存されているキーワードがあった場合、キーワードとしてリンク化(いわゆるタグ化)する実装の流れについてご教授いただきたいです。

いま、想定される問題は以下です。
・参照するDB内のキーワードの量が200を越すことが想定されます。リンクを生成し投稿完了画面への遷移時に遅延が発生しないか。

また、現在想定している実装の流れは以下です。
・参照用のDBの作成
・投稿テキストにDBのキーワードを総当て(ここの実装が難しいです)
・重複キーワードをviewにリンクタグとして表示する

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • moke

    2017/01/26 11:34

    では教えてください。サジェストなのかリコメンドなのか記事の重複防止なのか、はたまた検索補助なのか

    キャンセル

  • yuta_tokyo

    2017/01/26 11:38

    アーカイブへのリンクによるユーザービリティ向上です(リコメンドに近いきもしますが、一覧を見れる形になるのでリコメンドとはいいきれません)

    キャンセル

  • moke

    2017/01/26 11:45

    ユーザーとしてはかえって邪魔そうな機能だなーと思いますがとりあえず回答書きますね

    キャンセル

回答 1

+1

gem 'acts-as-taggable-on'
gem 'natto'
をgemfileに追加
bundle install
rake acts_as_taggable_on_engine:install:migrations
rake db::migrateを実行
Postモデルに
acts_as_taggable
を追加
Postモデルと

acts_as_taggable
before_save :standarize_text,:save_tags
def standarize_text
end
def save_tags
array=check_taggable_word(self.title)
self.tag_list.add(array)
end
def check_taggable_word(text)
ary=Array.new
      nm = Natto::MeCab.new
      nm.parse(text) do |n|
        ary<<n.surface
      end //mekabuで要素に分解
tags=ActsAsTaggableOn::Tag.pluck(:name)//タグを取得
return ary & tags //共通部分を返す
end


Commentモデルに

before_save :standarize_text,:save_tags
def standarize_text
重複や入力ブレを治すため文字を大文字とかに直す半角カナも直す、nkfとか使って見てください
end
def save_tags
array=check_taggable_word(self.comment)
self.post.tag_list.add(array)
end
def check_taggable_word(text)
ary=Array.new
      nm = Natto::MeCab.new
      nm.parse(text) do |n|
        ary<<n.surface
      end //mekabuで要素に分解
tags=ActsAsTaggableOn::Tag.pluck(:name)//タグを取得
return ary & tags //共通部分を返す
end


こんな感じですかね
mekabuライブラリで文章を単語に分解して作ったarrayと
tagに登録されている全ての単語のtagsの共通部分を取っています

あと新たなtagが追加されるたびに全てのコメントにそのタグがないかチェックして
追加する機能が必要です。
パッチを追加してact_as_taggle_onのtag classを拡張して見てください

module ActAsTaggleOnPatch
module TagPatch
 def self.included(base)
   base.extend(ClassMethods)
   base.send(:include, InstanceMethods)
   base.class_eval do
     after_commit :set_this_tag_for_post 
     end
   end
  module ClassMethods
  end

  module InstanceMethods
    def set_this_tag_for_post
       regexp=Regexp.new(self.name)
       Post.all.each do |post|

       tag=post.comment.match(regexp)
       if tag
        post.tag_list.add(self.name)
        post.comments.each do |comment|
        c=comment.comment.match(regexp)
         if c
          post.tag_list.add(self.name)
          brake
         end
       end

end

end
end


もちろんinitializer.rbに

ActsAsTaggableOn::Tag.send(:include,ActsAsTaggableOnPatch::TagPatch)


を入れてください

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/10 09:34

    self.tag_list.add(array)はtaggingsに追加されます。重複はしないようです。
    tagsにはあるか確認してなければ追加という形です。
    ActsAsTaggableOn::Tagにcreate_allとかできるんじゃないですか?
    無理ならふつうにActsAsTaggableOn::Tag.create(:name=>'hoge')とかをeachで回わせばいいのでは?
    最終手段としてsqlserverにexcelの値を貼り付けるとか
    まあ、なんにせよ、この最終手段を試してから質問してください

    キャンセル

  • 2017/02/10 09:37

    >>わざわざtagに追加せず、重複tagを表示させたいのですが、いかがでしょうか?
    もはや意図の分からない質問には答えかねます。
    ベアプログラミングというのがあります試してはいかがでしょう?

    キャンセル

  • 2017/02/16 18:12 編集

    おつかれさまです!自分でそのあといろいろ試してみたのですが、要素分解と重複確認まではうまくいったのですが、重複したものをtaggingテーブルに保存することと、viewにリンクを表示させることができません。

    また何故かpost.save_tagsをコンソール上(以下コード)で二度行うと一度目はエラーがでますが、二度目は成功します。なぜ一度で成功できないのでしょうか?

    上記二点アドバイス頂きたく思います!


    ////////コンソール
    irb(main):001:0> post = Post.find_by(id:61)
    Post Load (0.2ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ? [["id", 61], ["LIMIT", 1]]
    => #<Post id: 61, title: "タグと太陽、テスト", name: "あああ", created_at: "2017-02-14 01:21:03", updated_at: "2017-02-17 01:21:03", category_id: 1, ip: "::1">
    irb(main):002:0> post
    => #<Post id: 61, title: “タグと太陽、テスト”, name: "あああ", created_at: "2017-02-14 01:21:03", updated_at: "2017-02-17 01:21:03", category_id: 1, ip: "::1">
    irb(main):003:0> post.save_tags
    (0.2ms) SELECT "tags"."name" FROM "tags"
    ActsAsTaggableOn::Tagging Load (0.2ms) SELECT "taggings".* FROM "taggings" WHERE "taggings"."taggable_id" = ? AND "taggings"."taggable_type" = ? [["taggable_id", 61], ["taggable_type", "Post"]]
    ActsAsTaggableOn::Tag Load (0.2ms) SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = ? AND "taggings"."taggable_type" = ? AND (taggings.context = 'tags' AND taggings.tagger_id IS NULL) [["taggable_id", 61], ["taggable_type", "Post"]]
    []
    "ポストタグテスト中 save_tags"
    => "ポストタグテスト中 save_tags"
    irb(main):004:0> post.save_tags
    (0.1ms) SELECT "tags"."name" FROM "tags"
    [“タグ”, “太陽”]
    "ポストタグテスト中 save_tags"
    => "ポストタグテスト中 save_tags"

    /////post.rb(self.check_taggable_wordにしています)
    def save_tags
    array = self.check_taggable_word(self.title)
    p self.tag_list
    self.tag_list.add(array) #postにのみつける場合はここを変更
    #self.save
    p "ポストタグテスト中 save_tags"
    end
    def check_taggable_word(text)
    ary = Array.new
    nm = Natto::MeCab.new
    nm.parse(text) do |n|
    ary<<n.surface
    end #mekabuで要素に分解
    tags = ActsAsTaggableOn::Tag.pluck(:name) #タグを取得
    return ary & tags #共通部分を返す
    p "ポストタグテスト中 check_taggable_word"
    end

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.13%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る