如何提取 javascript 对象的子树

How to extract subtree of the javascript object

提问人:Andrei V 提问时间:9/20/2023 最后编辑:Andrei V 更新时间:9/22/2023 访问量:70

问:

我的nodejs API服务器返回了大型JSON对象。我想让用户能够查询类似于 GraphQL 但没有架构的字段子集。API 返回的对象具有一组标准字段,但也包含用户可以添加到其“自定义”属性的任何自定义字段。我查看了 GraphQL,据我了解,它需要定义所有字段的描述符,您可以通过这些字段进行查询。不幸的是,它不起作用,因为“自定义”字段可以包含任何内容。我希望用户发送查询以及GET请求,该请求描述了我想要包含哪些字段以及要排除哪些字段。例如,如果对象具有:

{
  user: {
    age: 25,
    weight: 170,
    name: "Joe"
  },
  book: {
    title: "Dune",
    description: "Space opera",
    localtion: {
      planet: "Arakis",
      population: 100000,
    },
    heroes: [
      {
        name: "Duke",
        age: 43
      },
      {
        name: "Paul",
        age: 16
      }
    ]
  }
}

现在,如果我想在用户名和书行星名称下的所有内容,我会发送类似的东西:

{'user.name': true, 'book.location.planet': true}

但是,如果我想在书中除英雄之外的所有内容,我会发送:

{'book': true, 'book.heroes': false}

有人知道图书馆有接近它的东西吗?

编辑: 理想情况下,它应该是查询数组元素:

{'book.heroes[0]': true} // extract first element
{'book.heroes[:last].name': true} // extract only name for the last element
{'book.heroes.name': true} // extract only names for all elements
JavaScript 对象 解析 GraphQL

评论

0赞 Bergi 9/22/2023
您仍然可以使用 GraphQL,只是不针对架构验证查询
0赞 Andrei V 9/22/2023
你能举个例子吗?

答:

1赞 Abito Prakash 9/20/2023 #1

这里有一种方法

const data={user:{age:25,weight:170,name:"Joe"},book:{title:"Dune",description:"Space opera",location:{planet:"Arakis",population:100000},heroes:[{name:"Duke",age:43},{name:"Paul",age:16},]}};


const extractData = (data, filters) => {
    const [pathsToInclude, pathsToExclude] = Object.keys(filters).reduce(
        (acc, keyPath) => {
            acc[filters[keyPath] ? 0 : 1].push(keyPath.split("."));
            return acc;
        },
        [[], []]
    );

    const result = pathsToInclude.length ? {} : { ...data };

    pathsToInclude.forEach((path) => {
        let current = path.shift();
        let obj = result;
        let dataObj = data;

        while (current && obj) {
            if (!path.length) {
                obj[current] = dataObj?.[current];
            } else {
                obj[current] ??= {};
                dataObj = dataObj[current];
            }
            obj = obj[current];
            current = path.shift();
        }
    });

    pathsToExclude.forEach((path) => {
        const keyToDelete = path.pop();

        let current = path.shift();
        let obj = result;

        while (obj[current]) {
            obj = obj[current];
            current = path.shift();
        }

        if (obj) {
            delete obj[keyToDelete];
        }
    });

    return result;
};

console.log(extractData(data, {"user.name": true,"book.location.planet": true}));

// Also works if you just want to return everything except some values
console.log(extractData(data, {"book.location": false}));

评论

0赞 Andrei V 9/20/2023
非常好的解决方案。很高兴找到允许更高级查询的库,例如按元素、索引或字段进行数组过滤,但如果不存在类似的东西,那么这对我来说是可靠的。谢谢!
0赞 Vineesh 9/20/2023 #2

您可以尝试以下逻辑。

let obj = {
  user: {
    age: 25,
    weight: 170,
    name: "Joe"
  },
  book: {
    title: "Dune",
    description: "Space opera",
    location: {
      planet: "Arakis",
      population: 100000,
    },
    heroes: [
      {
        name: "Duke",
        age: 43
      },
      {
        name: "Paul",
        age: 16
      }
    ]
  }
}
let fields1 = {'book': true, 'book.heroes': false}
let fields2 = {'user.name': true, 'book.location.planet': true}



function queryObject(obj, fields) {
  let newObj = {}
  Object.keys(fields).forEach(field => {
    const arr = field.split('.');
    if(fields[field]) {
      let partialObj = JSON.parse(JSON.stringify(obj))
      let addTo = newObj;
      for(let i=0;i<arr.length;i++) {
        if(i === arr.length-1) {
          addTo[arr[i]] = partialObj[arr[i]]
        } else {
            if(i==0) {
            newObj[arr[i]] = {}
           } else {
            addTo[arr[i]] = {}
           } 
          addTo = addTo[arr[i]]
          partialObj = partialObj[arr[i]]
        }
      }
    } else {
      let partialObj = newObj
      for(let i=0;i<arr.length;i++) {
        if(i === arr.length-1) {
          delete partialObj[arr[i]]
        } else {
          partialObj = partialObj[arr[i]]
        }
      }
    }
  })
  return newObj;
}

console.log(queryObject(obj, fields1))
console.log(queryObject(obj, fields2))