Запрос коллекции и документов на основе длины значения в Pymongo - PullRequest
1 голос
/ 09 июля 2020

предположим, что моя БД дает мне результат запроса:

{'_id': ObjectId('5c99d76a32aacf180485c3b3'),
 'text': 'ILLUSTRATION : 1\nFind the quotient and remainder q and r for the pairs of positive integers given below:\n(i) 23,4\n(ii) 81,3\n(iii) 12,5\nUTION.\n',
 'text2': '',
 'parent': None,
 'repost': 3,
 'time': datetime.datetime(2010, 5, 9, 16, 5, 27, 838000)}

Я хочу получить первые 1000 документов, в которых длина text или длина text2> = 5:

Я могу сделать это через Python, но это будет глупо sh Что делать:

objects = []
i = 0
for obj in db.essays.find():
    if len(obj['text']>=5) or len(obj['text2']>=5):
        objects.append(obj)
        i+=1
    if i==1000:
        break

Я знаю, что это так глупо sh.

Я могу использовать limit(1000), если у меня есть точные совпадения, но я не знаю, как получить документы на основе длины значения.

EDIT : Каким-то образом мне удалось сделать PATCH как:

{ "$or":[{"$expr": { "$gt": [ { "$strLenCP": "$text" }, 5 ]}},
                                     {"$expr": { "$gt": [ { "$strLenCP": "$text2" }, 5 ]}},
                                    {"$expr": { "$gt": [ { "$strLenCP": "$text3" }, 5 ]}},
                                     ]}

Но когда я использую операцию AND для получения документов, когда длина всех текстов меньше 3, возникает ошибка:

{ "$and":[{"$expr": { "$lt": [ { "$strLenCP": "$text" }, 5 ]}},
                                     {"$expr": { "$lt": [ { "$strLenCP": "$text2" }, 5 ]}},
                                    {"$expr": { "$lt": [ { "$strLenCP": "$text3" }, 5 ]}},
                                     ]}

работает с limit(2), но не работает с> 2 и выдает ошибку:

`OperationFailure: $strLenCP requires a string argument, found: null`

Ответы [ 2 ]

4 голосов
/ 09 июля 2020

Вы можете использовать конвейер с strLenCP

db.collection.aggregate([
  {
    "$match": {
      "$expr": {
        "$or": [
          {
            "$gte": [
              {
                "$strLenCP": {
                  "$ifNull": [
                    "$text",
                    ""
                  ]
                }
              },
              5
            ]
          },
          {
            "$gte": [
              {
                "$strLenCP": {
                  "$ifNull": [
                    "$text2",
                    ""
                  ]
                }
              },
              5
            ]
          }
        ]
      }
    }
  },
  {
    "$limit": 1000
  }
])

Однако, если вам действительно важна производительность, лучшим способом будет предварительная обработка этой информации:

{
'_id': ObjectId('5c99d76a32aacf180485c3b3'),
 'text': 'ILLUSTRATION : 1\nFind the quotient and remainder q and r for the pairs of positive integers given below:\n(i) 23,4\n(ii) 81,3\n(iii) 12,5\nUTION.\n',
 'text2': '',
 'parent': None,
 'repost': 3,
 'time': datetime.datetime(2010, 5, 9, 16, 5, 27, 838000),
  'text_len': 100,
  "text2_len": 0
}

Теперь достаточно простого запроса:

db.essays.find({"$or": [{"text_len": {"$gte": 5}}, {"text2_len": {"$gte": 5}}]}).limit(1000)

Пн go Детская площадка

1 голос
/ 09 июля 2020

Вы можете использовать конвейер агрегирования $strLenCP.

Вот пример кода:

objects = []

for obj in db.essays.aggregate([
    {
        "$project": {
            "text1Len": {
                "$strLenCP": "$text"
            },
            "text2Len": {
                "$strLenCP": "$text2"
            },
            "docRoot": "$$ROOT",
        }
    },
    {
        "$match": {
            "$or": [
                {"text1Len": {"$gte": 5}},
                {"text2Len": {"$gte": 5}},
            ]
        }
    },
    {
        "$limit": 1000
    },
    {
        "$replaceRoot": {
            "newRoot": "$docRoot"
        }
    },
]):
    objects.append(obj)
...