0

If I have a flat data structure which uses dot notation to denote depth. It looks like so:

 { 
   "key1": "name1",
   "key2.subKey.finalKey": "name2"
 }

I'm using this to create my query:

let query = _.reduce(jsonQuery, (queryBody, queryValue, queryName)=>{
    return queryBody.query("term", "queryName", "queryValue")
}, bodybuilder())

I've simplified, but this in no way handles the pseudo-nested data.

I have a function that can translate the dot-notation to nested.

Here is how bodybuilder suggests doing nested structure:

bodybuilder()
  .query('nested', 'path', 'obj1', (q) => {
    return q.query('match', 'obj1.color', 'blue')
  })
  .build()

This would result in the following query structure:

{
  "query": {
    "nested": {
      "path": "obj1",
      "query": {
        "match": {
          "obj1.color": "blue"
        }
      }
    }
  }
}

So in my multi-nested version, I would hope to get:

{
  "query": {
    "nested": {
      "path": "key2",
      "query": {
        "nested": {
          "field": "key2.subkey",
          "query": {
            "match": {
              "key2.subkey.finalKey": "name2"
            }
          }
        }
      }
    }
  }
}

I'm struggling to brain how to do this dynamically based on the pseudo-nested structure above. Something recursive but I'm not sure how to make the fat-arrow functions .

Here is the closest I've gotten so far:

const nestQuery = (chain, value, method="match") =>{
 return q => q.query(method, chain, value)
}

const pathMapping = 'key2.subkey.finalKey';
const fooMapping = pathMapping.split('.').map((part, index, splitPath) =>{
 return splitPath.slice(0, index+1).join('.');
})

const body = bodybuilder();
  fooMapping.reduce((nestedBody, subPath, index, allPaths)=>{
    const next = allPaths[index+1] 
    return nestedBody.query('nested', subPath, 
               nestQuery(next, 'blue'))
  }, body)
  console.log(body.build())
<script src="https://rawgit.com/danpaz/bodybuilder/master/browser/bodybuilder.min.js"></script>

But this gives you three separate queries, when I want one nested query.

AncientSwordRage
  • 7,086
  • 19
  • 90
  • 173

1 Answers1

1

Your intuition to use reduce is indeed going in the right direction. But it will be much easier if you reduce in the opposite direction, starting from the innermost query (whose arguments look quite different from the other query calls), working backwards. This way you'll wrap the previously built callback into yet another callback function, working from the inside out:

function buildQuery(path, value) {
    // Build one part less, as the last one needs a different query syntax anyway
    //  ... and we have its value already in `path`:
    const keys = path.split('.').slice(0, -1).map((part, index, splitPath) => {
        return splitPath.slice(0, index+1).join('.');
    });

    // Use reduceRight to build the query from the inside out.
    const f = keys.reduceRight( (f, key) => {
        return q => q.query("nested", "path", key, f); // f is result of previous iteration
    }, q => q.query("match", path, value)); // Initial value is the innermost query

    return f(bodybuilder()).build();
}    

// Demo
console.log(buildQuery("key2.subkey.finalKey", "name2"));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://rawgit.com/danpaz/bodybuilder/master/browser/bodybuilder.min.js"></script>
trincot
  • 317,000
  • 35
  • 244
  • 286