MongoDB — Операции с вложенными полями

#mongodb #mongodb-query #aggregation-framework

#mongodb #mongodb-запрос #агрегация-фреймворк

Вопрос:

У меня есть данные Twitter, которые выглядят следующим образом:

 db.users.findOne()
{
    "_id" : ObjectId("578ffa8e7eb9513f4f55a935"),
    "user_name" : "koteras",
    "retweet_count" : 0,
    "tweet_followers_count" : 461,
    "source" : "<a href="http://twitter.com/download/iphone" rel="nofollow">Twitter for iPhone</a>",
    "coordinates" : null,
    "tweet_mentioned_count" : 1,
    "tweet_ID" : "755891629932675072",
    "tweet_text" : "RT @ochocinco: I beat them all for 10 straight hours #FIFA16KING",
    "user" : {
        "CreatedAt" : ISODate("2011-12-27T09:04:01Z"),
        "FavouritesCount" : 5223,
        "FollowersCount" : 461,
        "FriendsCount" : 619,
        "UserId" : 447818090,
        "Location" : "501"
    }
  

Например, я хочу найти количество пользователей, у которых «FollowersCount» больше, чем «Favoritescount». Как я могу это сделать?

Ответ №1:

Оператор $where специально разработан для этого.

 db.users.find( { $where: function() { return (this.user.FollowersCount > this.user.FavouritesCount) } } );
  

Но имейте в виду, что это привело бы к запуску однопоточного JS-кода и было бы медленнее.

Другой вариант — использовать конвейер агрегации, проецирующий разницу, а затем имеющий $match для этой разницы

 db.users.aggregate([
  {$project: {
    diff: {$subtract: ["$user.FollowersCount", "$user.FavouritesCount"]},
    // project remaining fields here
    }
  },
  {$match: {diff: {$gt: 0}}}
])
  

По своему опыту я обнаружил, что второй способ намного быстрее первого.

Комментарии:

1. И в обоих случаях применитесь itcount() к возвращаемому курсору, чтобы получить количество совпадающих документов

2. Спасибо, чувак! Что насчет функции «и»? Если я хочу найти твиты, которые содержат определенный текст и из определенного местоположения.

3. Я пробовал это, и это не работает db.users.find({$и [{«пользователь. Местоположение»: «501»},{ tweet_text: /UEFA/}]})

4. В $and этом нет необходимости. workdb.users.find({"user.Location": "501", tweet_text: /UEFA/}) также должно работать

Ответ №2:

Чтобы получить количество пользователей, у которых «FollowersCount» больше, чем «Favoritescount», вы могли бы использовать платформу агрегации, в которой есть некоторые операторы, которые вы можете применить.

Рассмотрим первый вариант использования, который рассматривает манипулирование операторами сравнения в $project конвейере и последующем $match конвейере для фильтрации документов на основе $cmp значения. Затем вы можете получить окончательное количество пользователей, применив $group конвейер, который объединяет отфильтрованные документы:

 db.users.aggregate([
    {
        "$project": {               
            "hasMoreFollowersThanFavs": { 
                "$cmp": [ "$user.FollowersCount", "$user.FavouritesCount" ]
            }
        }
    },
    { "$match": { "hasMoreFollowersThanFavs": 1 } },    
    {
        "$group": {
            "_id": null,
            "count": { "$sum": 1 }
        }
    }
])
  

Другим вариантом является использование единого конвейера с $redact оператором, который включает в себя функциональность $project и $match , как указано выше, и возвращает все документы, которые соответствуют указанному условию, используя $$KEEP системную переменную, и отбрасывает те, которые не соответствуют, используя $$PRUNE системную переменную:

 db.collection.aggregate([
    {
        "$redact": {
            "$cond": [
                { 
                    "$eq": [
                        { "$cmp": [ "$user.FollowersCount", "$user.FavouritesCount" ] }, 
                        1
                    ]
                }, 
                "$$KEEP", 
                "$$PRUNE"
            ]
        }
    },  
    {
        "$group": {
            "_id": null,
            "count": { "$sum": 1 }
        }
    }
])