Преобразовать точечную нотацию JSON в упорядоченный поисковый глубоко вложенный запрос JSON с помощью bodybuilder.js?

Если у меня есть плоская структура данных, которая использует точечную запись для обозначения глубины. Это выглядит так:

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

Я использую это для создания моего запроса:

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

Я упростил, но это никоим образом не обрабатывает псевдо-вложенные данные.

У меня есть функция, которая может переводить точечные обозначения во вложенные.

Вот как культурист предлагает создавать вложенные структуры:

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

Это приведет к следующей структуре запроса:

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

Так что в моей многоуровневой версии я надеюсь получить:

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

Я пытаюсь понять, как это сделать динамически, основываясь на псевдо-вложенной структуре выше. Что-то рекурсивное, но я не уверен, как заставить функционировать жирную стрелку.

Вот самое близкое, что я получил до сих пор:

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>

Но это дает вам три отдельных запроса, когда я хочу один вложенный запрос.

1 ответ

Решение

Ваша интуиция в использовании reduce действительно идет в правильном направлении. Но будет гораздо проще, если вы уменьшите в обратном направлении, начиная с самого внутреннего запроса (чьи аргументы выглядят совершенно иначе, чем другие query звонки), работает в обратном направлении. Таким образом, вы превратите ранее созданный обратный вызов в еще одну функцию обратного вызова, работая изнутри:

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>

Другие вопросы по тегам