2

I want to query some specific non-consecutive indexes of an array in a MongoDB document.
The options I have considered are:

  1. Query the entire array, then filter by index in client script. This isn't viable as for a large array it will result in fetching unwanted items from the database (Suppose I need only index 5 and 20, but there are actually 1000 items in the array).
  2. Using $slice multiple times picking every time each of the indices I want from the array. But this results in N number of calls.
  3. Instead of a simple array, I can use a child schema where every item in the array has an assigned Id, and while querying, I can use "find" to select those specific ids. But this adds an extra "id" field which I do not want. I am sure there must be a way to pick items by the array index.

I am looking for a more elegant solution where I can pick certain specific indices from the array in a single operation.

Example document in Quiz model:

{
     questions: [
          {
               text: "Question 1",
               answer: "Answer 1",
          },
          {
               text: "Question 2",
               answer: "Answer 2",
          },
          ...
          ...
          {
               text: "Question 1000",
               answer: "Answer 1000",
          },
     ]
}

I want something in the lines of:

const questions = await Quiz.find({
     questions: {
          $index_in: [10, 42, 66]
     }
}).exec();

Expected output:

[
     {
          text: "Question 10",
          answer: "Answer 10",
     },
     {
          text: "Question 42",
          answer: "Answer 42",
     },
     {
          text: "Question 66",
          answer: "Answer 66",
     },
4
  • Can you add example for your data? Commented Aug 21, 2021 at 21:39
  • Are you trying to select a consecutive set of items? If so, a query, projection, then limit / offset should do it. If you are selecting several items at non-consecutive indexs I'm curious how you know the indexs in the first place? Commented Aug 21, 2021 at 21:40
  • @NenadMilosavljevic - I added some examples. Commented Aug 21, 2021 at 22:07
  • @robinsax - They are non-consecutive indexes. In an earlier operation, the code picks a few random indices (random questions for the quiz example I added) from the array of items, at which point we have the index numbers. At a later stage, client "submits" those questions back, which contains the index information. Now I need to query the particular index items again, to match the user submitted answer with the database. Hope I could explain. Commented Aug 21, 2021 at 22:07

2 Answers 2

3

If you want to take specific indexes from an array

Test code here

Query

Replace

  • [0,2,5] with the indexes you want (or a driver variable that is array)
  • instead of "$myarray", put your field name (in your case "$questions")

Its fast because $arrayElemAt is O(1)

db.collection.aggregate([
  {
    "$addFields": {
      "myarray": {
        "$map": {
          "input": [
            0,
            2,
            5
          ],
          "as": "i",
          "in": {
            "$arrayElemAt": [
              "$myarray",
              "$$i"
            ]
          }
        }
      }
    }
  }
])
Sign up to request clarification or add additional context in comments.

1 Comment

This works exactly as I was expecting. Also this is much faster than all the other options I have mentioned in my question, by more than 50%. Thank you so much.
0

You can do it with aggregation pipeline, like this:

const questions = await Quiz.aggregate([
  {
    $project: {
      items: {
        $filter: {
          input: "$questions",
          cond: {
            $in: [
              "$$this.text",
              [
                "Question 2",
                "Question 5"
              ]
            ]
          }
        }
      }
    }
  }
]);

Now, you can just dynamically populate wanted questions.

Here is the working example: https://mongoplayground.net/p/aXRiVSawpLf

1 Comment

I can't search by text as the texts may vary. I specifically need to search by index positions.

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.