Recurse through arrays of objects based on the same key

Hey all,

I have been searching the internet forever trying to find an answer and nothing yet.

I want to return a document where some nested object field matches the search term. It would be a bonus if I could just return the nested object but the whole document is fine.

I have the following document for example, note how there are nested children within children, i want to traverse these children to look if the ‘slug’ property matches. I dont know how deep the nesting will go.

search term example: “backup-power/inverters/hybrid-inverters”

  {
    "_id": {
      "$oid": "641841c3ae5e0d1f81c21df4"
    },
    "name": "backup-power",
    "slug": "backup-power",
    "label": "Back-up Power",
    "children": [
      {
        "name": "batteries",
        "slug": "backup-power/batteries",
        "label": "Batteries",
        "parent": "641841c3ae5e0d1f81c21df4"
      },
      {
        "name": "inverters",
        "slug": "backup-power/inverters",
        "label": "Inverters",
        "parent": "641841c3ae5e0d1f81c21df4",
        "children": [
          {
            "name": "hybrid-inverters",
            "slug": "backup-power/inverters/hybrid-inverters",
            "label": "Hybrid Inverters",
            "parent": "6418bf4cae5e0d1f81c21dfc"
          }
        ]
      },
      {
        "name": "portable-power",
        "slug": "backup-power/portable-power",
        "label": "Portable Power",
        "parent": "641841c3ae5e0d1f81c21df4"
      }
    ]
  }

Any advice here? Should I just query the top level document and then on the server traverse the object? Should i build up a query on the server based on the slug that will tell mongodb exactly where to look? Or is there some query that can do this automatically?

First part of the problem finding the top level document.

The only way I can find so far is to create a query that leverages the number of slashes in your search term because I don’t think you can write one generic query. Something with the logic:

queried_field_variable = "slug" 

for each slash in search_term_variable
{
    queried_field_variable = "children." + queried_field_variable
}

db.collection.find( { [queried_field_variable] : search_term } )

As a remark, you seem to have a lot of duplicated data in your slug field as it seems it is a concatenation of the ancestors name. Personally, I would forgo the slug field and use the same logic to build a query such as:

query = { "name" : "backup-power" }
query = { ...query , "children.name" : "inverters" }
query = { ...query , "children.name.name" : "hybrid-inverters" }

you probably could leverage a much smaller index using name while leaving slug out. The fact that you may want to specify a search with slashes does not mean that your really have to have it in your data.

Using the above logic, for backup-power/inverters/hybrid-inverters, the queried_field_variable would be equal to children.children.slug.

Thanks, that was an idea I had.

What your query will do is return the whole document anyways, so if I match on a child object somewhere inside the document I will still get the entire document back unless I do an aggregation.

So what I decided to do was match on the top level document which was easy and then on my server I recursively loop through the children to find what I need - its not the most performant method but for now it will work.

In the future I can look to create a view or aggregation that does what I need.

I appreciate the response!

1 Like