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

Node: Enable +/-inf as score for ZADD #3370

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

jamesx-improving
Copy link
Collaborator

@jamesx-improving jamesx-improving commented Mar 14, 2025

Issue link

This Pull Request is linked to issue (URL): #3360

Checklist

Before submitting the PR make sure the following are checked:

  • This Pull Request is related to one issue.
  • Commit message has a detailed description of what changed and why.
  • Tests are added or updated.
  • CHANGELOG.md and documentation files are updated.
  • Destination branch is correct - main or release
  • Create merge commit if merging release branch into main, squash otherwise.

@Yury-Fridlyand Yury-Fridlyand added the node Node.js wrapper label Mar 14, 2025
Copy link
Collaborator

@Yury-Fridlyand Yury-Fridlyand left a comment

Choose a reason for hiding this comment

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

Update zadd in transaction too
Add changelog

membersAndScores: SortedSetDataType | Record<string, number>,
membersAndScores:
| SortedSetDataType
| Record<string, number>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
| Record<string, number>

I think you can remove this line, because InfScore already includes a number.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thinking further I think you can update SortedSetDataType

export type SortedSetDataType = {
    /** The sorted set element name. */
    element: GlideString;
    /** The element score. */
    score: InfScore;
}[];

Copy link
Collaborator Author

@jamesx-improving jamesx-improving Mar 14, 2025

Choose a reason for hiding this comment

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

SortedSetDataType is used as the return type of many sorted set commands. Changing it will be too much of an impact, not to mention we are avoiding a "breaking change" here.

Same concern resulting me not removing | Record<string, number>, but I'm not entirely sure if removing it would count as a "breaking change". Please advise.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh right.
Before suggesting other destructive ideas, could you please try ZADD with Number.POSITIVE_INFINITY and Number.NEGATIVE_INFINITY?
Probably we just should add extra handling and convert those to +-inf? No type changes needed at all in that case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Number.POSITIVE_INFINITY and Number.NEGATIVE_INFINITY works with existing API, no change needed. Have suggested the same as workaround, but I don't think we can use that as a reason to not do this fix.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If the standard of non-breaking change is that existing downstream code still works, I think we can go ahead and modify both the object type (SortedSetDataType -> ElementAndScore) as well as the Record type (Record<string, number> -> Record<string, Score>). User will be able to feed +inf and -inf using either of the formats. I've tested and all existing tests passed after the change. Will update the PR for review.

Copy link
Member

Choose a reason for hiding this comment

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

Well, I'm not sure what the standard is, i can check it.
To my understanding it means breaking. Like literally breaking.

If nothing is going to happen to the user code, and he can keep running the same code, it's not breaking anything.

I'll ask Google to make sure.

Copy link
Member

Choose a reason for hiding this comment

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

Screenshot_2025-03-19-19-49-34-09_40deb401b9ffe8e1df2f1cc5ba480b12.jpg

Copy link
Member

Choose a reason for hiding this comment

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

I think we are good.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Great. Have updated the PR accordingly. Please review again 🙏

element: GlideString;
/** The element score. */
score: Score;
}[];
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@avifenesh I followed how SortedSetDataType is defined as an array, instead of your approach of adding | ElementAndScore[] towards the end. I understand that | ElementAndScore[] gives user more flexibility, as they could feed either an array or a single object. However, I feel like this is deviated from the main purpose of this PR.

Please comment.

Copy link
Member

Choose a reason for hiding this comment

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

Does ElementAndScore is always an array?
I find it very limiting.
I would prefer

export type ElementAndScore = {
    /** The sorted set element name. */
    element: GlideString;
    /** The element score. */
    score: Score;
};

And

     public async zadd(
        key: GlideString,
        membersAndScores: ElementAndScore | ElementAndScore[] | Record<string, Score>,
        options?: ZAddOptions,
    ):

Copy link
Member

Choose a reason for hiding this comment

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

Or does the non Array version is not relevant?
If so,

public async zadd( key: GlideString, membersAndScores: ElementAndScore[] | Record<string, Score>, options?: ZAddOptions, ):

Copy link
Member

Choose a reason for hiding this comment

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

And if still array so at least ElementsAndScores since it is not a singular. But I would really prefer an array of custom type, then costume type which is an array of unnamed object, with another type in it.
Array is a real type, unnamed object is the one I would give the customization to, and it will be more dynamic.

Copy link
Collaborator Author

@jamesx-improving jamesx-improving Mar 19, 2025

Choose a reason for hiding this comment

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

I totally agree that giving a name to an object makes more sense than to an array of it. Also, this is another example of using the same type on multiple commands, especially when it's an input-output crossover, being very problematic, as the output (return value) will always be an array, but not necessarily the input.

My actual concern on accepting ElementAndScore as a single object is, under corner cases, it creates ambiguity, as we don't know if user is passing a single ElementAndScore object, or a Record<string, Score>. Consider below example:

const ambiguousMembersAndScores = {
    element: "+inf",
    score: 42,
};

This could be interpreted as:

  1. A single member-score pair, with "+inf" being the member, and 42 being the score.
  2. Two member-score pairs, with the first member being "element", first score being +inf, and the second member being "score", and second score being 42.

Maybe we can get away with this ambiguity with some cleaver if-else condition in createZAdd(), but I'm not sure if it's worth it.

If you agree with me on this, I'll just change the definition of ElementAndScore to non-array, single object, and use ElementAndScore[] | Record<string, Score> as the type of membersAndScores on ZADD, so for both types in this union type, it is a one-to-one substitution (SortedSetDataType -> ElementAndScore[], Record<string, number> -> Record<string, Score>), potentially easier for user to understand, as we are pulling off this type change on a minor version (1.4), and trying not to make it a breaking change.

Copy link
Member

Choose a reason for hiding this comment

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

I agree with the last. Our goal is to remove Record<string, Score> later, right?
You can mark it as @dperecated in the docs above, so users will see. Never tried it, so just if it is not extremely complicated. Otherwise, add a note in which users should use the ElementAndScore and avoid Record<string, Score> as it going to be deprecated.

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

Successfully merging this pull request may close these issues.

Node Client: zadd does not support -inf and inf for score
5 participants