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

Support Apache Lucene Query equals/hashCode approach #854

Open
SharplEr opened this issue Sep 19, 2023 · 1 comment
Open

Support Apache Lucene Query equals/hashCode approach #854

SharplEr opened this issue Sep 19, 2023 · 1 comment

Comments

@SharplEr
Copy link

Problem:

I work on search engine based on Apache Lucene, and want to use EqualsVerifier to check equals and hashCode implementations in custom queries. Because bugs here could break query cache what may be hidden until very production.

Here how Query equals and hashCode calculation is done in Lucene:

// Here methods and field from org.apache.lucene.search.Query:

 protected final boolean sameClassAs(Object other) {
    return other != null && getClass() == other.getClass();
  }

  private final int CLASS_NAME_HASH = getClass().getName().hashCode();

  protected final int classHash() {
    return CLASS_NAME_HASH;
  }

// Here how they use it, copied from org.apache.lucene.search.KnnVectorQuery:

  @Override
  public boolean equals(Object obj) {
    if (sameClassAs(obj) == false) {
      return false;
    }
    return ((KnnVectorQuery) obj).k == k
        && ((KnnVectorQuery) obj).field.equals(field)
        && Arrays.equals(((KnnVectorQuery) obj).target, target)
        && Objects.equals(filter, ((KnnVectorQuery) obj).filter);
  }

  @Override
  public int hashCode() {
    return Objects.hash(classHash(), field, k, Arrays.hashCode(target), filter);
  }

And EqualsVerifier report such problem:

Significant fields: hashCode relies on CLASS_NAME_HASH, but equals does not.
These objects are equal, but probably shouldn't be

Asking to ignore CLASS_NAME_HASH field doesn't help.

Because EqualsVerifier find out that CLASS_NAME_HASH affects on hashCode and doesn't affect on equals.
Which looks like contract violation, but CLASS_NAME_HASH effectively static (getClass().getName().hashCode()) and works like salt for hashCode.

You could reproduce it with Lucene like that:

        EqualsVerifier.forClass(ConstantScoreQuery.class)
            .withIgnoredFields("CLASS_NAME_HASH")
            .withPrefabValues(Query.class, new MatchNoDocsQuery(), new MatchAllDocsQuery())
            .withNonnullFields("query")
            .usingGetClass()
            .verify();

Possible solution:

Support such salt-like logic by adding method SingleTypeEqualsVerifierApi#withSaltFields(String[])

Alternative solution:

Alternatively could be better to add more general method SingleTypeEqualsVerifierApi#useDefaultValuesForFields(String[]).

@jqno
Copy link
Owner

jqno commented Sep 20, 2023

Did you try using .suppress(Warning.STRICT_HASHCODE)? Your call would look like this:

        EqualsVerifier.forClass(ConstantScoreQuery.class)
            .suppress(Warning.STRICT_HASHCODE)
            .withPrefabValues(Query.class, new MatchNoDocsQuery(), new MatchAllDocsQuery())
            .withNonnullFields("query")
            .usingGetClass()
            .verify();

If that doesn't work, can you give me the Maven coordinates for Lucene that you're using so I can try it locally?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants