Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mongodb o以有意义的方式“查询”任何内容,而不引入不必要的开销,这将破坏应用程序性能,并大大增加代码维护的复杂性,然后使用值而不是键来识别那些有意义的数据点,而不是以这种方式使用。您尝试了什么?请考虑阅读和编辑你的问题。你有没有机会去修复那个数据设计?您将日期作为_Mongodb_Aggregation Framework - Fatal编程技术网

Mongodb o以有意义的方式“查询”任何内容,而不引入不必要的开销,这将破坏应用程序性能,并大大增加代码维护的复杂性,然后使用值而不是键来识别那些有意义的数据点,而不是以这种方式使用。您尝试了什么?请考虑阅读和编辑你的问题。你有没有机会去修复那个数据设计?您将日期作为

Mongodb o以有意义的方式“查询”任何内容,而不引入不必要的开销,这将破坏应用程序性能,并大大增加代码维护的复杂性,然后使用值而不是键来识别那些有意义的数据点,而不是以这种方式使用。您尝试了什么?请考虑阅读和编辑你的问题。你有没有机会去修复那个数据设计?您将日期作为,mongodb,aggregation-framework,Mongodb,Aggregation Framework,o以有意义的方式“查询”任何内容,而不引入不必要的开销,这将破坏应用程序性能,并大大增加代码维护的复杂性,然后使用值而不是键来识别那些有意义的数据点,而不是以这种方式使用。您尝试了什么?请考虑阅读和编辑你的问题。你有没有机会去修复那个数据设计?您将日期作为字符串作为键(lvals)。你不能轻易地对此提出质疑,更不用说agg了。您的文档将得到更好的服务,如{date:ISODate(“20121207”),value:“value1”},这样您的查询就变得简单了。不,日期是字符串格式的,只有最后才


o以有意义的方式“查询”任何内容,而不引入不必要的开销,这将破坏应用程序性能,并大大增加代码维护的复杂性,然后使用而不是来识别那些有意义的数据点,而不是以这种方式使用。

您尝试了什么?请考虑阅读和编辑你的问题。你有没有机会去修复那个数据设计?您将日期作为字符串作为键(lvals)。你不能轻易地对此提出质疑,更不用说agg了。您的文档将得到更好的服务,如
{date:ISODate(“20121207”),value:“value1”}
,这样您的查询就变得简单了。不,日期是字符串格式的,只有最后才可以完成。您确实应该避免创建多个管道阶段,这些管道阶段可以连续执行相同的操作(例如,
$project
)。您还可以使用
$toDate
,它实际上可以识别
dd-mm-YYYY
。实际上,您错过了问题中关于使用输入对象创建查询的部分。不完全是OP所要求的,但在多个管道上进行了一次很好的尝试!有时我使用“更大”的函数来递增地显示正在发生的事情,因为MQL的新手有时无法遵循
$maps的
$filters
$maps
,等等。我想OP说他需要传递开始和结束日期,而不是从输入文档获取它。。。。?但最后:你下面的全面回答明确了一点:我们希望在设计中使用值,而不是键。解释得很好。谢谢我有一个查询,在输入列中,但COL是动态的,我只能传递开始和结束日期。你能帮我在哪里做修改吗
{
    col1: {
        12 - 02 - 2019: val1,
        14 - 02 - 2019: val3
    },
    col2: {
        12 - 02 - 2019: val1,
        14 - 02 - 2019: val3
    },
    col3: {
        12 - 02 - 2019: val1,
        14 - 02 - 2019: val3
    }
}
{
    _id: ObjectId('65656222dss5ds'),
    data: {
        col1: {
            '12-07-2012': 'value1',
            '13-07-2012': 'value2',
            '14-07-2012': 'value3',
            '15-07-2012': 'value5'
        },
        col2: {
            '12-07-2012': 'value1',
            '13-07-2012': 'value2',
            '14-07-2012': 'value3',
            '15-07-2012': 'value5'
        },
        col3: {
            '12-07-2012': 'value1',
            '13-07-2012': 'value2',
            '14-07-2012': 'value3',
            '15-07-2012': 'value5'
        }
    }
}
c = db.foo.aggregate([
// Start the journey of turning lvals into rvals...                          
{$project: {x: {$objectToArray: "$$CURRENT.data"}}}

// ... and do it again!                                                      
,{$project: {QQ: {$map: {
                input: "$x",
                as: "z",
                in: {
                    vv: {$objectToArray: "$$z.v"},
                    colk: "$$z.k"
                }
            }}
    }}

// At this point we have no more lvals of interest, but we have too          
// many arrays.  Let's simplify and turn it into individual docs:            
,{$unwind: "$QQ"}

// At this point we have a bunch of docs where QQ.colk is the collection     
// key and QQ.vv is an array of (k,v) value pairs of (string date, value):    
//    {                                                                      
//      "_id" : 1,                                                         
//      "QQ" : {                                                           
//        "vv" : [                                                           
//          {"k" : "12-07-2012",  "v" : "value44"},                          
//          {"k" : "13-07-2012",  "v" : "value45"},                          
//          {"k" : "14-07-2012",  "v" : "value46"},                          
//          {"k" : "15-07-2012",  "v" : "value47"                            
//           ],                                                              
//        "colk" : "col3"                                                    
//        }                                                                  
//   }                                                                       
//       
// OK.  Now it is time to turn those DD-MM-YYYY strings into dates so we     
// can do a proper filter.  We do so by running the QQ.vv array through      
// the $map function and using $dateFromParts + $substr to make a date.      
// Note that we "reuse" projected field QQ (i.e. input was QQ and the        
// project is QQ, sort of like saying QQ = f(QQ) ) and just keep carrying    
// along colk:                                                               
,{$project: {QQ: {$map: {
                input: "$QQ.vv",
                as: "z",
                in: {
                    v: "$$z.v",
                    d: {$dateFromParts : {
                            "year":  {$toInt: {$substr: ["$$z.k",6,4]}},
                            "month": {$toInt: {$substr: ["$$z.k",3,2]}},
                            "day":   {$toInt: {$substr: ["$$z.k",0,2]}}
                        }}
                }
            }},
             colk: "$QQ.colk"
    }}


// We now have filterable dates in an array associated with colk.            
// Now we can filter!  I hardcode the dates here but it should be clear this is
// where variables would come into play:                                                  
,{$project: {QQ: {$filter: {
                input: "$QQ",
                as: "zz",
                cond: { $and: [
{$gt: [ "$$zz.d", new ISODate("20120713") ]},
{$lt: [ "$$zz.d", new ISODate("20120716") ]}
                               ]}
            }},
             colk: "$colk"
    }}



// Almost home!   Now: reconstitute the collection key (colk):               
,{$group: {_id: "$colk", members: {$push: "$QQ"} }}

   ]);
{                                                                            
  "_id" : "col1",                                                            
  "members" : [                                                              
    [                                                                        
      {                                                                      
        "v" : "value3",                                                      
        "d" : ISODate("2012-07-14T00:00:00Z")                                
      },                                                                     
      {                                                                      
        "v" : "value5",                                                      
        "d" : ISODate("2012-07-15T00:00:00Z")                                
      }                                                                      
    ],                                                                       
    [                                                                        
      {                                                                      
        "v" : "value22",                                                     
        "d" : ISODate("2012-07-14T00:00:00Z")                                
      },                                                                     
      {                                                                      
        "v" : "value23",                                                     
        "d" : ISODate("2012-07-15T00:00:00Z")                                
      }                                                                      
    ]                                                                        
  ]                                                                          
}                                                                          ```
const { MongoClient } = require('mongodb');

const url = 'mongodb://localhost:27017';
const opts = { useNewUrlParser: true, useUnifiedTopology: true };

// Basic logging helper
const log = data => console.log(JSON.stringify(data, undefined, 2));

// Sample document
const data = {
  data: {
    col1: {
      '12-07-2012': 'value1',
      '13-07-2012': 'value2',
      '14-07-2012': 'value3',
      '15-07-2012': 'value5'
    },
    col2: {
      '12-07-2012': 'value1',
      '13-07-2012': 'value2',
      '14-07-2012': 'value3',
      '15-07-2012': 'value5'
    },
    col3: {
      '12-07-2012': 'value1',
      '13-07-2012': 'value2',
      '14-07-2012': 'value3',
      '15-07-2012': 'value5'
    }
  }
};

// Sample input conditions
const input = {
  col1: {
    '12-07-2012': 'value1',   // clearly pairs of "from" and "to"
    '14-07-2012': 'value3'
  },
  col2: {
    '12-07-2012': 'value1',
    '14-07-2012': 'value3'
  },
  col3: {
    '12-07-2012': 'value1',
    '14-07-2012': 'value3'
  }
};

// Helper for converting strings to valid ISO dates
const toDate = dateStr => new Date(dateStr.split("-").reverse().join("-"));

//  Helper for the $filter arguments for $or
const makeCond = input => Object.entries(input)
  // get key and value pairs of object and make an array per 'key'
  .map(([k,v]) =>
    ({
      // Reduce the v objects as key value pairs into a single array
      '$and': Object.entries(v).reduce((o, [k,v], i) =>
        [
          ...o,     // spread the reduced array

          // Add and spread these new array elements
          ...[
            // Use $gte or $lte depending on current index
            { [(i == 0) ? '$gte' : '$lte']: [ '$$this.date', toDate(k) ] },
            { [(i == 0) ? '$gte' : '$lte']: [ '$$this.value', v ] }
          ]
        ],
        // The initial array for reduce
        [{ '$eq': [ '$$this.col', k ] }])
    })
  );

const makeOrCondition = input => Object.entries(input)
  .map(([col,v]) =>
    ({
      col,
      date: Object.keys(v).reduce((o,k,i) =>
        ({ ...o, [(i == 0) ? '$gte' : '$lte']: toDate(k) }), {}),
      value: Object.values(v).reduce((o,v,i) =>
        ({ ...o, [(i == 0) ? '$gte': '$lte']: v }), {})
    })
  );

(async function() {

  let client;

  try {
    client = await MongoClient.connect(url, opts);

    let db = client.db('test');

    await db.collection('example').deleteMany({});
    await db.collection('example').insertOne(data);

    // Debug the makeCond
    //log(makeCond(input));

    // Covert objects to arrays of arrays
    const mapObjects = {
      '$map': {
        'input': { '$objectToArray': '$data' },
        'in': {
          '$let': {
            'vars': { 'col': '$$this.k' },
            'in': {
              '$map': {
                'input': { '$objectToArray': '$$this.v' },
                'in': {
                  'col': '$$col',
                  'date': { '$toDate': '$$this.k' },
                  'value': '$$this.v'
                }
              }
            }
          }
        }
      }
    };

    // Flatten arrays of arrays to single array
    const joinArrays = {
      '$reduce': {
        'input': mapObjects,
        'initialValue': [],
        'in': { '$concatArrays': [ '$$value', '$$this' ] }
      }
    };

    // Apply the filter to the array elements
    const filterArray = {
      '$filter': {
        'input': joinArrays,
        'cond': { '$or': makeCond(input) }
      }
    };

    // Basically an inline version of $group
    const grouper = {
      '$reduce': {
        'input': filterArray,
        'initialValue': [],
        'in': {
          '$let': {
            'vars': { 'current': '$$this' },
            'in': {
              '$concatArrays': [
                //  Filter reduce output from the matching col
                { '$filter': {
                  'input': '$$value',
                  'cond': { '$ne': [ '$$current.col', '$$this.k' ] }
                }},
                // Conditionally join to:
                { '$cond': {
                  'if': {
                    '$ne': [
                      { '$indexOfArray': [
                        '$$value.k', '$$this.col'
                      ]},
                      -1
                    ]
                  },
                  // Concat the inner array where matched
                  'then': [{
                    'k': '$$this.col',
                    'v': {
                      '$concatArrays': [
                        { '$arrayElemAt': [
                          '$$value.v',
                          { '$indexOfArray': ['$$value.k', '$$this.col'] }
                        ]},
                        [{ 'k': '$$this.date', 'v': '$$this.value' }]
                      ]
                    }
                  }],
                  // Create the inner array where not matched
                  'else': [{
                    'k': '$$this.col',
                    'v': [{
                      'k': '$$this.date',
                      'v': '$$this.value'
                    }]
                  }]
                }}
              ]
            }
          }
        }
      }
    };

    const pipeline = [
      { '$match': {
        '$expr': { '$gt': [{ '$size': filterArray }, 0] }
      }},
      { '$project': {
        'data': {
          '$arrayToObject': {
            '$map': {
              'input': grouper,
              'in': {
                // reformat
                'k': '$$this.k',
                'v': {
                  '$arrayToObject': {
                    '$map': {
                      'input': '$$this.v',
                      'in': {
                        'k': {
                          '$dateToString': {
                            'date': '$$this.k',
                            'format': '%d-%m-%Y'
                          }
                        },
                        'v': '$$this.v'
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }}
    ];

    log(pipeline);

    let result = await db.collection('example').aggregate(pipeline).toArray();
    log(result);

    // Create example2

    await db.collection('example').aggregate([
      { '$project': { 'data': joinArrays } },
      { '$out': 'example2' }
    ]).toArray();


    /*
     * Simple $elemMatch and $filter usage when already an array
     *
     */
    let result2 = await db.collection('example2').aggregate([
      { '$match': {
        'data': {
          '$elemMatch': {
            '$or': makeOrCondition(input)
          }
        }
      }},
      { '$project': {
        'data': {
          '$filter': {
            'input': '$data',
            'cond': { '$or': makeCond(input) }
          }
        }
      }}
    ]).toArray();

    log(result2);

    // Create example3
    await db.collection('example2').aggregate([
      { '$unwind': '$data' },
      { '$replaceRoot': { 'newRoot': '$data' } },
      { '$out': 'example3' }
    ]).toArray();

    /*
     * Really simple when the elements are discreet documents
     * in their own collection
     */

    let result3 = await db.collection('example3').find({
      '$or': makeOrCondition(input)
    }).toArray();

    log(result3);

  } catch (e) {
    console.error(e);
  } finally {
    if (client)
      client.close();
  }

})()
  {
    "_id": "5d6a7ac8736dce1c76d9d3e8",
    "data": {
      "col1": {
        "12-07-2012": "value1",
        "13-07-2012": "value2",
        "14-07-2012": "value3"
      },
      "col2": {
        "12-07-2012": "value1",
        "13-07-2012": "value2",
        "14-07-2012": "value3"
      },
      "col3": {
        "12-07-2012": "value1",
        "13-07-2012": "value2",
        "14-07-2012": "value3"
      }
    }
  }
// Covert objects to arrays of arrays
const mapObjects = {
  '$map': {
    'input': { '$objectToArray': '$data' },
    'in': {
      '$let': {
        'vars': { 'col': '$$this.k' },
        'in': {
          '$map': {
            'input': { '$objectToArray': '$$this.v' },
            'in': {
              'col': '$$col',
              'date': { '$toDate': '$$this.k' },
              'value': '$$this.v'
            }
          }
        }
      }
    }
  }
};
// Apply the filter to the array elements
const filterArray = {
  '$filter': {
    'input': joinArrays,
    'cond': { '$or': makeCond(input) }
  }
};