简体   繁体   中英

Convert dot-notation JSON into elastic search deeply nested query JSON, with bodybuilder.js?

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.

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> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM