You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails ActiveRecord:筛选符合多条件的可投标职位

Alright, let's tackle this problem. I'll start by assuming standard model associations for a freelance platform like TaskRabbit/Upwork—if your schema differs a bit, you can tweak these to fit your setup.

First, Let's Define the Core Models & Associations

These are the typical relationships you'd need for this functionality:

# app/models/job.rb
class Job < ApplicationRecord
  has_many :bids, dependent: :destroy
  has_many :job_skills, dependent: :destroy
  has_many :skills, through: :job_skills
  # Assumes your job has a :start_date (datetime/date field)
end

# app/models/worker.rb
class Worker < ApplicationRecord
  has_many :bids, dependent: :destroy
  has_many :worker_skills, dependent: :destroy
  has_many :skills, through: :worker_skills
end

# app/models/bid.rb
class Bid < ApplicationRecord
  belongs_to :job
  belongs_to :worker
  # Assumes a boolean :accepted field to track client-approved bids
end

# app/models/skill.rb
class Skill < ApplicationRecord
  has_many :job_skills
  has_many :jobs, through: :job_skills
  has_many :worker_skills
  has_many :workers, through: :worker_skills
end

# Join models (required for many-to-many skill relationships)
# app/models/job_skill.rb
class JobSkill < ApplicationRecord
  belongs_to :job
  belongs_to :skill
end

# app/models/worker_skill.rb
class WorkerSkill < ApplicationRecord
  belongs_to :worker
  belongs_to :skill
end

Building the Query Step-by-Step

We'll chain ActiveRecord scopes to enforce all four conditions efficiently:

1. Filter Jobs with Future Start Dates

Straightforward—we only want jobs that haven't started yet. Use Time.current for datetime fields, or Date.today if your start_date is a date-only field:

future_jobs = Job.where("start_date > ?", Time.current)

2. Exclude Jobs the Worker Has Already Bidded On

We need to rule out any jobs where the worker has submitted a bid. Using an exists subquery is more performant than where.not(id: ...) because the database only checks for existence, not returns all matching IDs:

unbidded_jobs = future_jobs.where.not(
  Bid.where("jobs.id = bids.job_id AND bids.worker_id = ?", current_worker.id).exists
)

3. Exclude Jobs Where a Bid Has Already Been Accepted

We want to ignore jobs where the client has already picked a worker. Again, using exists keeps this efficient:

open_jobs = unbidded_jobs.where.not(
  Bid.where("jobs.id = bids.job_id AND bids.accepted = ?", true).exists
)
# Alternative: If your Job model uses an `accepted_bid_id` foreign key instead:
# open_jobs = unbidded_jobs.where(accepted_bid_id: nil)

4. Filter Jobs Matching the Worker's Skills

We need to ensure the worker has at least one skill required by the job. An exists subquery works best here for large datasets:

qualified_jobs = open_jobs.where(
  JobSkill.joins(:skill)
          .where("job_skills.job_id = jobs.id AND skills.id IN (?)", current_worker.skill_ids)
          .exists
)
# If you want to include jobs with no required skills, adjust this to:
# qualified_jobs = open_jobs.where(
#   "(SELECT COUNT(*) FROM job_skills WHERE job_skills.job_id = jobs.id) = 0 OR " \
#   "(SELECT EXISTS(SELECT 1 FROM job_skills JOIN skills ON job_skills.skill_id = skills.id WHERE job_skills.job_id = jobs.id AND skills.id IN (?)))", current_worker.skill_ids
# )

Full Combined Query (With Scopes for Cleanliness)

Wrap these conditions into scopes in the Job model to keep your code reusable:

# app/models/job.rb
scope :future, -> { where("start_date > ?", Time.current) }
scope :not_bidded_by, ->(worker) { where.not(Bid.where("jobs.id = bids.job_id AND bids.worker_id = ?", worker.id).exists) }
scope :no_accepted_bid, -> { where.not(Bid.where("jobs.id = bids.job_id AND bids.accepted = ?", true).exists) }
scope :matches_worker_skills, ->(worker) { where(JobSkill.joins(:skill).where("job_skills.job_id = jobs.id AND skills.id IN (?)", worker.skill_ids).exists) }

Now you can call this cleanly in your controller:

# In your workers/jobs_controller.rb
def index
  @available_jobs = Job.future
                       .not_bidded_by(current_worker)
                       .no_accepted_bid
                       .matches_worker_skills(current_worker)
end

Performance Tips

Add these indexes to speed up the queries (create a migration for this):

add_index :jobs, :start_date
add_index :bids, [:job_id, :worker_id]
add_index :bids, [:job_id, :accepted]
add_index :job_skills, [:job_id, :skill_id]
add_index :worker_skills, [:worker_id, :skill_id]

内容的提问来源于stack exchange,提问作者Mike N.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 11:13:19