EntityFieldQuery có thực sự kém hiệu quả không?


11

Tôi là một người mới thừa nhận API thực thể, nhưng tôi đang cố gắng khắc phục điều đó. Tôi đang làm việc trên một trang web sử dụng một số loại nội dung với các trường khác nhau được đính kèm; không có gì lạ mắt. Vì vậy, khi tôi muốn lấy một tập hợp các mục, tôi đã, trong sự thiếu hiểu biết của mình, gọi trực tiếp vào cơ sở dữ liệu và làm một cái gì đó như thế này:

$query = db_select('node', 'n')->extend('PagerDefault');
$query->fields('n', array('nid'));
$query->condition('n.type', 'my_content_type');

$query->leftJoin('field_data_field_user_role', 'role', 'n.nid = role.entity_id');
$query->condition('role.field_user_role_value', $some_value);

$query->leftJoin('field_data_field_withdrawn_time', 'wt', 'n.nid = wt.entity_id');
$query->condition('wt.field_withdrawn_time_value', 0);

$query->orderBy('n.created', 'desc');

$query->limit(10);

$result = $the_questions->execute()->fetchCol();

(vâng, tôi có thể có thể thu gọn một loạt các dòng này thành một $the_questions->câu lệnh; xin vui lòng bỏ qua điều đó ngay bây giờ.)

Cố gắng viết lại điều này với EntityFieldQuery, tôi nghĩ ra:

$query = new EntityFieldQuery();
$query
  ->entityCondition('entity_type', 'node')
  ->entityCondition('bundle', 'my_content_type')
  ->fieldCondition('field_user_role', 'value', $some_value)
  ->fieldCondition('field_withdrawn_time', 'value', 0)
  ->propertyOrderBy('created', 'desc')
  ->pager(10);

$result = $query->execute();

if (isset($result['node'])) {
    $result_nids = array_keys($result['node']);
}
else {
    $result_nids = array();
}

mang lại cho tôi kết quả mong muốn và chắc chắn là đẹp hơn nhiều.

Vì vậy, bây giờ tôi đang tự hỏi về hiệu suất. Khi bắt đầu, tôi ném từng đoạn mã đó vào một for()vòng lặp ngu ngốc , thu giữ time()trước và sau khi thực hiện. Tôi chạy mỗi phiên bản 100 lần trên một cơ sở dữ liệu không lớn lắm và nhận được một cái gì đó như thế này:

  • Phiên bản trực tiếp: 110 msec
  • Phiên bản EFQ: 4943 msec

Rõ ràng tôi nhận được kết quả khác nhau khi tôi chạy lại bài kiểm tra, nhưng kết quả luôn nằm trong cùng một sân bóng.

Rất tiếc. Tôi đang làm gì đó sai ở đây, hay đây chỉ là chi phí sử dụng EFQ? Tôi chưa thực hiện bất kỳ điều chỉnh cơ sở dữ liệu đặc biệt nào liên quan đến các loại nội dung; chúng chỉ là những gì xuất phát từ việc xác định các loại nội dung theo cách thông thường, dựa trên hình thức. Có suy nghĩ gì không? Mã EFQ chắc chắn sạch hơn, nhưng tôi thực sự không nghĩ rằng mình có thể đủ khả năng đạt hiệu suất 40 lần.


3
bạn có thể kết xuất cả hai truy vấn sql được tạo ra?
Andre Baumeier

1
Xem cái này nếu bạn không chắc chắn làm cách nào để đưa SQL ra khỏi EFQ
Clive

2
OK, có tiến triển: Điều đang diễn ra ở đây là trang web của tôi có một loạt các quy tắc truy cập nút đang tăng kích thước của truy vấn khá nhiều. Chúng được tự động áp dụng cho truy vấn EFQ (mặc dù không có ->addTag('node_access')trong truy vấn ??). Tôi chạy lại truy vấn "trực tiếp" bằng thẻ node_access và thời gian thực hiện gần hơn rất nhiều: Thời gian của EFQ chỉ bằng khoảng 2 lần so với cách tiếp cận trực tiếp, có vẻ hợp lý với SQL tương đối mà cả hai đang bơm ra (mà Tôi có thể đăng nếu mọi người vẫn quan tâm). (tiếp theo bình luận tiếp theo ....)
Jim Miller

Vì vậy, bây giờ câu hỏi, tôi đoán, là tại sao tôi tự động nhận được các công cụ node_access trong phiên bản EFQ? Tôi nghĩ bạn phải yêu cầu rõ ràng thông qua mệnh đề addTag () ??
Jim Miller

Câu trả lời:


10

Các EntityFieldQuerylớp là hiệu quả như các yêu cầu của nó cho phép nó được. Nó cần phải tương thích với bất kỳ lớp lưu trữ trường nào, ngay cả với những lớp sử dụng công cụ NoQuery để lưu trữ dữ liệu trường, chẳng hạn như lớp sử dụng MongoDB . Vì lý do đó, EntityFieldQuerykhông thể truy vấn trực tiếp cơ sở dữ liệu, vì phụ trợ lưu trữ trường hiện tại có thể không sử dụng cơ sở dữ liệu SQL.

Ngay cả trong trường hợp bộ lưu trữ trường sử dụng công cụ SQL để lưu trữ dữ liệu của nó, tương đương $query->leftJoin('field_data_field_user_role', 'role', 'n.nid = role.entity_id'); $query->condition('role.field_user_role_value', $some_value);với EntityFieldQuerylớp yêu cầu:

  • Mã để xây dựng tên bảng cơ sở dữ liệu từ tên trường
  • Mã để xây dựng điều kiện sử dụng để tham gia bảng chứa dữ liệu trường với bảng chứa dữ liệu thực thể
  • Mã để xây dựng tên của hàng cơ sở dữ liệu chứa dữ liệu trường

Sự khác biệt có thể nhìn thấy ngay lập tức: Trong một trường hợp, bạn đang sử dụng ba chuỗi rác, trong khi trong trường hợp khác có mã (trong trường hợp đơn giản nhất) là chuỗi nối.

Theo nhận xét của bạn về mã kiểm tra xem người dùng có quyền truy cập vào các trường hay không, bạn có thể bỏ qua việc sử dụng dòng sau vào mã bằng EntityFieldQuerylớp.

$query->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT');

Điều này hoạt động nếu bạn đang sử dụng Drupal 7.15 hoặc cao hơn; Đối với các phiên bản trước, bạn nên sử dụng mã sau đây.

$account = user_load(1);
$query->addMetaData('account', $account);

Như thường lệ, bạn không nên bỏ qua quyền truy cập nếu mã có thể hiển thị cho thông tin người dùng mà người dùng không nên có quyền truy cập. Điều này tương tự với những gì được thực hiện từ Drupal khi một nút chưa được công bố chỉ được hiển thị cho người dùng có quyền xem các nút chưa được công bố. Ví dụ, nếu mục đích của mã là chọn một số thực thể bị xóa liên tiếp (ví dụ: trong các tác vụ cron), thì việc vượt qua điều khiển truy cập sẽ không gây hại gì và đó là cách duy nhất để tiến hành.


Tôi nên thừa nhận rằng tôi có thể không đúng, vì truy vấn đầu tiên cũng sử dụng máy nhắn tin (tôi không nhận thấy điều đó ->extend('PagerDefault');lúc đầu)
mojzis

Rất tiếc, bạn đã đúng.
kiamlaluno

điều này khiến tôi thực sự thích thú, vì vậy tôi đang thử một cái gì đó dọc theo dòng thử nghiệm ở trên và không thể xác nhận sự khác biệt lớn về số lượng ... ai đó cũng có thể thử nó không?
mojzis

Vì vậy, chỉ cần xác nhận: EFQ gọi LUÔN LUÔN gọi quy tắc truy cập nút của trang web trừ khi bạn làm điều gì đó để ngăn điều đó xảy ra (như được mô tả ở trên). Đúng?
Jim Miller

@JimMiller Điều đó là chính xác và đó là lý do tại sao thẻ "DANGEROUS_ACCESS_CHECK_OPT_OUT" đã được thêm vào Drupal 7.15.
kiamlaluno
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.