2013-12-09 20 views
7

Tôi có một tài liệu với một mảng bên trong, như thế này:

"userTags" : [ 
     "foo", 
     "foo", 
     "foo", 
     "foo", 
     "moo", 
     "bar" 
    ] 

Nếu tôi thực hiện db.products.update({criteriaToGetDocument}, {$push: {userTags: "foo"}}} tôi có thể chèn một cách chính xác một thể hiện của foo vào mảng.

Tuy nhiên, nếu tôi làm db.products.update({criteriaToGetDocument}, {$pull: {userTags: "foo"}}} sau đó nó loại bỏ tất cả trường hợp của foo từ mảng, để lại tôi với:

"userTags" : [ 
     "moo", 
     "bar" 
    ] 

này sẽ không làm gì cả, tôi chỉ muốn kéo một mục từ mảng thay vì tất cả chúng. Làm thế nào tôi có thể thay đổi lệnh để chỉ có một foo bị xóa? Có một số loại phương thức $pullOnce có thể hoạt động ở đây không?

Trả lời

4

Không, không có gì như thế này vào lúc này. Rất nhiều người đã yêu cầu tính năng này và bạn có thể theo dõi nó trong mongodb Jira. Theo như bạn có thể thấy nó không được giải quyết và cũng không được lên kế hoạch (có nghĩa là bạn không có may mắn trong tương lai gần).

Các lựa chọn duy nhất là sử dụng logic ứng dụng để đạt được điều này sẽ là:

  1. tìm yếu tố mà bạn muốn và có userTags như foo
  2. lặp qua userTags và loại bỏ một foo từ nó
  3. cập nhật phần tử đó bằng userTags mới

Hãy nhớ rằng hoạt động này phá vỡ nguyên tử, nhưng vì Mongo không cung cấp phương pháp gốc để làm như vậy, bạn sẽ phá vỡ atomi thành phố theo bất kỳ cách nào.

Tôi đã chuyển một giải pháp thay thế cho câu trả lời mới, bởi vì nó không trả lời câu hỏi này, nhưng đại diện cho một trong các cách tiếp cận để cấu trúc lại lược đồ hiện có. Nó cũng trở nên quá lớn, bắt đầu lớn hơn nhiều so với câu trả lời ban đầu.

+0

Đây là ... rất khó chịu. Tôi giả sử một cách khác là thay đổi thiết kế dữ liệu của mình để 'userTags' là một đối tượng với' {tag: (numberofinstances) 'và chỉ tăng/giảm số này bất cứ khi nào cần thiết, nhưng tôi thực sự không làm điều đó vì nó messes với rất nhiều thứ khác! – Jascination

+0

Tôi đồng ý với bạn. Dường như tính năng này thực sự hữu ích và không đọc tài liệu có thể hiểu sai ý tưởng về $ pull. Nhưng mọi người ở mongo đang làm một công việc tốt làm tăng tính năng của mongo trong mỗi lần phát hành. Vì vậy, tôi tin rằng một ngày bạn sẽ thấy điều này ra khỏi hộp. P.S. - bỏ phiếu cho tính năng trong JIRA. –

+0

Chỉnh sửa đẹp. Bạn có thể đề xuất bất kỳ lệnh nào sẽ kiểm tra thẻ hiện có sau đó kiểm tra xem nó có lớn hơn một thẻ không? Hoặc tôi sẽ phải tìm xem liệu thẻ 'tag: {$ exist: true}' trong một lần tra cứu có thực hiện cập nhật với số thẻ mới không? – Jascination

1

Nếu bạn thực sự muốn sử dụng tính năng này, bạn phải xem xét sửa đổi giản đồ của mình. Một cách để thực hiện là thực hiện một việc như sau:

"userTags" : { 
    "foo" : 4, 
    "moo": 1, 
    "bar": 1 
} 

Trường hợp số là số lượng thẻ. Bằng cách này, bạn có thể sử dụng $ inc nguyên tử để thêm hoặc xóa thẻ. Trong khi thêm thẻ mới có thể thích hợp, trong khi xóa các thẻ bạn phải cẩn thận. Bạn cần phải kiểm tra xem thẻ tồn tại và nó là> = sau đó 1.

Dưới đây là làm thế nào bạn có thể làm điều đó:

db.a.insert({ 
    _id : 1, 
    "userTags" : { 
    "foo" : 4, 
    "moo": 1, 
    "bar": 1 
    } 
}) 

Để thêm một khóa gì bạn cần làm chỉ này:

db.a.update({_id : 1}, {$inc : {'userTags.zoo' : 1}}) 

Nếu thẻ không tồn tại trước khi nó sẽ tạo ra nó

để loại bỏ các Tag, bạn cần phải làm nhiều hơn một chút:

db.a.update({ 
    _id : 1, 
    'userTags.moo' : {$gte : 1 } 
}, { 
    $inc : {'userTags.moo' : -1} 
}) 

Ở đây bạn đang kiểm tra rằng phần tử tồn tại và lớn hơn 1 (không cần phải kiểm tra sự tồn tại vì $ gte cũng đang làm việc này).

Hãy nhớ rằng phương pháp này có một nhược điểm. Bạn có thể có một số thẻ được liệt kê dưới dạng các trường, nhưng không phải là các thẻ thực (chúng có giá trị là 0). Vì vậy, nếu bạn muốn tìm tất cả các phần tử có tag foo, bạn phải nhớ điều này và thực hiện như sau:

db.a.find({'userTags.zoo' : { $gte : 1}}) 

Ngoài ra khi bạn xuất tất cả các thẻ cho một phần tử, bạn có thể gặp vấn đề tương tự. Vì vậy, bạn cần phải khử trùng đầu ra trên lớp ứng dụng.

0

Tôi biết đó là một cách giải quyết và không phải là một giải pháp thực tế nhưng, như tôi đã chạy vào vấn đề này cùng, tôi thấy rằng trong PHP ít nhất, bạn có thể làm một cái gì đó như thế này:

// Remove up to two elements "bar" from an array called "foo" 
// in a document that matches the query {foo:"bar"} 

$un_bar_qty = 2; 
$un_bar_me = $db->foo->findOne(array("foo"=>"bar")); 
foreach($un_bar_me['bar'] as $foo => $bar) { 
    if($bar == 'bar') { 
     unset($un_bar_me['bar'][$foo]); 
     $un_bar_qty--; 
    } 
    if(!$un_bar_qty) break; 
} 
$db->foo->update(array('_id'=>$un_bar_me['_id']),$bar); 

tài liệu trước đây:

{ 
    { foo: "bar" }, 
    { bar: [ "bar" , "bar" , "foo", "bar" ] } 
} 

tài liệu sau:

{ 
    { foo: "bar" }, 
    { baz: [ "foo", "bar" ] } 
} 

Không quá xấu một cách để làm điều đó. Chắc chắn dường như ít rắc rối với tôi hơn là rối tung xung quanh với các thẻ và gia số, YMMV.

Các vấn đề liên quan