在MongoDB中的多个字段中使用$ in

时间:2018-05-03 10:11:29

标签: node.js mongodb mongoose

我遇到了MongoDB中与$in相关的问题,但没有得到理想的结果。

这是我的MongoDB集合数据,以便更好地实现可视化

    [
        {
        "_id" : ObjectId("5aeaf7c73c3e9de82d91e439"),
        "companyID" : "4",
        "accounts" : [ 
            {
                "_id" : ObjectId("5aeaf7c720262a1db759edf5"),
                "userID" : "1",
                "preferences" : [ 
                    {
                        "emailNotification" : true,
                        "smsNotification" : true,
                        "pushNotification" : false,
                        "webNotification" : false,
                        "lastUpdatedBy" : "SYSTEM",
                        "_id" : ObjectId("5aeaf7c720262a1db759edf7"),
                        "preferenceID" : "6fbd6c-4c56-11e8-842f-0ed5f89f718b",
                        "createdAt" : ISODate("2018-05-03T11:51:35.509Z"),
                        "updatedAt" : ISODate("2018-05-03T11:51:35.509Z")
                    }, 
                    {
                        "emailNotification" : true,
                        "smsNotification" : true,
                        "pushNotification" : false,
                        "webNotification" : false,
                        "lastUpdatedBy" : "SYSTEM",
                        "_id" : ObjectId("5aeaf7c720262a1db759edf6"),
                        "preferenceID" : "6fb118-4c56-11e8-842f-0ed5f89f718b",
                        "createdAt" : ISODate("2018-05-03T11:51:35.509Z"),
                        "updatedAt" : ISODate("2018-05-03T11:51:35.509Z")
                    }
                ]
            }
        ],
        "__v" : 0
    },

    {
        "_id" : ObjectId("5aeafe693c3e9de82d91e43a"),
        "companyID" : "5",
        "accounts" : [ 
            {
                "_id" : ObjectId("5aeafe698b1d5f2057419c99"),
                "userID" : "1",
                "preferences" : [ 
                    {
                        "emailNotification" : true,
                        "smsNotification" : true,
                        "pushNotification" : false,
                        "webNotification" : false,
                        "lastUpdatedBy" : "SYSTEM",
                        "_id" : ObjectId("5aeafe698b1d5f2057419c9b"),
                        "preferenceID" : "6fbd6c-4c56-11e8-842f-0ed5f89f718b",
                        "createdAt" : ISODate("2018-05-03T12:19:53.436Z"),
                        "updatedAt" : ISODate("2018-05-03T12:19:53.436Z")
                    }, 
                    {
                        "emailNotification" : true,
                        "smsNotification" : true,
                        "pushNotification" : false,
                        "webNotification" : false,
                        "lastUpdatedBy" : "SYSTEM",
                        "_id" : ObjectId("5aeafe698b1d5f2057419c9a"),
                        "preferenceID" : "6fb118-4c56-11e8-842f-0ed5f89f718b",
                        "createdAt" : ISODate("2018-05-03T12:19:53.436Z"),
                        "updatedAt" : ISODate("2018-05-03T12:19:53.436Z")
                    }
                ]
            }
        ],
        "__v" : 0
    },
    {
        "_id" : ObjectId("5aeafe6d3c3e9de82d91e43b"),
        "companyID" : "6",
        "accounts" : [ 
            {
                "_id" : ObjectId("5aeafe6d8b1d5f2057419c9c"),
                "userID" : "1",
                "preferences" : [ 
                    {
                        "emailNotification" : true,
                        "smsNotification" : true,
                        "pushNotification" : false,
                        "webNotification" : false,
                        "lastUpdatedBy" : "SYSTEM",
                        "_id" : ObjectId("5aeafe6d8b1d5f2057419c9e"),
                        "preferenceID" : "6fbd6c-4c56-11e8-842f-0ed5f89f718b",
                        "createdAt" : ISODate("2018-05-03T12:19:57.331Z"),
                        "updatedAt" : ISODate("2018-05-03T12:19:57.331Z")
                    }, 
                    {
                        "emailNotification" : true,
                        "smsNotification" : true,
                        "pushNotification" : false,
                        "webNotification" : false,
                        "lastUpdatedBy" : "SYSTEM",
                        "_id" : ObjectId("5aeafe6d8b1d5f2057419c9d"),
                        "preferenceID" : "6fb118-4c56-11e8-842f-0ed5f89f718b",
                        "createdAt" : ISODate("2018-05-03T12:19:57.331Z"),
                        "updatedAt" : ISODate("2018-05-03T12:19:57.331Z")
                    }
                ]
            }, 
            {
                "_id" : ObjectId("5aeafe738b1d5f2057419c9f"),
                "userID" : "2",
                "preferences" : [ 
                    {
                        "emailNotification" : true,
                        "smsNotification" : true,
                        "pushNotification" : false,
                        "webNotification" : false,
                        "lastUpdatedBy" : "SYSTEM",
                        "_id" : ObjectId("5aeafe738b1d5f2057419ca1"),
                        "preferenceID" : "6fbd6c-4c56-11e8-842f-0ed5f89f718b",
                        "createdAt" : ISODate("2018-05-03T12:20:03.987Z"),
                        "updatedAt" : ISODate("2018-05-03T12:20:03.987Z")
                    }, 
                    {
                        "emailNotification" : true,
                        "smsNotification" : true,
                        "pushNotification" : false,
                        "webNotification" : false,
                        "lastUpdatedBy" : "SYSTEM",
                        "_id" : ObjectId("5aeafe738b1d5f2057419ca0"),
                        "preferenceID" : "6fb118-4c56-11e8-842f-0ed5f89f718b",
                        "createdAt" : ISODate("2018-05-03T12:20:03.987Z"),
                        "updatedAt" : ISODate("2018-05-03T12:20:03.987Z")
                    }
                ]
            }, 
            {
                "_id" : ObjectId("5aeafe778b1d5f2057419ca2"),
                "userID" : "3",
                "preferences" : [ 
                    {
                        "emailNotification" : true,
                        "smsNotification" : true,
                        "pushNotification" : false,
                        "webNotification" : false,
                        "lastUpdatedBy" : "SYSTEM",
                        "_id" : ObjectId("5aeafe778b1d5f2057419ca4"),
                        "preferenceID" : "6fbd6c-4c56-11e8-842f-0ed5f89f718b",
                        "createdAt" : ISODate("2018-05-03T12:20:07.062Z"),
                        "updatedAt" : ISODate("2018-05-03T12:20:07.062Z")
                    }, 
                    {
                        "emailNotification" : true,
                        "smsNotification" : true,
                        "pushNotification" : false,
                        "webNotification" : false,
                        "lastUpdatedBy" : "SYSTEM",
                        "_id" : ObjectId("5aeafe778b1d5f2057419ca3"),
                        "preferenceID" : "6fb118-4c56-11e8-842f-0ed5f89f718b",
                        "createdAt" : ISODate("2018-05-03T12:20:07.062Z"),
                        "updatedAt" : ISODate("2018-05-03T12:20:07.062Z")
                    }
                ]
            }
        ],
        "__v" : 0
    }]

我想从数据中获取什么。 我正在尝试根据公司ID& amp;获取每个公司的用户偏好。 userID,但适用于多个公司和多个用户,而不是单个用户。

比如说我有这个输入数据[ userID:2 , companyID:6 ]和[userID:1companyID:4]

基于上面添加的文档数据的输出应该是

    [
        {
            "userID":2,
            "companyID":6,
            "preferences":[] // the preferences from that db
        },
        {
            "userID":1,
            "companyID":4,
            "preferences":[] // their respective preferences array
        }
    ]

以上是所需的输出,现在我的解决方法

方法

  • 我在对象的表单数组中获取输入参数

    input_json = [{
            "userID":2,
            "companyID":6
        },
        {
            "userID":1,
            "companyID":4
        },
    ]
    
  • 获取输入JSON后,我正在制作两个不同的数组用户数组,公司的数组,user_array将保存所有用户ID,company_array将保存所有的公司ID此

    user_array = input_json.map((elem)=>elem.userID) // which will hold all the company ID
    
    //For the above input_json, the value of `user_array` would be 
      //  `user_array: [2,1]`
    
    
    company_array = input_json.map((elem)=>elem.userID)
    //For the above input_json, the value of `company_array` would be 
      //  `company_array`:[4,4] 
    
  • 获得userIDs&的值后companyID单独,我使用$ in来匹配MongoDB上的查询来匹配数组

    中的id
    db.collection.find({
        userID:{
               $in:user_array 
        },
        accounts.companyID:{
            $in:company_array
        }
    })
    
  • 我没有得到理想的结果,我希望将userID和companyID用作获取数据的唯一键。

但正在发生的是匹配user_array中的userID和company_array中的companyID。但是,我希望检查userIDcompanyID是否获得数据。

任何帮助都会被贬低:)

1 个答案:

答案 0 :(得分:3)

只需在样本上稍微扩展一下就可以包含"可能"您的请求数组实际上要求来自同一公司的多个用户的情况。

基本"查询" condition只是重新映射输入数组并在$or查询参数中使用它。出来像:

{
  "$or": [
    {
      "companyID": "6",
      "accounts.userID": "3"
    },
    {
      "companyID": "6",
      "accounts.userID": "2"
    },
    {
      "companyID": "4",
      "accounts.userID": "1"
    }
  ]
}

这将匹配"文档",但相应的"用户"信息包含在"accounts"数组中。为了仅检索那些项目,我们需要应用$filter条件来简单地保持那些符合条件的数组条目。然后,只需要在剩余的数组内容上使用$unwind,然后使用$project将一个小文档重新整理为所需的输出格式。

整个生成的语句将如下所示:

Company.aggregate([
  { '$match': { 
    '$or': [
      { companyID: '6', 'accounts.userID': '3' },
      { companyID: '6', 'accounts.userID': '2' },
      { companyID: '4', 'accounts.userID': '1' }
    ]
  }},
  { '$addFields': { 
    accounts: { 
     '$filter': { 
        input: '$accounts',
        cond: {
          '$or': [ 
            { '$and': [
              { '$eq': [ '$companyID', '6' ] },
              { '$eq': [ '$$this.userID', '3' ] }
            ] },
            { '$and': [
              { '$eq': [ '$companyID', '6' ] },
              { '$eq': [ '$$this.userID', '2' ] }
            ] }, 
            { '$and': [
              { '$eq': [ '$companyID', '4' ] }, 
              { '$eq': [ '$$this.userID', '1' ] }
            ] }
          ]
        }
      }
    }
  }},
  { '$unwind': '$accounts' },
  { '$project': {
    userID: '$accounts.userID',
    companyID: 1,
    preferences: '$accounts.preferences'
  }}
])

查询的$or内容和$or$filter的其他形式基本上是从输入数组中生成的,如下所示:

    let query = {
      $or: input.map(({ userID, companyID }) =>
        ({ companyID, 'accounts.userID': userID }))
    };

    let condition = input.map(({ userID, companyID }) =>
      ({ "$and": [
        { "$eq": ["$companyID", companyID] },
        { "$eq": ["$$this.userID", userID] }
      ]})
    );

然后在管道构造的其余部分中用作参数,这主要是静态的。请注意$filter中的"cond"用法需要"聚合逻辑运算符"它根据测试结果返回一个布尔值。因此它们与形式函数中的查询运算符不同。

相同"配对"适用于每个$or条件,以便在查找与该组合匹配时,会考虑帐户数组中的"companyID"值以及当前"userID"值。在$filter内,重要的是检查数组外部的"companyID",同时检查当前数组元素。

我们无法使用标准positional $ operator投影执行此操作的原因主要是因为查询中存在$or条件。还存在"多个匹配"的附加约束。在这里添加了用于演示,但由于查询中的$or,MongoDB无法确定哪些"条件和#34;无论如何,实际上对任何单个文档都满足元素匹配位置。

所以如果你想要"一个"那真的不重要。用户要匹配来自每个公司或多个",因为需要应用相同的聚合过滤器以便提取正确的"用户"细节无论如何。

完整的演示清单如下:

const { Schema } = mongoose = require('mongoose');

const uri = 'mongodb://localhost/test';

mongoose.Promise = global.Promise;
mongoose.set('debug',true);

let input = [
  {
    "userID": 3,
    "companyID": 6
  },
  {
    "userID": 2,
    "companyID": 6
  },
  {
    "userID": 1,
    "companyID": 4
  }
];


// non-strict for testing
const Company = mongoose.model('Company', new Schema({},{ strict: false }));

const log = data => console.log(JSON.stringify(data, undefined, 2));

(async function() {

  try {

    const conn = await mongoose.connect(uri);

    // Clean data to actually be matching strings
    input = input.map(({ userID, companyID }) =>
      ({ userID: userID.toString(), companyID: companyID.toString() }));


    let query = {
      $or: input.map(({ userID, companyID }) =>
        ({ companyID, 'accounts.userID': userID }))
    };
    log(query);

    let condition = input.map(({ userID, companyID }) =>
      ({ "$and": [
        { "$eq": ["$companyID", companyID] },
        { "$eq": ["$$this.userID", userID] }
      ]})
    );
    log(condition);


    let result = await Company.aggregate([
      { "$match": query },
      { "$addFields": {
        "accounts": {
          "$filter": {
            "input": "$accounts",
            "cond": { "$or": condition }
          }
        }
      }},
      { "$unwind": "$accounts" },
      { "$project": {
        "userID": "$accounts.userID",
        "companyID": 1,
        "preferences": "$accounts.preferences"
      }}
    ]);

    log(result);

    mongoose.disconnect();
  } catch(e) {
    console.error(e)
  } finally {
    process.exit()
  }


})()

这会产生如下输出:

[
  {
    "_id": "5aeaf7c73c3e9de82d91e439",
    "companyID": "4",
    "userID": "1",
    "preferences": [
      {
        "emailNotification": true,
        "smsNotification": true,
        "pushNotification": false,
        "webNotification": false,
        "lastUpdatedBy": "SYSTEM",
        "_id": "5aeaf7c720262a1db759edf7",
        "preferenceID": "6fbd6c-4c56-11e8-842f-0ed5f89f718b",
        "createdAt": "2018-05-03T11:51:35.509Z",
        "updatedAt": "2018-05-03T11:51:35.509Z"
      },
      {
        "emailNotification": true,
        "smsNotification": true,
        "pushNotification": false,
        "webNotification": false,
        "lastUpdatedBy": "SYSTEM",
        "_id": "5aeaf7c720262a1db759edf6",
        "preferenceID": "6fb118-4c56-11e8-842f-0ed5f89f718b",
        "createdAt": "2018-05-03T11:51:35.509Z",
        "updatedAt": "2018-05-03T11:51:35.509Z"
      }
    ]
  },
  {
    "_id": "5aeafe6d3c3e9de82d91e43b",
    "companyID": "6",
    "userID": "2",
    "preferences": [
      {
        "emailNotification": true,
        "smsNotification": true,
        "pushNotification": false,
        "webNotification": false,
        "lastUpdatedBy": "SYSTEM",
        "_id": "5aeafe738b1d5f2057419ca1",
        "preferenceID": "6fbd6c-4c56-11e8-842f-0ed5f89f718b",
        "createdAt": "2018-05-03T12:20:03.987Z",
        "updatedAt": "2018-05-03T12:20:03.987Z"
      },
      {
        "emailNotification": true,
        "smsNotification": true,
        "pushNotification": false,
        "webNotification": false,
        "lastUpdatedBy": "SYSTEM",
        "_id": "5aeafe738b1d5f2057419ca0",
        "preferenceID": "6fb118-4c56-11e8-842f-0ed5f89f718b",
        "createdAt": "2018-05-03T12:20:03.987Z",
        "updatedAt": "2018-05-03T12:20:03.987Z"
      }
    ]
  },
  {
    "_id": "5aeafe6d3c3e9de82d91e43b",
    "companyID": "6",
    "userID": "3",
    "preferences": [
      {
        "emailNotification": true,
        "smsNotification": true,
        "pushNotification": false,
        "webNotification": false,
        "lastUpdatedBy": "SYSTEM",
        "_id": "5aeafe778b1d5f2057419ca4",
        "preferenceID": "6fbd6c-4c56-11e8-842f-0ed5f89f718b",
        "createdAt": "2018-05-03T12:20:07.062Z",
        "updatedAt": "2018-05-03T12:20:07.062Z"
      },
      {
        "emailNotification": true,
        "smsNotification": true,
        "pushNotification": false,
        "webNotification": false,
        "lastUpdatedBy": "SYSTEM",
        "_id": "5aeafe778b1d5f2057419ca3",
        "preferenceID": "6fb118-4c56-11e8-842f-0ed5f89f718b",
        "createdAt": "2018-05-03T12:20:07.062Z",
        "updatedAt": "2018-05-03T12:20:07.062Z"
      }
    ]
  }
]

显示我们只返回了文档中匹配的公司和用户详细信息组合。

  

注意不确定使用您提供的输入样本是"故意" ,这些值是"数字&#34 ;而不是"字符串",当然它们实际上是"字符串"在它需要匹配的数据中。这是一个简单的代码行,它将数值转换为字符串,当然输入类型和存储类型都已匹配,这当然不是必需的。

     

同时,猫鼬通常会"演员"正常查询操作下的这些值对于模式中的任何内容,此不会与聚合管道一起发生。您在汇总管道操作中申请匹配的任何条件都需要"您" 自行将值转换为正确的类型。

     

Mongoose不会这样做,因为它无法做出假设"在聚合流水线阶段,当时呈现的数据与模式所知的状态相同。聚合操作通常是关于重塑文档"并且出于这个原因"架构"不适用于此。