# Display, search and update attributes from a JSON field

This example shows you how to display information from a JSON field using smart fields.

‌ Here a `user` record has a field `contact` of the type JSONB that includes an `email` and a `phone` attribute. On the `users` collection, we want to implement smart fields (called `email` and `phone`) that will display the value of information contained within the JSON as separate fields and be able to update these fields.

{% embed url="<https://www.loom.com/share/14ff13645ecf4104a54fd82753235782>" %}

\
Requirements
------------

‌

* An admin backend running on `forest-express-sequelize`
* A PostgreSQL database including a table with a JSONB column

‌

\
How it works <a href="#how-it-works" id="how-it-works"></a>
-----------------------------------------------------------

### Directory: /models

This directory contains the `users.js` file where the model is declared.

{% code title="/models/users.js" %}

```javascript
module.exports = (sequelize, DataTypes) => {
  const { Sequelize } = sequelize;
  const Users = sequelize.define('users', {
    contact: {
      type: DataTypes.JSONB,
    },
  }, {
    tableName: 'users',
    timestamps: false,
    schema: process.env.DATABASE_SCHEMA,
  });

  Users.associate = (models) => {
  };

  return Users;
};
```

{% endcode %}

### **Directory: /forest**

This directory contains the `users.js` file where the smart fields `email` and `phone` are implemented.&#x20;

The `get()` method retrieves the value that will be displayed in the UI and the `set()` method allows to specify the logic to be followed with regards to the input value entered upon edit. Here we will handle the update at the routes level so we just pass the object as a result of the `set()` method.&#x20;

{% code title="/forest/users.js" %}

```javascript
const { collection } = require('forest-express-sequelize');

collection('users', {
  actions: [],
  fields: [
    {
      field: 'email',
      type: 'String',
      get: (object) => {
        return object.contact ? object.contact.email : null;
      },
      search: (query, search) => {
        // Add search on contact->email field of the JSONB
        const searchCondition = {
          'contact.email': { [Op.iLike]: `%${search}%` },
        };
  
        query.where[Op.and][0][Op.or].push(searchCondition);
        return query;
      },    
      set: (object, email) => {
        return object;
      },
    }, {
      field: 'phone',
      type: 'String',
      get: (object) => {
        return object.contact ? object.contact.phone : null;
      },
      set: (object, phone) => {
        return object;
      },
    },
  ],
  segments: [],
});
```

{% endcode %}

### **Directory: /routes**

This directory contains the `users.js` file where the implementation of the PUT route for the update action is handled.&#x20;

{% hint style="info" %}
Note that a JSON field cannot be updated attribute per attribute, it needs to be updated as a whole as not to erase the values already contained in it.
{% endhint %}

{% code title="/routes/users.js" %}

```javascript
const express = require('express');
const { PermissionMiddlewareCreator, RecordGetter } = require('forest-express-sequelize');
const { users } = require('../models');

const router = express.Router();
const permissionMiddlewareCreator = new PermissionMiddlewareCreator('users');
//...


// Method to update a JSON object
function updateJson(record, formAttributes, jsonField, jsonAttributes) {
  const json = record[jsonField] || {};
  jsonAttributes.forEach((attribute) => {
    json[attribute] = attribute in formAttributes ? formAttributes[attribute] : json[attribute];
  });
  return json;
}

// Update a User
router.put('/users/:recordId', permissionMiddlewareCreator.update(), (request, response, next) => {
  const recordGetter = new RecordGetter(users, request.user, request.query);
  const attr = request.body.data.attributes;
  recordGetter.get(request.params.recordId).then((record => {
    attr.contact = updateJson(record, attr, 'contact', ['email', 'phone']);
    return record.update(attr);
  }))
  .then((recordUpdated) => recordGetter.serialize(recordUpdated))
  .then((recordSerialized) => response.send(recordSerialized))
  .catch(next);
});

//...

module.exports = router;
```

{% endcode %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.forestadmin.com/woodshop/how-tos/update-attributes-from-a-jsonb-field.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
