Filter on smart field/ smart relationship

{

  field: "projectID",

  type: "String",

  reference: "projects._id",

  isFilterable: true,

  get: function (result) {

    return projects.findById(result.projectID)._id;

  },

},

Expected behavior

I want to filter on the smart field using only the projectId (id of relation) but it forces a dropdown menu (sub field) with all the attributes of the relation and when i choose the id and try to filter with it i get the following error
[forest] :deciduous_tree::deciduous_tree::deciduous_tree: Unexpected error: “filter” method missing on smart field “projectID”

I want to be only able to filter by id which is a mongoose objectId what should i do

Context

"express": "~4.17.1",

"express-jwt": "6.0.0",

"forest-express-mongoose": "^8.0.0",

"mongoose": "~5.8.2",
  • Project Name: simulationService

Hello @mostafa_hatem,

Ok, you need to define a filter hooks in your Smart Field in order to provide a way to filter the smart filed.

You can find the related documentation here.

Let me know if it help !

Kind regards,
Morgan

but how to prevent it from having a subfield when filtering or allowing the subfield only to be the id

I also encountered this error
[forest] :deciduous_tree::deciduous_tree::deciduous_tree: Unexpected error: Cast to ObjectId failed for value “55f” at path “_id” for model “projects”
I expected it to find no results not give me a server error

{

  field: "projectID",

  type: "String",

  reference: "projects._id",

  isFilterable: true,

  get: function (result) {

    return projects.findById(result.projectID)._id;

  },

  filter({ condition, where }) {

    const value = !!condition.value && condition.value;

    switch (condition.operator) {

      case "in":

        return {

          projectID: value,

        };

      default:

        return null;

    }

  },

},

can you please explain how to use the filter function you can see the code above and the condition under in the screenshot and i got no results found


Also is there a way to to remove the second drop down which chooses a field in the relation and make it filter on the id by default? and the conditions dropdown show all the options but I only have one in the switch case so using other options gives error

Hello again @mostafa_hatem,

The projectID is a Smart Field (kind of a data you have built even if in your case it’s just an id of a related data) and when you filter your data this SmartField does not really exist in your record.

  • First thing first, does the get function work ? The code projects.findById(result.projectID)._id; seems weird since result does not have any projectID ?

  • Then for the filter part you need to implements the filters you want to be available. Here’s the list of all filters (equivalent to condition.operator in the code): not, greater_than, less_than, contains, starts_with, ends_with, not_contains, present, not_equal, blank, equal, includes_all, in

  • The you have very complicated business logic. You can use complexe queries (to compute idsThatMatchsProjectId) then to build the real filter condition return { _id: { $in: [ idsThatMatchsProjectId ] } }. We have an example for the segment that can inspire you.

https://docs.forestadmin.com/documentation/reference-guide/segments/smart-segments

Simple example (This should not work since I don’t understand if projectID is part of your record or not see my comment on get hook)

filter({ condition, where }) {
  const value = !!condition.value && condition.value;

  switch (condition.operator) {
    // Define equal operator
    case 'equal':
      return { projectID: value };

    default:
      return null;
    }
  }
},

And reading at this, I’m wondering. Do you want just to be able to filter on projectID ?

The answer is yes for filtering, you need to set isFilterable: false, for all the fields you don’t want your users to be able to filter.

projects.findById(result.projectID)._id this works fine as I get a link to the project in viewing and it works just fine

"And reading at this, I’m wondering. Do you want just to be able to filter on projectID ?

The answer is yes for filtering, you need to set isFilterable: false, for all the fields you don’t want your users to be able to filter."
what i actually meant is that the projectID is a relation so i Just want to filter on the id of the relation not the other attributes so i don’t want a subfield
sub

I just not quite understand why you need to do projects.findById(result.projectID)._id since projectID is the same value ? Am I right ?

Sorry, I didn’t understood it quite well. :confused:

The answer is no, (you can remove them if you disable isFilterable for the fields you don’t want to be filterable in the project collection)
but you can use the search hook and only make this field searchable ?

search(search) {
  // Example search with projectID start with the search of the user
  const startWithRegex = new RegExp('^' + search);
  return { projectID: startWithRegex };
},

// Or simple exact match
search(search) {
  return { projectID: search };
},

projects.findById(result.projectID)._id is a mistake it should be projects.findById(result.projectID)
as the field projectID is a smart relationship when I come to filter it shows me the subfield as shown in the screenshot when I implement the filter function as below and use the operator “is” with a value that is there it gives me no results found… do I have to implement the filter function in a different way because it is a smart relationship
{

  field: "projectID",

  type: "String",

  reference: "projects._id",

  isFilterable: true,

  get: function (result) {

    return projects.findById(result.projectID);

  },

  filter({ condition, where }) {

    const value = !!condition.value && condition.value; // array of ids

    switch (condition.operator) {

      case "in":

        return { projectID: { $in: value } };

      default:

        return null;

    }

  },

},

I realized that references attribute is enough for creating a smart relation and I don’t need a get function i misunderstood it but I still think I am implementing the filter function incorrectly so can anyone please help me with that

Hello @mostafa_hatem,

What do you want to implement ? I think you can remove this filter hook (function) too. If you don’t want a specific behaviour let the default one.

Let me know what you want to achieve.

Regards,
Morgan

I want the default behavior but if i remove the filter function, this error occurs Unexpected error: “filter” method missing on smart field “projectID”

I only want to implement default filtering on my smart relation field

I’m not sure to have all the informations to help you.

Can you share your mongoose model for this collection ? I’m wondering if you already define projectID in your model.

Plus, you can this the documentation to create native belong-to relationship.
Also, the documentation for smart relationship, the your are trying to implement.

Regards,
Morgan

results model:


// This model was generated by Forest CLI. However, you remain in control of your models.

// Learn how here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/models/enrich-your-models

module.exports = (mongoose, Mongoose) => {

  // This section contains the properties of your model, mapped to your collection's properties.

  // Learn more here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/models/enrich-your-models#declaring-a-new-field-in-a-model

  const schema = Mongoose.Schema(

    {

      resources: { type: [Mongoose.Schema.Types.ObjectId], ref: 'resources' },

      technologies: {

        type: [Mongoose.Schema.Types.ObjectId],

        ref: 'technologies',

      },

      loads: { type: [Mongoose.Schema.Types.ObjectId], ref: 'loads' },

      carriers: { type: [Mongoose.Schema.Types.ObjectId], ref: 'carriers' },

      lifeTime: Number,

      projectID: Mongoose.Schema.Types.ObjectId,

    },

    {

      timestamps: false,

    }

  )

  return mongoose.model('results', schema, 'results')

}

project model in another database i want a smart relation with:


// This model was generated by Forest CLI. However, you remain in control of your models.

// Learn how here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/models/enrich-your-models

module.exports = (mongoose, Mongoose) => {

  // This section contains the properties of your model, mapped to your collection's properties.

  // Learn more here: https://docs.forestadmin.com/documentation/v/v6/reference-guide/models/enrich-your-models#declaring-a-new-field-in-a-model

  const schema = Mongoose.Schema({

    'city': String,

    'company': String,

    'created_at': Date,

    'configuration': {

      'general': {

        'lifetime': Number,

        'interestRate': Number,

        'kpi': {

          'cpx': Object,

          'opx': Object,

        },

        'latitude': Object,

        'longitude': Object,

        'inflationRate': Number,

        'budget': String,

        'notes': String,

        'fixedOpex': String,

        'fixedCapex': String,

      },

      'revision': Number,

      'system': {

        'budget': Number,

        'network': {

          'revision': Number,

        },

      },

    },

    'icon': String,

    'street': String,

    'importance_capex': Number,

    'status': String,

    'importance_opex': Number,

    'capex': Boolean,

    'interest': Number,

    'electrical': Boolean,

    'updated_at': Date,

    'country': String,

    'heating': Boolean,

    'coo': Boolean,

    'importance_coo': Number,

    'domain': String,

    'cooling': Boolean,

    'lifetime': Number,

    'title': String,

    'discount': Number,

    'email': String,

    'opex': Boolean,

    'author': String,

    'description': String,

    'user_id': Number,

    'zipcode': Number,

  }, {

    timestamps: false,

  });

  return mongoose.model('projects', schema, 'projects');

};

fields array in forest/results file:


fields: [

    {

      field: "projectID",

      type: "String",

      reference: "projects._id",

      isFilterable: true,

    },

  ],

the filtration is not working as it needs a filter function but i am not quite sure how to implement this on smart relation as a sub field appears in the filter

Hello @mostafa_hatem,

It’s much clear now. You have already a relationship in your code. projectID is already defined in your model. That why you can of struggle with SmartFields.

SOLUTION 1
In your first model (results) replace

projectID: Mongoose.Schema.Types.ObjectId,

by a true belongsTo relationship

'projects_id': { type: Mongoose.Schema.Types.ObjectId, ref: 'projects' },

SOLUTION 2
(I do not advice other solution ! It would make your code harder than it needs to be.)

Regards,
Morgan

solution 1 is not possible as they are in different databases so that is why i used a smart relation

My bad @mostafa_hatem,

So you need the SOLUTION 2… I think the best solution for you is to add a search hook and not filters ! I already gave the answer.

Just to have a cleaner example, I change the name of the SmartField and it uses the Field projectID in the get function. And it defines a search hook only searching the projectID of your record.

{
  field: "project_id",
  type: "String",
  reference: "projects._id",
  isSearchable: true,
  get: function (record) {
    return record.projectID;
  },
  search(search) {
    // Example search with projectID start with the search of the user
    const startWithRegex = new RegExp('^' + search);
    return { projectID: startWithRegex };
  },
}

Tell me if it help.

Regards,
Morgan