2012-10-14 29 views
8

Tôi đang cố gắng tìm hiểu MongoDB và nó đã được tuyệt vời cho đến nay. Tuy nhiên tôi gặp phải một tình huống và tôi không chắc chắn làm thế nào để giải quyết nó. Hy vọng rằng ai đó có thể giúp tôi và cảm ơn trước.

Tôi muốn nhận bản ghi rằng giá trị mảng (toàn bộ) nằm trong truy vấn. Ví dụ:

kỷ lục 1:

{"name" : "Mango Shake", 
"ingredients" : [{"type" : "fruit", "name" : "mango"}, 
        {"type" : "milk", "name" : "soy milk"}]} 

kỷ lục 2:

{"name" : "Mango Banana Shake", 
"ingredients" : [{"type" : "fruit", "name" : "mango"}, 
        {"type" : "milk", "name" : "soy milk"}, 
        {"type" : "fruit", "name" : "banana"}]} 

kỷ lục 3:

{"name" : "Milk Shake", 
"ingredients" : [{"type" : "milk", "name" : "soy milk"}]} 

thì tôi sẽ có một truy vấn giống như

{"ingredients" : {$all : [{"type" : "fruit", "name" : "mango"}, 
          {"type" : "milk", "name" : "soy milk"}, 
          {"type" : "fruit", "name" : "strawberry"}]}} 

vì tôi có xoài, sữa đậu nành và dâu tây. Vì vậy, tôi muốn biết tôi có thể làm gì. Rõ ràng điều này không trả lại bất cứ điều gì vì truy vấn không thể có thêm công cụ. Nếu tôi sử dụng $in thì tất cả sẽ trở lại, nhưng tôi không thể làm xoài chuối lắc vì tôi không có chuối ..

Vì vậy, những gì tôi chỉ cần là người đầu tiên và cuối cùng. Bất kỳ ý tưởng? Đánh giá cao nó :)

+0

Vì vậy, bạn muốn tạo một truy vấn từ những thành phần mà bạn có trong tay, do đó trả lại tập kết quả chỉ bao gồm những phần lắc mà bạn có thể thực hiện? – chb

+0

có chính xác .. cảm ơn chb –

+0

bạn có bất kỳ yêu cầu nào như nó chỉ nên là một truy vấn không? tôi có thể trả lại tên/id không? –

Trả lời

0

Tôi không thể nghĩ ra cách để làm điều này với tìm kiếm. Nhưng bạn có thể làm điều đó với một số mapReduce.

Chức năng bản đồ của bạn sẽ lặp lại thành phần của tài liệu hiện được kiểm tra và chỉ phát ra khi tất cả các trường là thành phần bạn có. Bạn không cần một chức năng giảm cho điều này, do đó, chìa khóa của các giá trị phát ra phải là _id của các tài liệu uống.

Khi các phím là duy nhất, chức năng giảm sẽ không bao giờ được gọi (chức năng giảm được sử dụng để xác định điều gì xảy ra khi nhiều giá trị được phát ra cho cùng một khóa). Tôi không nghĩ rằng bạn chỉ có thể bỏ qua chức năng giảm, vì vậy bạn nên viết một hàm chỉ trả về phần tử đầu tiên của mảng giá trị.

0

Bản đồ giảm sẽ có tác dụng nếu bạn thích, hoặc bạn chỉ có thể sử dụng $ và với một điều kiện cho mỗi thành phần của bạn:

http://docs.mongodb.org/manual/reference/operator/and/

Tôi đã sử dụng $ và trước khi cho nhu cầu truy vấn tương tự. Thật không may, trong cả hai trường hợp, điều này sẽ phải kiểm tra từng tài liệu công thức mỗi khi bạn truy vấn sẽ có tác động nghiêm trọng đến hiệu suất nếu bộ sưu tập của bạn trở nên lớn.

Điều này có thể được xử lý tốt hơn với cơ sở dữ liệu quan hệ. Ít nhất bạn nên xem xét việc giữ một chỉ mục có thể truy vấn riêng biệt về các thành phần có bản đồ cho id công thức sử dụng chúng. Sau đó, bạn có thể lấy chỉ là các thành phần bạn có và mất giao điểm của các công thức nấu ăn trong mã. Một lần nữa, vì đây thực sự là một cái gì đó một cơ sở dữ liệu quan hệ có thể làm cho bạn nó chỉ đơn giản là sự lựa chọn tốt hơn ở đây.

0

Bạn có thể sử dụng dot notation để truy cập các yếu tố cụ thể của mảng và kiểm tra xem mỗi phần tử là $in danh sách các thành phần có sẵn của bạn và bạn có thể xây dựng một truy vấn $and để làm điều đó cho mỗi phần tử trong tài liệu được truy vấn NHƯNG tất nhiên rằng sẽ không hoạt động vì bạn không biết có bao nhiêu phần tử trong danh sách thành phần của mỗi công thức. Một giải pháp cho vấn đề đó là thêm một mảng vào mỗi tài liệu có kích thước cố định (có thể là 3 phần tử) mà bạn đặt các thành phần phổ biến nhất từ ​​công thức, phần đệm (lặp lại nếu cần thiết để điền vào mảng). Bây giờ bạn có thể thực hiện truy vấn bằng cách sử dụng $and$in đối với mỗi phần tử của mảng đó. Điều này sẽ tìm thấy tất cả các công thức nấu ăn mà 'có thể' là có thể, và kể từ khi bạn lưu trữ các thành phần phổ biến nhất nó nên làm một công việc tốt lọc ra công thức nấu ăn bạn không thể thực hiện. Sau đó, bạn có thể thực hiện bộ lọc cuối cùng phía máy khách.

Đôi khi việc thêm một đại diện khác của dữ liệu có thể là một cách tốt để giải quyết vấn đề truy vấn phức tạp như thế này, và đôi khi kết hợp lọc máy chủ và phía máy khách có thể phù hợp.

0

Bạn có thể sử dụng hàm javascript làm thông số tìm. Tuy nhiên, tôi nghĩ rằng MapReduce sẽ là một giải pháp thanh lịch hơn nhiều tổng thể do khả năng mở rộng và sự khó chịu chung đi kèm với chèn đầu vào của người dùng vào mã được thực thi.

Dưới đây là một truy vấn mẫu sẽ được tạo. Về cơ bản, bộ lọc lọc thành phần của mục và kiểm tra danh sách được lọc có cùng độ dài với danh sách thành phần ban đầu:

db.shakes.find(function() { 

    // Implant your query JSON object here 
    var query_obj = [{"type" : "fruit", "name" : "mango"}, 
        {"type" : "milk", "name" : "soy milk"}, 
        {"type" : "fruit", "name" : "strawberry"}]; 
    return this.ingredients.filter(function(ingredient) { 
    var has_ingredients = false; 
    query_obj.forEach(function(query) { 
     has_ingredients |= (query.type == ingredient.type && query.name == ingredient.name); 
    }); 
    return has_ingredients; 
    }).length === this.ingredients.length; 
}) 
0

Không có cách nào trực tiếp để thực hiện việc này. bạn có thể xây dựng một hàm javascript đơn giản để giải quyết truy vấn của bạn bằng cách xem từng tài liệu và kiểm tra các thành phần của mảng có tất cả ba thành phần.

db.shakes.find().forEach(

function (shakes) { 
    for (i = 0; i < shakes.ingredients.length; i++) { 
     switch (shakes.ingredients[i].name) { 
     case "banana": 
      var count = 1; 
     case "soy milk": 
      count++; 
     case "mango": 
      count++; 
     } 
     if (count == 3) { 
      print(shakes.name); 
     } 
    } 
} 
); 
0

Để mang lại một số ý tưởng mới, trong trường hợp bạn đã biết tất cả các thành phần đã biết. Ví dụ các thành phần có thể là chỉ 5:

["mango","soy milk", "strawberry", "banana", "pineapple"] 

Và bạn không' có cả chuối hay dứa

Một lựa chọn là để truy vấn cho các thành phần bạn không có:

{"ingredients" : {$nin : [{"type" : "fruit", "name" : "banana"}, 
          {"type" : "fruit", "name" : "pineapple"}]}} 

Điều này trả về tất cả các tài liệu không có thành phần mà bạn không có, vì vậy, recieps với các thành phần bạn thực sự có.

0

Giả sử bạn là ok với caveats of using the $where operator, bạn có thể sử dụng nó, kết hợp với Array.prototype.someArray.prototype.every để có được kết quả mong muốn.

Điều này trông giống như giải pháp dành riêng cho javascript, nhưng không phải như vậy. Javascript được tiêm trực tiếp vào Mongo (V8). Tất cả các trình điều khiển ngôn ngữ nên hỗ trợ nó. Tôi biết ít nhất là PHP does.

Bạn sẽ có thể dán mã này trực tiếp vào Mongo REPL và nhận được kết quả mong đợi (gõ trên gỗ).

Nếu cơ sở dữ liệu milkshake của bạn có khả năng mở rộng vượt quá những gì $where có thể cung cấp cho bạn, Aggregation Framework là bạn của bạn.

var query = { 
    "$where": function() { 
     var myIngredients = [ 
      {"type" : "fruit", "name" : "mango"}, 
      {"type" : "milk", "name" : "soy milk"}, 
      {"type" : "fruit", "name" : "strawberry"} 
     ]; 
     return obj.ingredients.every(function(milkShakeIngredient){ 
      return myIngredients.some(function(myIngredient){ 
       return (milkShakeIngredient.name === myIngredient.name && milkShakeIngredient.type === myIngredient.type); 
      }); 
     }); 
    } 
}; 

db.milkShakes.find(query); 
Các vấn đề liên quan