skip to Main Content

I have large JSON object returned by my nodejs API server. I want to give users ability to query subset of fields similar to GraphQL but without schema. Object that is returned by API have set of standard fields but also set of any custom fields that users can add to its "custom" property. I looked at GraphQL and as far as i understand it requires descriptor that defines all fields by which you can query. Unfortunately it will not work since "custom" field can have anything. I want for user to send query along with GET request that escribes what fields i want include and what i want to exclude. For example if object has:

{
  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
      }
    ]
  }
}

Now if I want everything under user name and book planet name I would send something like:

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

But if I want everything under book except heroes I would send:

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

Anyone knows library that have something close to it?

2

Answers


  1. Here’s an approach

    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}));
    Login or Signup to reply.
  2. You can try the below logic.

    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))
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search