我正试图在我的Rails 3应用程序中加载.我把它缩小到一个非常基本的样本,而不是生成我期望的一个查询,它产生4.
首先,这是我的模型的简单细分.
class Profile < ActiveRecord::Base
belongs_to :gender
def to_param
self.name
end
end
class Gender < ActiveRecord::Base
has_many :profiles, :dependent => :nullify
end
Run Code Online (Sandbox Code Playgroud)
然后我有一个ProfilesController :: show动作,我在那里查询模型.
def ProfilesController < ApplicationController
before_filter :find_profile, :only => [:show]
def show
end
private
def find_profile
@profile = Profile.find_by_username(params[:id], :include => :gender)
raise ActiveRecord::RecordNotFound, "Page not found" unless @profile
end
end
Run Code Online (Sandbox Code Playgroud)
当我查看生成的查询时,它显示以下内容:
SELECT `profiles`.* FROM `profiles` WHERE `profiles`.`username` = 'matt' LIMIT 1
SELECT `genders`.* FROM `genders` WHERE (`genders`.`id` = 1)
Run Code Online (Sandbox Code Playgroud)
我期望看到的是一个查询:
SELECT `profiles`.*, `genders`.* …Run Code Online (Sandbox Code Playgroud) ruby-on-rails query-optimization eager-loading ruby-on-rails-3
我添加了项目符号 gem来建议我开发中的任何 N+1 查询(Rails 4.0.2),它建议急切地加载模型的两个父关联,在其中显示其所有记录。
在急切加载这些所属关联之一之前:
Completed 200 OK in 5252ms (Views: 1.8ms | ActiveRecord: 114.1ms)
Run Code Online (Sandbox Code Playgroud)
急切加载后:
Completed 200 OK in 6741ms (Views: 2.1ms | ActiveRecord: 146.0ms)
Run Code Online (Sandbox Code Playgroud)
此外,通过急切加载,浏览器会在控制台显示已完成后挂起,并且在一段时间内(大约 6 秒)不会更新。服务器生产没有这种冻结问题,但在这种情况下,急切加载仍然是一个糟糕的建议。
急切加载会变慢是否有意义?该视图确实访问(预先加载的)父记录。
此外,由于某种原因,子弹宝石不显示调用堆栈。
我有这个存储库:
public class Repository<T> : IRepository<T> where T : class
{
private readonly DbContext context;
private readonly DbSet<T> dbEntitySet;
public Repository(DbContext context)
{
if (context == null)
throw new ArgumentNullException("context");
this.context = context;
this.dbEntitySet = context.Set<T>();
}
public IEnumerable<T> GetAll()
{
return this.dbEntitySet;
}
public IEnumerable<T> GetAll(string include)
{
return this.dbEntitySet.Include(include);
}
public IEnumerable<T> GetAll(string[] includes)
{
IQueryable<T> query = context.Set<T>();
foreach (var include in includes)
query.Include(include);
return query;
}
public void Create(T model)
{
this.dbEntitySet.Add(model);
}
public void Update(T …Run Code Online (Sandbox Code Playgroud) 我在Laravel模型上有一个雄辩的关系,它是动态的 - 也就是说,特定数据库字段的值决定了哪个模型将被加载.当我第一次实例化模型实例然后引用关系时,我能够很好地加载这种关系,但是当我急切地加载这种关系时它不起作用.
具体来说,我有一个Product模型.该产品可能是也可能不是其他产品的父母.如果parent_id产品的设置被设置为0那么该产品被视为父产品(无论是否有孩子).如果将parent_id其设置为其他产品的ID,则该产品是子产品.我需要能够访问Product::with('parent')和知道的parent关系将返回要么本身(是的,重复的数据),或者不同的产品,如果它是一个孩子.
这是我到目前为止的关系:
public function parent()
{
if ($this->parent_id > 0) {
return $this->belongsTo('App\Product', 'parent_id', 'id');
} else {
return $this->belongsTo('App\Product', 'id', 'id');
}
}
Run Code Online (Sandbox Code Playgroud)
当我急切加载时,$this->parent_id总是未定义,因此即使它实际上是父产品,这种关系也只会自行返回.
在关系被急切加载之前,有没有办法访问模型的属性?在我返回关系之前,我考虑过在单独的查询中工作,但我意识到我甚至无法访问产品的id来运行该查询.
如果不可能,还有哪些其他方法可以解决这类问题?这似乎不能通过传统的多态关系来解决.我只有两个可能的想法:
belongsTo在我动态确定外键的关系中添加某种约束.老实说,我不知道如何实现其中任何一个.我是以正确的方式来做这件事的吗?有什么我可以忽略的吗?
在考虑了这个之后,我认为提出问题的最简单方法是:有没有办法在运行时为关系本身内部的关系动态选择外键?当我调用关系时,我的用例不允许我使用急切的加载约束 - 约束需要应用于关系本身.
想象一组提案,其中每个用户都可以对提案投赞成票或反对票。
所以我有2个模型:
Proposal,Vote,与 a proposal、 auser和 an opinion(向上或向下)相关。现在我想显示我的所有提案,包括每个提案的额外信息:
这真的很容易实现,但这意味着每个提案显示 4 个请求(称为 n+1 查询问题)。我来自 Rails 世界,在那里可以使用预先加载或一些棘手的查询轻松解决此问题。但是我无法用教义解决它,需要一些帮助!
第一种解决方案是进行自定义查询:选择所有提案,包括赞成票、反对票的数量,当前用户是否赞成或反对。
查询现在返回4分列比模型来描述:count_upvotes,count_downvotes,has_user_upvoted和has_user_downvoted。
为了与学说一起使用,我必须将这些字段添加到我的Proposal实体中。
对我来说,这不是一个好的解决方案,因为它意味着始终使用此查询来避免混蛋模型,其中这些字段可以为空。
第二种解决方案是让视图使用另一个对象。这个对象是用一个参数创建的:当前用户。该对象由优化查询创建,包含以下方法:
getAllProposals()getUpvotes($a_proposal)getDownvotes($a_proposal)hasUserUpvoted($a_proposal)hasUserDownvoted($a_proposal)对我来说,仅仅为了视图优化而被迫创建一个对象真的太过分了。
这第三个解决方案使用唯一的查询来获取提案、他们的赞成票、反对票,并检查用户是否赞成或反对。
它创建了一个新的“扩展”实体,
public function __construct(
Proposal $badgeProposal,
$upvotesCount,
$downvotesCount,
$hasUserUpvoted,
$hasUserDownvoted
) {
Run Code Online (Sandbox Code Playgroud)
对我来说,这是更好的解决方案,但目前这行不通,因为我不知道如何简单地从 SQL 行中获取建议(但我几乎没有搜索)。
那么,还有什么其他解决方案呢?对于学说开发人员来说,是必须是一个众所周知的问题。
我有以下代码来检索给定部分的线程及其评论及其喜欢。它有效,但我想对结果进行分页。
return $this->threads()->where('state', '=', 'active')->with('comments.likes')->get();
Run Code Online (Sandbox Code Playgroud)
一种方法是这样做,但它会导致大量查询,因为它不急于加载。
return $this->threads()->where('state', '=', active)->paginate(5);
Run Code Online (Sandbox Code Playgroud)
有什么方法可以急切加载所有数据并利用 Laravel 的分页魔法?
假设我有 User 1-n 和 UserNotification
问题是我不知道为什么每次我尝试保存 UserNotification 时,rails auto SELECT the user of that UserNotification 都会触发 n + 1 问题
例如
UserNotification.where(id: [1,2,3]).update(updated_at: Time.current)
Run Code Online (Sandbox Code Playgroud)
将生成这些 SQL
UserNotification Load (0.4ms) SELECT `user_notifications`.* FROM `user_notifications` WHERE `user_notifications`.`id` IN (1, 2, 3)
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` IN (3, 1)
User Load (0.2ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 4
Run Code Online (Sandbox Code Playgroud)
UserNotification 模型很简单belongs_to :user
我已经阅读了更新源代码,但仍然不知道
有任何想法吗 ?
我正在使用Bullet gem来查看应用程序中的n + 1个查询。它告诉我taggings在调用序列化程序时急于加载我的关联。我的代码如下所示:
render json: @products, each_serializer: ::V1::ProductSerializer, includes: [:taggings], links: links, status: :ok
Run Code Online (Sandbox Code Playgroud)
但是添加完之后,我仍然从Bullet gem得到同样的警告。看起来像这样:
GET /api/v1/product_feed?state=CA&page=1
USE eager loading detected
Product => [:taggings]
Add to your finder: :includes => [:taggings]
Call stack
/home/jay/current_projects/api/app/controllers/api/v1/products_controller.rb:111:in `product_feed'
Run Code Online (Sandbox Code Playgroud)
有谁知道为什么不急于加载标签表。
这在 Software Engineering SE 中有一个兄弟问题。
考虑Company,Product和Person。
Company和之间存在多对多的关系Product,通过一个联结表Company_Product,因为给定的公司可能生产不止一种产品(如“汽车”和“自行车”),但也可能生产一种给定的产品,如“汽车” , 可由多家公司生产。在连接表中Company_Product有一个额外的字段“价格”,它是给定公司销售给定产品的价格。
还有另一种之间的许多一对多的关系Company_Product,并Person通过结合表Company_Product_Person。是的,它是一种多对多关系,涉及一个已经是联结表的实体。这是因为一个人可以拥有多种产品,例如公司 1 的汽车和公司 2 的自行车,而同一个 company_product 可以由多个人拥有,因为例如,person1 和 person2 都可以从公司1. 在连接表中Company_Product_Person有一个额外的字段“想法”,其中包含购买 company_product 的人的想法。
我想和查询sequelize从数据库中获得的所有实例Company,与所有相关的Products与各Company_Product后者又包括所有相关Persons与各自的Company_Product_Persons。
获取两个连接表的元素也很重要,因为字段“价格”和“想法”很重要。
我无法弄清楚如何做到这一点。
为了调查这个问题,我尽可能缩短了代码。看起来很大,但大部分都是模型声明样板:(要运行它,先做npm install sequelize sqlite3)
const Sequelize = require("sequelize");
const sequelize = new Sequelize({ dialect: …Run Code Online (Sandbox Code Playgroud) 鉴于我有两个雄辩的模型:预订和客户。
当我将所有预订与相应客户一起列出时,我还想显示相应客户的总预订量(此预订的数量 + 所有其他预订)。
示例输出:
为了避免 n+1 问题(显示此内容时每个预订额外查询一个),我想bookingsCount为客户急切加载。
关系是:
预订: public function customer() { return $this->belongsTo(Customer::class) }
顾客: public function bookings() { return $this->hasMany(Booking::class) }
使用预先加载查询预订的示例
工作,但没有急切地加载bookingsCount:
Booking::whereNotCancelled()->with('customer')->get();
Run Code Online (Sandbox Code Playgroud)
不工作:
Booking::whereNotCancelled()->with('customer')->withCount('customer.bookings')->get();
Run Code Online (Sandbox Code Playgroud)
我了解到,您不能withCount在相关模型的字段上使用,但您可以创建一个hasManyThrough关系并调用withCount该关系,例如Booking::whereNotCancelled()->withCount('customerBookings');(请参阅此处接受的答案)。
但是:这不起作用。我想,这是因为一个预订属于一个客户,而一个客户有许多预订。
这是类 Booking 的 hasManyThrough 关系
public function customerBookings()
{
// return the bookings of this booking's customer
return $this->hasManyThrough(Booking::class, Customer::class);
}
Run Code Online (Sandbox Code Playgroud)
这是 hasManyThrough 的失败测试
/**
* @test …Run Code Online (Sandbox Code Playgroud) eager-loading ×10
laravel ×3
eloquent ×2
php ×2
associations ×1
c# ×1
doctrine ×1
javascript ×1
laravel-5 ×1
many-to-many ×1
node.js ×1
pagination ×1
rails-bullet ×1
relationship ×1
ruby ×1
sequelize.js ×1
sql ×1
symfony ×1