MongoDB
 sql >> база данни >  >> NoSQL >> MongoDB

Документ за връщане с максимален поддокумент

Първо, нека покажем вашите данни по начин, по който хората могат да ги използват и да създадат желания резултат:

{ value: 1,
  _id: ObjectId('5cb9ea0c75c61525e0176f96'),
  name: 'Test',
  category: 'Development',
  subcategory: 'Programming Languages',
  status: 'Supported',
  description: 'Test',
  change:
   [ { version: 1,
       who: 'ATL User',
       when: new Date('2019-04-19T15:30:39.912Z'),
       what: 'Item Creation' },
     { version: 2,
       who: 'ATL Other User',
       when: new Date('2019-04-19T15:31:39.912Z'),
       what: 'Name Change' } ],
}

Имайте предвид, че "when" датите всъщност са различни, така че ще има $max стойност и те не са просто еднакви. Сега можем да прегледаме случаите

Случай 1 – Извличане на „единично“ $max стойност

Основният случай тук е да използвате $arrayElemAt и $indexOfArray оператори за връщане на съответстващия $max стойност:

db.items.aggregate([
  { "$match": {
    "subcategory": "Programming Languages", "name": { "$exists": true }
  }}, 
  { "$addFields": {
    "change": {
      "$arrayElemAt": [
        "$change",
        { "$indexOfArray": [
          "$change.when",
          { "$max": "$change.when" }
        ]}
      ]
    }
  }}
])

Връща:

{
        "_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
        "value" : 1,
        "name" : "Test",
        "category" : "Development",
        "subcategory" : "Programming Languages",
        "status" : "Supported",
        "description" : "Test",
        "change" : {
                "version" : 2,
                "who" : "ATL Other User",
                "when" : ISODate("2019-04-19T15:31:39.912Z"),
                "what" : "Name Change"
        }
}

По принцип "$max":"$change.when" връща стойността, която е "максималната" от този масив от стойности. След това намирате съответстващия „индекс“ на този масив от стойности чрез $indexOfArray което връща първия намерен съответстващ индекс. Тази позиция на "индекс" ( всъщност само от масив от "when" стойности, транспонирани в същия ред) след това се използва с $arrayElemAt за да извлечете "целия обект" от "промяната" масив на посочената индексна позиция.

Случай 2 – Връщане на „multiple“ $max записи

Почти същото нещо с $max , освен този път ние $filter за връщане на множеството "възможно" стойности, съответстващи на това $max стойност:

db.items.aggregate([
  { "$match": {
    "subcategory": "Programming Languages", "name": { "$exists": true }
  }}, 
  { "$addFields": {
    "change": {
      "$filter": {
        "input": "$change",
        "cond": {
          "$eq": [ "$$this.when", { "$max": "$change.when" } ]
        }
      }       
    }
  }}
])

Връща:

{
        "_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
        "value" : 1,
        "name" : "Test",
        "category" : "Development",
        "subcategory" : "Programming Languages",
        "status" : "Supported",
        "description" : "Test",
        "change" : [
                {
                        "version" : 2,
                        "who" : "ATL Other User",
                        "when" : ISODate("2019-04-19T15:31:39.912Z"),
                        "what" : "Name Change"
                }
        ]
}

Така че $max разбира се е същото, но този път единствената стойност, върната от този оператор, се използва в $eq сравнение в рамките на $filter . Това инспектира всеки елемент от масива и разглежда текущия "когато" стойност ( "$$this.when" ). Където „равно“ тогава елементът се връща.

По принцип същият като първия подход, но с изключение на $filter позволява "множество" елементи за връщане. Затовавсичкото със същите $max стойност.

Случай 3 – Предварително сортиране на съдържанието на масива.

Сега може да забележите, че в примерните данни, които включих (адаптирани от вашите собствени, но с действителна „максимална“ дата) стойността „максимална“ всъщност е последната стойност в масива. Това може да се случи естествено в резултат на $push (по подразбиране) "прилага" до края на съдържанието на съществуващия масив. Така че "по-ново" записите обикновено са в края от масива.

Това разбира се е по подразбиране поведение, но има основателни причини, поради които „можете“ искате да промените това. Накратконай-добрият начин да получите "най-новото" въвеждането в масив е всъщност да върне първия елемент от масива.

Всичко, което всъщност трябва да направите, е да осигурите „най-новото“ всъщност се добавяпърво вместо последен . Има два подхода:

  1. Използвайте $position за "предварително зареждане" на елементи от масив: Това е прост модификатор за $push използвайки 0 позиция за да добавяте винаги къмотпред :

    db.items.updateOne(
      { "_id" : ObjectId("5cb9ea0c75c61525e0176f96") },
      { "$push": {
          "change": {
            "$each": [{
              "version": 3,
              "who": "ATL User",
              "when": new Date(),
              "what": "Another change"
            }],
            "$position": 0
          }
       }}
    )
    

    Това ще промени документа на:

    {
        "_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
        "value" : 1,
        "name" : "Test",
        "category" : "Development",
        "subcategory" : "Programming Languages",
        "status" : "Supported",
        "description" : "Test",
        "change" : [
                {
                        "version" : 3,
                        "who" : "ATL User",
                        "when" : ISODate("2019-04-20T02:40:30.024Z"),
                        "what" : "Another change"
                },
                {
                        "version" : 1,
                        "who" : "ATL User",
                        "when" : ISODate("2019-04-19T15:30:39.912Z"),
                        "what" : "Item Creation"
                },
                {
                        "version" : 2,
                        "who" : "ATL Other User",
                        "when" : ISODate("2019-04-19T15:31:39.912Z"),
                        "what" : "Name Change"
                }
        ]
    }
    

Имайте предвид, че това ще изисква от вас действително да отидете и да "обърнете" всичките си елементи от масива предварително, така че "най-новият" вече да е отпред, така че редът да се запази. За щастие това донякъде е покрито във втория подход...

  1. Използвайте $sort да променяте документите по ред на всеки $push : И това е другият модификатор, който всъщност "пресортира" атомарно при всяко добавяне на нов елемент. Нормалното използване е основно същото с всички нови елементи към $each както по-горе, или дори просто „празен“ масив, за да приложите $sort само към съществуващи данни:

    db.items.updateOne(
      { "_id" : ObjectId("5cb9ea0c75c61525e0176f96") },
      { "$push": {
          "change": {
            "$each": [],
            "$sort": { "when": -1 } 
          }
       }}
    )
    

    Резултати в:

    {
            "_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
            "value" : 1,
            "name" : "Test",
            "category" : "Development",
            "subcategory" : "Programming Languages",
            "status" : "Supported",
            "description" : "Test",
            "change" : [
                    {
                            "version" : 3,
                            "who" : "ATL User",
                            "when" : ISODate("2019-04-20T02:40:30.024Z"),
                            "what" : "Another change"
                    },
                    {
                            "version" : 2,
                            "who" : "ATL Other User",
                            "when" : ISODate("2019-04-19T15:31:39.912Z"),
                            "what" : "Name Change"
                    },
                    {
                            "version" : 1,
                            "who" : "ATL User",
                            "when" : ISODate("2019-04-19T15:30:39.912Z"),
                            "what" : "Item Creation"
                    }
            ]
    }
    

    Може да отнеме минута, за да разберете защо бихте $push за да $sort масив като този, но общото намерение е, когато могат да бъдат направени модификации на масив, които „променят“ свойство като Дата стойност, по която се сортира и бихте използвали такъв израз, за ​​да отразите тези промени. Или просто добавете нови елементи с $sort и го оставете да се получи.

Така че защо „магазин“ масивът подреден по този начин? Както споменахме по-рано, вие искате първото елемент като "най-скорошен" , а след това заявката за връщане, която просто става:

db.items.find(
  {
    "subcategory": "Programming Languages",
    "name": { "$exists": true }
  },
  { "change": { "$slice": 1 } }
)

Връща:

{
        "_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
        "value" : 1,
        "name" : "Test",
        "category" : "Development",
        "subcategory" : "Programming Languages",
        "status" : "Supported",
        "description" : "Test",
        "change" : [
                {
                        "version" : 3,
                        "who" : "ATL User",
                        "when" : ISODate("2019-04-20T02:40:30.024Z"),
                        "what" : "Another change"
                }
        ]
}

Така че $slice след това може да се използва само за извличане на елементи от масив чрез известни индекси. Технически можете просто да използвате -1 там, за да върнете последния елемент от масива така или иначе, но пренареждането, където най-новото е първо, позволява други неща като потвърждение, че последната модификация е направена от определен потребител и/или други условия като ограничение за диапазон от дати. т.е.:

db.items.find(
  {
    "subcategory": "Programming Languages",
    "name": { "$exists": true },
    "change.0.who": "ATL User",
    "change.0.when": { "$gt": new Date("2018-04-01") }
  },
  { "change": { "$slice": 1 } }
)

Тук отбелязваме, че нещо като "change.-1.when" е незаконно твърдение, което всъщност е причината да пренаредим масива, така че да можете да използвате законния 0 за първо вместо -1 за последно .

Заключение

Така че има няколко различни неща, които можете да направите, или като използвате подхода на агрегиране за филтриране на съдържанието на масива, или чрез стандартни формуляри за заявки, след като направите известна промяна в начина, по който данните действително се съхраняват. Кой да използвате зависи от вашите собствени обстоятелства, но трябва да се отбележи, че всеки от стандартните формуляри за заявки ще работи значително по-бързо от всяко манипулиране чрез рамката за агрегиране или изчислени оператори.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. MongoDB:mongoimport губи връзка при импортиране на големи файлове

  2. MongoDB и Robomongo:Не мога да се свържа (удостоверяване)

  3. Mongodb не се актуализира, когато използвам по този начин

  4. База данни над 2 GB в MongoDB

  5. Mongoose:findOneAndUpdate не актуализира съществуващо поле