Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

criteria factory suggestion #393

Conversation

baksale
Copy link

@baksale baksale commented Feb 8, 2022

This change is Reviewable

Hi guys, please take a look at the suggestion for Criteria Factory.
If you like the idea, I will add security checks and bind variables support.

Any other feedback is also welcome.
This is related to #292

    String criteriaAsString =
      'IsWon = true AND Amount > 100000 AND' +
      ' ((Account.NumberOfEmployees > 1000 AND Account.BillingCountry = \'USA\') OR (Owner.Title = \'CEO\' AND Owner.Country = \'USA\'))';

    fflib_CriteriaFactory bigUsCompanies = new fflib_CriteriaFactory()
      .configureForReferenceField(Opportunity.AccountId)
      .greaterThan(Account.NumberOfEmployees, 1000)
      .equalsTo(Account.BillingCountry, 'USA');

    fflib_CriteriaFactory ownerInUs = new fflib_CriteriaFactory()
      .configureForReferenceField(Opportunity.OwnerId)
      .equalsTo(User.Title, 'CEO')
      .equalsTo(User.Country, 'USA');

    fflib_CriteriaFactory cb = new fflib_CriteriaFactory()
      .equalsTo(Opportunity.IsWon, true)
      .greaterThan(Opportunity.Amount, 100000)
      .composite(
            new fflib_CriteriaFactory()
                  .composite(bigUsCompanies)
                  .withOr()
                  .composite(ownerInUs));

    System.assertEquals(criteriaAsString, cb.toCriteria());

More details can be found here

@wimvelzeboer
Copy link
Contributor

@baksale I like that this criteria/condition factory is a small class and resolves this long open issue.
But have you ever looked at this fflib_Critera class? It has a wider scope and can also be used for realtime evaluations in domain classes.

@baksale
Copy link
Author

baksale commented Feb 8, 2022

Thanks for the feedback @wimvelzeboer

fflib_Criteria looks interesting and much more solid.
Based on the code, I could see the class has 2 responsibilities:

  1. Construct criteria to be used in SOQL
  2. Construct criteria to evaluate against records in runtime

I would suggest to split it into 2 or even 3 classes:

  1. the criteria itself
  2. soql criteria converter
  3. evaluator

Evaluator class could contain a reference to Criteria class to do the evaluation of the records in runtime.

I did some load testing:

  1. on average fflib_CriteriaFactory consumes 4 times less heap vs fflib_Criteria
  2. cpu consumption is about the same or difference is not significant
    Can share the tests.

Two things I could not understand:

  1. Like and Not Like builder methods are absent, but saw the operator for Like - it is not supported yet, isn't it?
  2. Reference Attributes-based criteria construction - could not understand if it is supported or not; could not find builder methods.

fflib_CriteriaFactory alternatives for this:

cf().isNotLike(Contact.LastName, 'A%') // could not find analogue in fflib_Criteria
cf()
  .configureForReferenceField(Opportunity.AccountId) //similar to fflib_QueryFactory approach
  .greaterThan(Account.NumberOfEmployees, 1000)
  .equalsTo(Account.BillingCountry, 'USA')

We like fflib_QueryFactory but criteria build capability is missing and badly required there.

Do you think we can incorporate fflib_CriteriaFactory or fflib_Criteria into the main codebase?

Happy to incorporate any feedback if it is going to be useful.
I like the fields accessibility check inside fflib_QueryFactory, so would like to apply the same for Fields used in Criteria.

@baksale
Copy link
Author

baksale commented Feb 22, 2022

Hu @stohn777 @daveespo

Could you provide any hints to move this forward?

This is the example from the PR description:

   String criteriaAsString =
      'IsWon = true AND Amount > 100000 AND' +
      ' ((Account.NumberOfEmployees > 1000 AND Account.BillingCountry = \'USA\') OR (Owner.Title = \'CEO\' AND Owner.Country = \'USA\'))';

    fflib_CriteriaFactory bigUsCompanies = new fflib_CriteriaFactory()
      .configureForReferenceField(Opportunity.AccountId)
      .greaterThan(Account.NumberOfEmployees, 1000)
      .equalsTo(Account.BillingCountry, 'USA');

    fflib_CriteriaFactory ownerInUs = new fflib_CriteriaFactory()
      .configureForReferenceField(Opportunity.OwnerId)
      .equalsTo(User.Title, 'CEO')
      .equalsTo(User.Country, 'USA');

    fflib_CriteriaFactory cb = new fflib_CriteriaFactory()
      .equalsTo(Opportunity.IsWon, true)
      .greaterThan(Opportunity.Amount, 100000)
      .composite(
            new fflib_CriteriaFactory()
                  .composite(bigUsCompanies)
                  .withOr()
                  .composite(ownerInUs));

    System.assertEquals(criteriaAsString, cb.toCriteria());

I need some guidance how to move this forward.
If you like the idea - what are the next steps/feedback.

I saw in many other similar PRs/Issues you mentioned that you put decisions on hold as the strategy is not clear.
Can I help with it anyhow?

Copy link
Author

@baksale baksale left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Supports all Comparison Operators
  2. Compile Time validation of the Field Names used in SOQL Criteria
  3. Dynamic SOQL Criteria based on User input
  4. Bind Variables
  5. Date Functions support
  6. Runtime Exception if criteria size breaks the 4000 characters limit

@daveespo
Copy link
Contributor

See #483

@daveespo daveespo closed this Feb 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants