POSTGRESQLDBに対してクエリを実行しています。私のアプリには記事があり、記事にはいくつかのハッシュタグを付けることができます。これらの関係は、ハッシュタグと記事の結合されたテーブルに保存されます。
特定のハッシュタグを含む記事を返す、または特定のハッシュタグを含まないすべての記事を返す作業方法があります
def test(hashtags, include = true)
articles= []
hashtags.split(' ').each do |h|
articles+= Article.joins(:hashtags).where('LOWER(hashtags.value) LIKE LOWER(?)', "#{h}")
end
if include
articles.uniq
else
(Article.all.to_set - articles.uniq.to_set).to_a
end
end
私はそれをこのように呼ぶことができます:
test("politics people china", true)
そしてそれは私にそれらのハッシュタグの1つを持っているすべての記事を与えるでしょう
または私はそれをそのように呼ぶことができます
test("politics people china", false)
そしてそれは私にこれらのハッシュタグの1つを持っている人を除いてすべての記事を与えるでしょう
それはうまく機能しますが、DBレベルではなくRubyで多くのことを行うので、これはあまり効率的ではないと思います。
私はこれを試しました:
def test2(hashtags, include = true)
articles= []
pattern = ''
hashtags.split(' ').each do |h|
pattern += "#{h}|"
end
pattern = '(' + pattern[0...-1] + ')'
if include
articles = Article.joins(:hashtags).where('hashtags.value ~* ?', "#{pattern}")
else
articles = Article.joins(:hashtags).where('hashtags.value !~* ?', "#{pattern}")
end
articles.uniq
end
しかし、思ったようには動作しません。まず第一に、私がそれをそのように呼ぶならば:
test2("politics china", true)
ハッシュタグpolitics
またはを持っているすべての記事だけでなく、次のようなchina
文字の1つを含むハッシュタグを持っているすべての記事も表示されます。politics
china
(p|o|l|i|t|c|s|h|n|a)
しかし、実際にこれをチェックする必要があり、パターンは実際には次のようになります。コンソールに表示されます。
(politics|china)
私が見つけたものは奇妙なtbhではありません...
そしてと
test2("politics", false)
1つ以上のハッシュタグが関連付けられている記事のみが表示されますが、ハッシュタグがまったくない記事は除外されます
誰かが私の作業方法をより効率的にするのを手伝ってもらえますか?
編集:これは答えで提案されたような私の更新されたコードです
def test2(hashtags, include = false)
hashtags =
if include
Hashtag.where("LOWER(value) iLIKE ANY ( array[?] )", hashtags)
else
Hashtag.where("LOWER(value) NOT iLIKE ANY ( array[?] )", hashtags)
end
Slot.joins(:hashtags).merge(hashtags).distinct
end
incude
残念ながら偽の場合、ハッシュタグがまったくない記事を私に与えることはまだ不足しています
あなたは正しいです
私はDBレベルではなくRubyで多くのことをしているので、これは非常に効率的だとは思いません。
ActiveRecordは単純なクエリには適していますが、物事が複雑になっている場合は、プレーンSQLを使用するのが妥当です。それでは、テストケースに一致するクエリを作成してみましょう。
1)この呼び出しtest("politics people china", true)
の場合、クエリは次のようになります。
SELECT DISTINCT ON (AR.id) AR.*
FROM articles AR
JOIN articles_hashtags AHSH ON AHSH.article_id = AR.id
JOIN hashtags HSH ON HSH.id = AHSH.hashtag_id
WHERE LOWER(HSH.value) IN ('politics', 'people', 'china')
ORDER BY AR.id;
(結合テーブルの名前がわからないので、名前が付けられていると仮定しますarticles_hashtags
)。
無地でシンプルに:私たちは、からデータを取りarticles
、内2と合流使用してテーブルarticles_hashtags
とhashtags
し、where
私たちが見てみたいハッシュタグをフィルタリング条件を、。そして最終的には、そのハッシュタグが付いたすべての記事が表示されます。フィルタリングするIN
ハッシュタグの数に関係なく、リストにハッシュタグが1つしかない場合でも、ステートメントは適切に機能します。
DISTINCT ONに注意してください:同じ記事が特定のハッシュタグリストから複数のハッシュタグを持っている場合、結果セットから重複する記事を削除するために必要です。
2)呼び出しの場合test("politics people china", false)
、クエリはもう少し複雑です。ハッシュタグを付けた記事は除外する必要があります。したがって、ハッシュタグの異なる記事だけでなく、ハッシュタグのない記事も返す必要があります。物事を単純に保つために、前のクエリを使用できます。
SELECT A.*
FROM articles A
WHERE A.id NOT IN (
SELECT DISTINCT ON (AR.id) AR.id
FROM articles AR
JOIN articles_hashtags AHSH ON AHSH.article_id = AR.id
JOIN hashtags HSH ON HSH.id = AHSH.hashtag_id
WHERE LOWER(HSH.value) IN ('politics', 'people', 'china')
ORDER BY AR.id
);
ここではすべての記事を取得していますが、特定のハッシュタグのいずれかを持っている記事を取得しています。
3)これらのクエリをRubyメソッドに変換すると、次のようになります。
def test3(hashtags, include = true)
# code guard to prevent SQL-error when there are no hashtags given
if hashtags.nil? || hashtags.strip.blank?
return include ? [] : Article.all.to_a
end
basic_query = "
SELECT DISTINCT ON (AR.id) AR.*
FROM #{Article.table_name} AR
JOIN articles_hashtags AHSH ON AHSH.article_id = AR.id
JOIN #{Hashtag.table_name} HSH ON HSH.id = AHSH.hashtag_id
WHERE LOWER(HSH.value) IN (:hashtags)
ORDER BY AR.id"
query = if include
basic_query
else
"SELECT A.*
FROM #{Article.table_name} A
WHERE A.id NOT IN (#{basic_query.sub('AR.*', 'AR.id')})"
end
hashtag_arr = hashtags.split(' ').map(&:downcase) # to convert hashtags string into a list
Article.find_by_sql [query, { hashtags: hashtag_arr }]
end
上記のメソッドは、空かどうかに関係なく、条件に一致する記事の配列を返します。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加