Moongoose aggregate $match does not match id's











up vote
9
down vote

favorite
1












I want to show products by ids (56e641d4864e5b780bb992c6 and 56e65504a323ee0812e511f2) and show price after subtracted by discount if available.



I can count the final price using aggregate, but this return all document in a collection, how to make it return only the matches ids



"_id" : ObjectId("56e641d4864e5b780bb992c6"), 
"title" : "Keyboard",
"discount" : NumberInt(10),
"price" : NumberInt(1000)

"_id" : ObjectId("56e65504a323ee0812e511f2"),
"title" : "Mouse",
"discount" : NumberInt(0),
"price" : NumberInt(1000)

"_id" : ObjectId("56d90714a48d2eb40cc601a5"),
"title" : "Speaker",
"discount" : NumberInt(10),
"price" : NumberInt(1000)


this is my query



productModel.aggregate([
{
$project: {
title : 1,
price: {
$cond: {
if: {$gt: ["$discount", 0]}, then: {$subtract: ["$price", {$divide: [{$multiply: ["$price", "$discount"]}, 100]}]}, else: "$price"
}

}
}
}
], function(err, docs){
if (err){
console.log(err)
}else{
console.log(docs)
}
})


and if i add this $in query, it returns empty array



productModel.aggregate([
{
$match: {_id: {$in: ids}}
},
{
$project: {
title : 1,
price: {
$cond: {
if: {$gt: ["$discount", 0]}, then: {$subtract: ["$price", {$divide: [{$multiply: ["$price", "$discount"]}, 100]}]}, else: "$price"
}

}
}
}
], function(err, docs){
if (err){
console.log(err)
}else{
console.log(docs)
}
})









share|improve this question




























    up vote
    9
    down vote

    favorite
    1












    I want to show products by ids (56e641d4864e5b780bb992c6 and 56e65504a323ee0812e511f2) and show price after subtracted by discount if available.



    I can count the final price using aggregate, but this return all document in a collection, how to make it return only the matches ids



    "_id" : ObjectId("56e641d4864e5b780bb992c6"), 
    "title" : "Keyboard",
    "discount" : NumberInt(10),
    "price" : NumberInt(1000)

    "_id" : ObjectId("56e65504a323ee0812e511f2"),
    "title" : "Mouse",
    "discount" : NumberInt(0),
    "price" : NumberInt(1000)

    "_id" : ObjectId("56d90714a48d2eb40cc601a5"),
    "title" : "Speaker",
    "discount" : NumberInt(10),
    "price" : NumberInt(1000)


    this is my query



    productModel.aggregate([
    {
    $project: {
    title : 1,
    price: {
    $cond: {
    if: {$gt: ["$discount", 0]}, then: {$subtract: ["$price", {$divide: [{$multiply: ["$price", "$discount"]}, 100]}]}, else: "$price"
    }

    }
    }
    }
    ], function(err, docs){
    if (err){
    console.log(err)
    }else{
    console.log(docs)
    }
    })


    and if i add this $in query, it returns empty array



    productModel.aggregate([
    {
    $match: {_id: {$in: ids}}
    },
    {
    $project: {
    title : 1,
    price: {
    $cond: {
    if: {$gt: ["$discount", 0]}, then: {$subtract: ["$price", {$divide: [{$multiply: ["$price", "$discount"]}, 100]}]}, else: "$price"
    }

    }
    }
    }
    ], function(err, docs){
    if (err){
    console.log(err)
    }else{
    console.log(docs)
    }
    })









    share|improve this question


























      up vote
      9
      down vote

      favorite
      1









      up vote
      9
      down vote

      favorite
      1






      1





      I want to show products by ids (56e641d4864e5b780bb992c6 and 56e65504a323ee0812e511f2) and show price after subtracted by discount if available.



      I can count the final price using aggregate, but this return all document in a collection, how to make it return only the matches ids



      "_id" : ObjectId("56e641d4864e5b780bb992c6"), 
      "title" : "Keyboard",
      "discount" : NumberInt(10),
      "price" : NumberInt(1000)

      "_id" : ObjectId("56e65504a323ee0812e511f2"),
      "title" : "Mouse",
      "discount" : NumberInt(0),
      "price" : NumberInt(1000)

      "_id" : ObjectId("56d90714a48d2eb40cc601a5"),
      "title" : "Speaker",
      "discount" : NumberInt(10),
      "price" : NumberInt(1000)


      this is my query



      productModel.aggregate([
      {
      $project: {
      title : 1,
      price: {
      $cond: {
      if: {$gt: ["$discount", 0]}, then: {$subtract: ["$price", {$divide: [{$multiply: ["$price", "$discount"]}, 100]}]}, else: "$price"
      }

      }
      }
      }
      ], function(err, docs){
      if (err){
      console.log(err)
      }else{
      console.log(docs)
      }
      })


      and if i add this $in query, it returns empty array



      productModel.aggregate([
      {
      $match: {_id: {$in: ids}}
      },
      {
      $project: {
      title : 1,
      price: {
      $cond: {
      if: {$gt: ["$discount", 0]}, then: {$subtract: ["$price", {$divide: [{$multiply: ["$price", "$discount"]}, 100]}]}, else: "$price"
      }

      }
      }
      }
      ], function(err, docs){
      if (err){
      console.log(err)
      }else{
      console.log(docs)
      }
      })









      share|improve this question















      I want to show products by ids (56e641d4864e5b780bb992c6 and 56e65504a323ee0812e511f2) and show price after subtracted by discount if available.



      I can count the final price using aggregate, but this return all document in a collection, how to make it return only the matches ids



      "_id" : ObjectId("56e641d4864e5b780bb992c6"), 
      "title" : "Keyboard",
      "discount" : NumberInt(10),
      "price" : NumberInt(1000)

      "_id" : ObjectId("56e65504a323ee0812e511f2"),
      "title" : "Mouse",
      "discount" : NumberInt(0),
      "price" : NumberInt(1000)

      "_id" : ObjectId("56d90714a48d2eb40cc601a5"),
      "title" : "Speaker",
      "discount" : NumberInt(10),
      "price" : NumberInt(1000)


      this is my query



      productModel.aggregate([
      {
      $project: {
      title : 1,
      price: {
      $cond: {
      if: {$gt: ["$discount", 0]}, then: {$subtract: ["$price", {$divide: [{$multiply: ["$price", "$discount"]}, 100]}]}, else: "$price"
      }

      }
      }
      }
      ], function(err, docs){
      if (err){
      console.log(err)
      }else{
      console.log(docs)
      }
      })


      and if i add this $in query, it returns empty array



      productModel.aggregate([
      {
      $match: {_id: {$in: ids}}
      },
      {
      $project: {
      title : 1,
      price: {
      $cond: {
      if: {$gt: ["$discount", 0]}, then: {$subtract: ["$price", {$divide: [{$multiply: ["$price", "$discount"]}, 100]}]}, else: "$price"
      }

      }
      }
      }
      ], function(err, docs){
      if (err){
      console.log(err)
      }else{
      console.log(docs)
      }
      })






      mongodb mongoose mongodb-query aggregation-framework






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Mar 24 '16 at 4:52









      Blakes Seven

      33.3k76072




      33.3k76072










      asked Mar 24 '16 at 4:27









      Muhammad Fasalir Rahman

      7319




      7319
























          1 Answer
          1






          active

          oldest

          votes

















          up vote
          24
          down vote



          accepted












          Your ids variable will be constructed of "strings", and not ObjectId values.



          Mongoose "autocasts" string values for ObjectId into their correct type in regular queries, but this does not happen in the aggregation pipeline, as in described in issue #1399.



          Instead you must do the correct casting to type manually:



          ids = ids.map(function(el) { return mongoose.Types.ObjectId(el) })


          Then you can use them in your pipeline stage:



          { "$match": { "_id": { "$in": ids } } }


          The reason is because aggregation pipelines "typically" alter the document structure, and therefore mongoose makes no presumption that the "schema" applies to the document in any given pipeline stage.



          It is arguable that the "first" pipeline stage when it is a $match stage should do this, since indeed the document is not altered. But right now this is not how it happens.



          Any values that may possibly be "strings" or at least not the correct BSON type need to be manually cast in order to match.






          share|improve this answer





















          • That's it, it now works. but usually i throw the id as string in findOneAndUpdate or other query, and it works fine, is the problem only happen in aggregate?
            – Muhammad Fasalir Rahman
            Mar 24 '16 at 4:59






          • 1




            @MuhammadFasalirRahman This is exactly what I answered with. A .find() can use the Schema which of course has a default type of ObjectId for the _id field. Aggregation pipelines do not use the Schema, as I actually already explained.
            – Blakes Seven
            Mar 24 '16 at 5:06










          • This is not working in mongoose 5
            – Mina Luke
            Feb 28 at 1:28










          • took me three days to get here; sigh.. I created a lamda inside my schema static method const castUserId = (userId) => mongoose.Types.ObjectId(userId) now i'm happy
            – timebandit
            Jun 19 at 17:23











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














           

          draft saved


          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f36193289%2fmoongoose-aggregate-match-does-not-match-ids%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          24
          down vote



          accepted












          Your ids variable will be constructed of "strings", and not ObjectId values.



          Mongoose "autocasts" string values for ObjectId into their correct type in regular queries, but this does not happen in the aggregation pipeline, as in described in issue #1399.



          Instead you must do the correct casting to type manually:



          ids = ids.map(function(el) { return mongoose.Types.ObjectId(el) })


          Then you can use them in your pipeline stage:



          { "$match": { "_id": { "$in": ids } } }


          The reason is because aggregation pipelines "typically" alter the document structure, and therefore mongoose makes no presumption that the "schema" applies to the document in any given pipeline stage.



          It is arguable that the "first" pipeline stage when it is a $match stage should do this, since indeed the document is not altered. But right now this is not how it happens.



          Any values that may possibly be "strings" or at least not the correct BSON type need to be manually cast in order to match.






          share|improve this answer





















          • That's it, it now works. but usually i throw the id as string in findOneAndUpdate or other query, and it works fine, is the problem only happen in aggregate?
            – Muhammad Fasalir Rahman
            Mar 24 '16 at 4:59






          • 1




            @MuhammadFasalirRahman This is exactly what I answered with. A .find() can use the Schema which of course has a default type of ObjectId for the _id field. Aggregation pipelines do not use the Schema, as I actually already explained.
            – Blakes Seven
            Mar 24 '16 at 5:06










          • This is not working in mongoose 5
            – Mina Luke
            Feb 28 at 1:28










          • took me three days to get here; sigh.. I created a lamda inside my schema static method const castUserId = (userId) => mongoose.Types.ObjectId(userId) now i'm happy
            – timebandit
            Jun 19 at 17:23















          up vote
          24
          down vote



          accepted












          Your ids variable will be constructed of "strings", and not ObjectId values.



          Mongoose "autocasts" string values for ObjectId into their correct type in regular queries, but this does not happen in the aggregation pipeline, as in described in issue #1399.



          Instead you must do the correct casting to type manually:



          ids = ids.map(function(el) { return mongoose.Types.ObjectId(el) })


          Then you can use them in your pipeline stage:



          { "$match": { "_id": { "$in": ids } } }


          The reason is because aggregation pipelines "typically" alter the document structure, and therefore mongoose makes no presumption that the "schema" applies to the document in any given pipeline stage.



          It is arguable that the "first" pipeline stage when it is a $match stage should do this, since indeed the document is not altered. But right now this is not how it happens.



          Any values that may possibly be "strings" or at least not the correct BSON type need to be manually cast in order to match.






          share|improve this answer





















          • That's it, it now works. but usually i throw the id as string in findOneAndUpdate or other query, and it works fine, is the problem only happen in aggregate?
            – Muhammad Fasalir Rahman
            Mar 24 '16 at 4:59






          • 1




            @MuhammadFasalirRahman This is exactly what I answered with. A .find() can use the Schema which of course has a default type of ObjectId for the _id field. Aggregation pipelines do not use the Schema, as I actually already explained.
            – Blakes Seven
            Mar 24 '16 at 5:06










          • This is not working in mongoose 5
            – Mina Luke
            Feb 28 at 1:28










          • took me three days to get here; sigh.. I created a lamda inside my schema static method const castUserId = (userId) => mongoose.Types.ObjectId(userId) now i'm happy
            – timebandit
            Jun 19 at 17:23













          up vote
          24
          down vote



          accepted







          up vote
          24
          down vote



          accepted








          Your ids variable will be constructed of "strings", and not ObjectId values.



          Mongoose "autocasts" string values for ObjectId into their correct type in regular queries, but this does not happen in the aggregation pipeline, as in described in issue #1399.



          Instead you must do the correct casting to type manually:



          ids = ids.map(function(el) { return mongoose.Types.ObjectId(el) })


          Then you can use them in your pipeline stage:



          { "$match": { "_id": { "$in": ids } } }


          The reason is because aggregation pipelines "typically" alter the document structure, and therefore mongoose makes no presumption that the "schema" applies to the document in any given pipeline stage.



          It is arguable that the "first" pipeline stage when it is a $match stage should do this, since indeed the document is not altered. But right now this is not how it happens.



          Any values that may possibly be "strings" or at least not the correct BSON type need to be manually cast in order to match.






          share|improve this answer














          Your ids variable will be constructed of "strings", and not ObjectId values.



          Mongoose "autocasts" string values for ObjectId into their correct type in regular queries, but this does not happen in the aggregation pipeline, as in described in issue #1399.



          Instead you must do the correct casting to type manually:



          ids = ids.map(function(el) { return mongoose.Types.ObjectId(el) })


          Then you can use them in your pipeline stage:



          { "$match": { "_id": { "$in": ids } } }


          The reason is because aggregation pipelines "typically" alter the document structure, and therefore mongoose makes no presumption that the "schema" applies to the document in any given pipeline stage.



          It is arguable that the "first" pipeline stage when it is a $match stage should do this, since indeed the document is not altered. But right now this is not how it happens.



          Any values that may possibly be "strings" or at least not the correct BSON type need to be manually cast in order to match.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Mar 24 '16 at 4:52









          Blakes Seven

          33.3k76072




          33.3k76072












          • That's it, it now works. but usually i throw the id as string in findOneAndUpdate or other query, and it works fine, is the problem only happen in aggregate?
            – Muhammad Fasalir Rahman
            Mar 24 '16 at 4:59






          • 1




            @MuhammadFasalirRahman This is exactly what I answered with. A .find() can use the Schema which of course has a default type of ObjectId for the _id field. Aggregation pipelines do not use the Schema, as I actually already explained.
            – Blakes Seven
            Mar 24 '16 at 5:06










          • This is not working in mongoose 5
            – Mina Luke
            Feb 28 at 1:28










          • took me three days to get here; sigh.. I created a lamda inside my schema static method const castUserId = (userId) => mongoose.Types.ObjectId(userId) now i'm happy
            – timebandit
            Jun 19 at 17:23


















          • That's it, it now works. but usually i throw the id as string in findOneAndUpdate or other query, and it works fine, is the problem only happen in aggregate?
            – Muhammad Fasalir Rahman
            Mar 24 '16 at 4:59






          • 1




            @MuhammadFasalirRahman This is exactly what I answered with. A .find() can use the Schema which of course has a default type of ObjectId for the _id field. Aggregation pipelines do not use the Schema, as I actually already explained.
            – Blakes Seven
            Mar 24 '16 at 5:06










          • This is not working in mongoose 5
            – Mina Luke
            Feb 28 at 1:28










          • took me three days to get here; sigh.. I created a lamda inside my schema static method const castUserId = (userId) => mongoose.Types.ObjectId(userId) now i'm happy
            – timebandit
            Jun 19 at 17:23
















          That's it, it now works. but usually i throw the id as string in findOneAndUpdate or other query, and it works fine, is the problem only happen in aggregate?
          – Muhammad Fasalir Rahman
          Mar 24 '16 at 4:59




          That's it, it now works. but usually i throw the id as string in findOneAndUpdate or other query, and it works fine, is the problem only happen in aggregate?
          – Muhammad Fasalir Rahman
          Mar 24 '16 at 4:59




          1




          1




          @MuhammadFasalirRahman This is exactly what I answered with. A .find() can use the Schema which of course has a default type of ObjectId for the _id field. Aggregation pipelines do not use the Schema, as I actually already explained.
          – Blakes Seven
          Mar 24 '16 at 5:06




          @MuhammadFasalirRahman This is exactly what I answered with. A .find() can use the Schema which of course has a default type of ObjectId for the _id field. Aggregation pipelines do not use the Schema, as I actually already explained.
          – Blakes Seven
          Mar 24 '16 at 5:06












          This is not working in mongoose 5
          – Mina Luke
          Feb 28 at 1:28




          This is not working in mongoose 5
          – Mina Luke
          Feb 28 at 1:28












          took me three days to get here; sigh.. I created a lamda inside my schema static method const castUserId = (userId) => mongoose.Types.ObjectId(userId) now i'm happy
          – timebandit
          Jun 19 at 17:23




          took me three days to get here; sigh.. I created a lamda inside my schema static method const castUserId = (userId) => mongoose.Types.ObjectId(userId) now i'm happy
          – timebandit
          Jun 19 at 17:23


















           

          draft saved


          draft discarded



















































           


          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f36193289%2fmoongoose-aggregate-match-does-not-match-ids%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          這個網誌中的熱門文章

          Xamarin.form Move up view when keyboard appear

          Post-Redirect-Get with Spring WebFlux and Thymeleaf

          Anylogic : not able to use stopDelay()