0

I was wondering if was possible to utilize functions inside of MongoDB query by including a function inside of options block or something.

Suppose my database collection has a document:

{"year": "2320"}

And I have function to format this document's year-field:

const reformat = function(input){
 return input.year.substring(2,4)+input.year.substring(0,2)
}

And I'll be calling a fetch function like this:

const test = {"year": "2023"}
fetchSomething(test, {}, reformat)

My fetch function looks like this:

async function fetchSomething(query, projection, options){
  const dat = await mongoCli.db("testDB").collection("testCollection").findOne(query, projection, options);
  return dat;
}

So, reformat-function should modify the database's document to match year-attribute of test-variable, not the other way around.

I'm not sure if MongoDB even supports it. Looking at the MongoDB documentation...

12
  • 3
    your code is not clear to me, 1) you have {"year": "2320"} in the collection and your query is for {"year": "2023"}, 2) you are passing blank {} in projection 3) you are passing that function in options. ---- find query will not support external function in project, and you can not use internal property in that function, you can use operators for substing in projection and that will solve your problem if you explain your actual problem in your question. Commented Feb 4, 2023 at 13:41
  • @turivishal projection blank will return entire document, pay no attention to it. Option parameter is just a placeholder, I didn't know what to put to it. If the external query isn't indeed supported, I should use cursor? Though .findOne().pretty() says there is no such function Commented Feb 4, 2023 at 13:52
  • Where are you executing this query? shell, or which driver code. Commented Feb 4, 2023 at 13:55
  • 1
    @Chlodio your "fetch" behaviour is achieved by the $lookup. You can continue your logic inside the sub-pipeline. In addition, there is $function in MongoDB. However, it is not recommended to use that if there are MongoDB alternatives since there will be a performance impact. Commented Feb 4, 2023 at 15:08
  • 2
    I am not getting the exact requirement. forgot the code, can you explain what is the exact requirement, like example data, and the expected results from it? Commented Feb 4, 2023 at 15:35

2 Answers 2

2

All you could do is like this:

const reformat = function(){
 return {allowDiskUse: true}
}

mongoCli.db("testDB").collection("testCollection").findOne(query, projection, reformat()); 

The option is an input attribute of findOne, you cannot re-define it.

Or you can use $function in aggregation pipeline:

db.mongoCli.aggregate([
   {
      $set: {
         year: {
            $function: {
               body: function (input) {
                  return input.year.substring(2, 4) + input.year.substring(0, 2)
               },
               args: ["$$ROOT"],
               lang: "js"
            }
         }
      }
   }
])

Please be aware, executing JavaScript inside an aggregation expression may decrease performance. Only use the $function operator if the provided pipeline operators cannot fulfill your application's needs. $substrCP and $concat are also available as pipeline operator.

Sign up to request clarification or add additional context in comments.

2 Comments

Um... How would that accomplish return input.year.substring(2,4)+input.year.substring(0,2?
@Wernfriend I suppose that is a working a solution in general sense, but it isn't really what I was after, albeit that was my fault for not explaining it better. The idea was that I could reuse the fetchSomething by definition different functions for it. But turns out I can accomplish this with cursor.
0

I now realize is that I can accomplish it with a cursor:

const reformat = function(input){
 return input.year.substring(2,4)+input.year.substring(0,2)
}

async function fetchSomething(query, options){
  const cursor = await mongoCli.db("testDB").collection("testCollection").find()
  let doc;
  while (await cursor.hasNext()) {
    doc = await cursor.next();
    if (options(doc) == query.year){ break; }
    else { doc = null; }
  }
  cursor.close();
  return doc;
}
const test = {"year": "2023"}
fetchSomething(test, reformat)

Though there might be a better way to do it.

5 Comments

We are still not clear about your requirement so probably can't comment on the correctness. It seems to me it's not performant as you are always doing collection scan and performing filtering on application level.
I agree with @ray, performance wise this is most likely the worst you can do. And it not at all related to your question. Simple mongoCli.db("testDB").collection("testCollection").findOne(test) would do exactly the same.
@WernfriedDomscheit, it would not do the exact same. Remember that, test's year attribute is '2023', while the document's server attribute is '2320', therefore the query would return nothing, while this approach returns the document because the reformat-function transforms '2320' to '2023' so it matches. Agree it isn't ideal, but you aren't giving me alternatives.
Then it is mongoCli.db("testDB").collection("testCollection").findOne({year: reformat(test.year)})
While that would result in the correct document, the point was not to alter test

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.