Im stuck with an advanced query in rails. I need a solution that works in mongoid and if possible also active record (probably not possible). I've put together a simplified example below.
Consider the following model:
class Announcement
include Mongoid::Document
field :title, type: String
field :user_group, type: Array
field :year, type: Array
field :tags, type: Array
has_and_belongs_to_many :subjects
before_save :generate_tags
private
def generate_tags
tags = []
if self.subjects.present?
self.subjects.each { |x| tags << x.name.downcase.gsub(" ", "_") }
end
if self.year.present?
self.year.each { |x| tags << "year_" + x.to_s }
end
self.tags = tags
end
end
Given the tags array of document 1:
["hsc_mathematics", "hsc_chemistry", "year_9"]
And document 2:
["hsc_mathematics", "hsc_chemistry"]
And document 3:
["hsc_mathematics", "hsc_chemistry", "year_9", "year_10"]
And document 4:
["year_9", "year_10"]
Now consider the following model:
class Student < User
include Mongoid::Document
field :year, type: Integer
has_many :subjects
def announcements
tags = []
if self.subjects.present?
self.subjects.each { |x| subjects << x.name.downcase.gsub(" ", "_") }
end
tags << "year_" + self.year.to_s
Announcement.where("user_group" => { "$in" => ["Student", "all_groups"]}).any_of({"tags" => { "$in" => tags }}, {tags: []})
end
end
For the purpose of our example our student has the following tags:
[ "hsc_mathematics", "hsc_physics", "year_10" ]
My query is incorrect as I want to return documents 2, 3 and 4 but not document 1.
I need the query to adhere to the following when returning announcements:
i. If the announcement has subject tags match on any subject
ii. If the announcement has year tags match on any year
iii. If announcement has year and subject tags match on any year and any subject
How would I go about writing this?
EDIT
Im happy to split year out of my tags but im still stuck
Announcement.where("user_group" => { "$in" => ["Student", "all_groups"]}).any_of({"tags" => { "$in" => ["hsc_mathematics", "hsc_physics"] }}, {tags: []}).any_of({"year_at_school" => { "$in" => 10 }}, {year_at_school: []})
So the solution was to adjust my models and use a more organised query rather then an entire tag bank.
Announcement model:
class Announcement
include Mongoid::Document
field :title, type: String
field :user_group, type: Array, default: [""]
field :year, type: Array, default: [""]
field :tags, type: Array, default: [""]
has_and_belongs_to_many :subjects
before_save :generate_tags
private
def generate_tags
tags = []
if self.subjects.present?
self.subjects.each { |x| tags << x.name.downcase.gsub(" ", "_") }
end
self.tags = tags
end
end
User model:
class Student < User
include Mongoid::Document
field :year, type: Integer
has_many :subjects
def announcements
year = "year_" + self.year.to_s
tags = [""]
if self.subjects.present?
self.subjects.each { |x| tags << x.name.downcase.gsub(" ", "_") }
end
Announcement.where("user_group" => { "$in" => ["Student", ""] }).and("year" => { "$in" => [year, ""]}).any_in(tags: tags).all.entries
end
end
EDIT: Heres a neater version of the query as suggested
This example also has an expiry field which assumes nil = never expires
Announcement.where(:user_group.in => ["Student", ""], :year.in => [year, ""], :tags.in => tags).any_of({:expires_at.gte => Time.zone.now}, {:expires_at => nil}).all.entries
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.