Запрос к MongoDB для получения вложенного массива с пропуском/ограничением по диапазону ключей вложенных объектов в Django с помощью pymongo

Я изучаю Django с помощью pymongo.

У меня есть коллекция MongoDB, в которой я храню некоторые слова и их встречаемость по годам в некоторых книгах.

Документы хранятся в MongoDB в следующем формате:

{
   "_id":{
      "$oid":"625c51eec27c99b793074501"
   },
   "word":"entropy",
   "occurrence":13,
   "year":{
      "1942":[
         {
            "book":{
               "$oid":"625c51eec27c99b7930744f9"
            },
            "number":8,
            "sentence":[
               1,
               288,
               322,
               1237,
               2570,
               2585,
               2617,
               2634
            ]
         }
      ],
      "1947":[
         {
            "book":{
               "$oid":"625c5280c27c99b793077042"
            },
            "number":5,
            "sentence":[
               377,
               2108,
               2771,
               3467,
               3502
            ]
         }
      ]
   }
}

Теперь я хочу получить список предложений с skip и limit (и соответствующий id книги), запрошенный по _id и для определенного year диапазона.

Например,

  1. I want to fetch an array where each row will be a dictionary containing 'year', 'book' and 'sentence'.
  2. The array will be queried by the _id and year range.
  3. A skip and limit will be applied on the sentence list

Возможна ли такая задача с использованием Django и pymongo? Если да, то какой метод самый быстрый?

На данный момент я сделал следующее:

search= {'$and': [{"_id": word_id_obj, "year.1942": {"$exists": 1}}]}
datalist= []
word_docs= wordcollec.find(search, {'year': 1, '_id': 0}).skip(1).limit(5)
sentlist['recordsFiltered']+= wordcollec.count_documents(search)

for b in word_docs:
    year_data= b['year'][1942]
    for by in year_data:
        i= i+1
        this_word= {'serial': i, 'year': cyear, 'book': str(by['book'])}
        datalist.append(this_word)

Но очевидно, что это не дает желаемого результата, поскольку skip и limit применяются к объекту корневого документа. Также параметр year имеет фиксированное значение и не имеет диапазона.

Похоже, что использование '$slice' - это вариант. Но я не могу понять этого.

Спасибо, что дочитали до конца. И многое другое, если вы сможете пролить свет.

Вот один из способов:

... получить массив, где каждая строка будет словарем содержащий 'год', 'книгу' и 'предложение'.

db.collection.aggregate([
  { "$set": { "designWorkAround": { "$objectToArray": "$year" } } },
  { "$set": {
      "designWorkAround": {
        "$map": {
          "input": "$designWorkAround",
          "as": "yearArray",
          "in": {
            "year": "$$yearArray.k",
            "books": {
              "$map": {
                "input": "$$yearArray.v",
                "as": "bookArray",
                "in": {
                  "bookId": "$$bookArray.book",
                  "number": "$$bookArray.number",
                  "sentence": "$$bookArray.sentence"
                }
              }
            }
          }
        }
      }
    }
  },
  { "$unwind": "$designWorkAround" },
  { "$unwind": "$designWorkAround.books" },
  { "$project": {
      "_id": 0,
      "year": "$designWorkAround.year",
      "book": "$designWorkAround.books.bookId",
      "sentence": "$designWorkAround.books.sentence"
    }
  }
])

Попробуйте на mongoplayground.net.

Я не знаю всех генераций данных и запросов, которые вам могут понадобиться, но я бы, вероятно, переработал коллекцию и сделал бы что-то вроде одного документа на книгу со всеми соответствующими полями в документе. Это упростит и сделает более эффективными запросы, индексирование и т.д.

Вернуться на верх