2016-11-12 1 views
1

Моей коллекции:Фильтра массив поддокумента используя подстроку в качестве критериев

{ 
    title: 'Computers', 
    maincategories:[ 
    { 
     title: 'Monitors', 
     subcategories:[ 
     { 
      title: '24 inch', 
      code: 'AFG' 
     } 
     ] 
    } 
    ] 
} 

Я хочу запросить код. Код - это только первая часть, поэтому я хочу иметь все подкатегории, содержащие данный поиск. Таким образом, AFG101 вернет эти подкатегории.

Моего запрос:

module.exports = (req, res) => { 
    var q = { 
    'maincategories.subcategories': { 
     $elemMatch: { 
     code: 'AFG101' 
     } 
    } 
    }; 

    var query = mongoose.model('TypeCategory').find(q, {'maincategories.$': 1, 'title': 1}); 

    query.exec((err, docs) => { 
    res.status(200).send(docs); 
    }); 
}; 

Моей проблема:

  1. Как найти часть строки? AFG101 должен возвращать все подкатегории с свойством code, содержащим любую часть строки. Таким образом, в этом случае AFG будет хитом. Как и в этом вопросе sql: MySQL: What is a reverse version of LIKE?

  2. Как проецировать подкатегории. Текущий запрос возвращает все подкатегории. Я только хочу вернуть эти удары.

+0

Я думаю, было бы лучше, если бы вы могли разобрать код 'AFG101' в соответствующий код, прежде чем запрашивать Монго. – hyades

+0

@hyades, я не понимаю. Как я могу это сделать? – Per

+0

Думаю, я понимаю, чего вы хотите в первом вопросе. Вы хотите, например: иметь несколько документов с «кодом: AFG1», «code; AFG2», «code: AFG3,» code: ABC1 ». Итак, когда пользователь (чего бы то ни было) набирает что-то вроде« AFG «mongo доставит вам все документы, содержащие« AFG »в поле CODE. Если это так, отметьте« regex »в mongodb. https://docs.mongodb.com/manual/reference/operator/query/regex/ #examples –

ответ

1

Лучший способ сделать это в MongoDB 3.4 с использованием оператора агрегации $indexOfCP строки.

let code = "afg101"; 

db.collection.aggregate([ 
    { "$project": { 
     "title": 1, 
     "maincategories": { 
      "$map": { 
       "input": "$maincategories", 
       "as": "mc", 
       "in": { 
        "$filter": { 
         "input": "$$mc.subcategories", 
         "as": "subcat", 
         "cond": { 
          "$gt": [ 
           { 
            "$indexOfCP": [ 
             code, 
             { "$toLower": "$$subcat.code" } 
            ] 
           }, 
           -1 
          ] 
         } 
        } 
       } 
      } 
     } 
    }} 
]) 

, которая возвращает:

{ 
    "_id" : ObjectId("582cba57e6f570d40d77b3a8"), 
    "title" : "Computers", 
    "maincategories" : [ 
     [ 
      { 
       "title" : "24 inch", 
       "code" : "AFG" 
      } 
     ] 
    ] 
} 

Вы можете прочитать другие мои ответы на аналогичный вопрос 1, 2 и 3.


Из 3.2 назад, единственный способ сделать это с mapReduce.

db.collection.mapReduce(
    function() { 
     var code = 'AFG101'; 
     var maincategories = this.maincategories.map(function(sdoc) { 
      return { 
       "title": sdoc.title, 
       "subcategories": sdoc.subcategories.filter(function(scat) { 
        return code.indexOf(scat.code) != -1; 
       } 
      )}; 
     }); 
     emit(this._id, maincategories); 
    }, 
    function(key, value) {}, 
    { "out": { "inline": 1 } 
}) 

, который дает что-то вроде этого:

{ 

    "results" : [ 
     { 
      "_id" : ObjectId("582c9a1aa358615b6352c45a"), 
      "value" : [ 
       { 
        "title" : "Monitors", 
        "subcategories" : [ 
         { 
          "title" : "24 inch", 
          "code" : "AFG" 
         } 
        ] 
       } 
      ] 
     } 
    ], 
    "timeMillis" : 15, 
     "counts" : { 
      "input" : 1, 
      "emit" : 1, 
      "reduce" : 0, 
      "output" : 1 
     }, 
    "ok" : 1 
} 
0

Ну, точно так же, как ваш вопрос состоит из двух частей, я мог бы подумать о двух отдельных решениях, однако я не вижу способа объединить их.

Для первой части $, где можно использовать обратное регулярное выражение, но оно грязное, оно является излишним, и оно не может использовать какие-либо индексы, поскольку $ где выполняется на каждом документе.

db.TypeCategory.find({$where:function(){for(var i in this.maincategories) 
{for(var j in this.maincategories[i].subcategories) 
{if("AFG101".indexOf(this.maincategories[i].subcategories[j].code)>=0) 
    {return true}}}}},{"maincategories.subcategories.code":1}) 

Даже если вы используете эту опцию, то потребуется несколько граничной проверка и не может проецировать два уровня вложенного массива. MongoDB не поддерживает такой прогноз (пока).

Для этой цели мы могли бы пойти на агрегацию

db.TypeCategory.aggregate([{$unwind:"$maincategories"}, 
{$unwind:"$maincategories.subcategories"}, 
{$match:{"maincategories.subcategories.code":"AFG"}}, 
{$group:{_id:"$_id","maincategories":{$push:"$maincategories"}}} 
]) 

Однако я не думаю, что есть способ сделать обратную проверку регулярных выражений в агрегации, но я мог бы быть неправильно тоже. Кроме того, это агрегирование является дорогостоящим, так как есть два разматывания, которые могут привести к переполнению ограничения памяти для агрегации для действительно большой коллекции.

0

Вы можете использовать $ SUBSTR и сделать это

db.getCollection('cat').aggregate([ 
      {"$unwind" : "$maincategories"}, 
      {"$unwind" : "$maincategories.subcategories"}, 
      {"$project" : 
       {"maincategories" : 1, 
       "title":1,"sub" : {"$substr" :["$maincategories.subcategories.code",0,3]}}}, 
     {"$match" : {"sub" : "AFG"}}, 
     {"$project" : 
       {"maincategories" : 1, 
        "title":1} 
      } 
    ]) 
+0

Вы можете использовать $ group: {"_id": "$ _id"} stage и $ push для создания массива –