Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

CSHARP-5201: Allow update to supply sort option #1453

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

Merged
papafe merged 22 commits into mongodb:main from papafe:csharp5201
Nov 19, 2024
Merged

Conversation

@papafe
Copy link
Contributor

@papafe papafe commented Sep 18, 2024

No description provided.

Copy link
Contributor

@BorisDog BorisDog left a comment

Choose a reason for hiding this comment

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

Looks good overall! Now we need to wait for spec finalization.

{
"MongoDB.Driver.Tests.Specifications.crud.tests.v1",
"MongoDB.Driver.Tests.Specifications.crud.tests.v2"
"MongoDB.Driver.Tests.Specifications.crud.tests.v2"//TODO I don't think we have v2 anymore?
Copy link
Contributor

Choose a reason for hiding this comment

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

Good catch, we don't, can be removed.

papafe reacted with thumbs up emoji
var description = testCase.Test["description"].AsString;
var lineNumer = lines.FirstOrDefault(p => p.Line.Contains(description)).Index;
testCase.Test.Add("_lineNumber", lineNumer);
var lineNumber = lines.FirstOrDefault(p => p.Line.Contains(description)).Index;
Copy link
Contributor

Choose a reason for hiding this comment

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

+1

{
switch (argument.Name)
{
//TODO Why don't we create UpdateOptions beforehand?
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably to exercise options == null path.

papafe reacted with thumbs up emoji
IsUpsert = options.IsUpsert,
Sort = options.Sort
};
//TODO Why don't UpdateOne and UpdateOneAsync have a common path for creating the UpdateOneModel and BulkWriteOptions (if not more)?
Copy link
Contributor

Choose a reason for hiding this comment

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

In that case that can be done elegantly, we should do that in a separate ticket for other methods as well. Usually we can address such improvements on green-build Fridays.

/// <summary>
/// Gets or sets the sort document.
/// </summary>
public BsonDocument Sort
Copy link
Contributor

Choose a reason for hiding this comment

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

Alphabetical order.

papafe reacted with thumbs up emoji
/// <summary>
/// The sort document to use.
/// </summary>
public BsonDocument Sort { get; init; }
Copy link
Member

@sanych-sun sanych-sun Oct 16, 2024

Choose a reason for hiding this comment

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

Should we use SortDefinition<TDocument>?

papafe reacted with thumbs up emoji
private BsonValue _hint;
private bool _isUpsert;
private readonly TDocument _replacement;
private BsonDocument _sort;
Copy link
Member

@sanych-sun sanych-sun Oct 16, 2024

Choose a reason for hiding this comment

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

Should we use SortDefinition<TDocument>?

papafe reacted with thumbs up emoji
/// <summary>
/// Gets or sets the sort document.
/// </summary>
public BsonDocument Sort
Copy link
Member

@sanych-sun sanych-sun Oct 16, 2024

Choose a reason for hiding this comment

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

Use SortDefinition<TDocument>.

/// <summary>
/// Gets or sets the sort document.
/// </summary>
public BsonDocument Sort
Copy link
Member

@sanych-sun sanych-sun Oct 16, 2024

Choose a reason for hiding this comment

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

Use SortDefinition<TDocument>.

papafe reacted with thumbs up emoji
/// <summary>
/// Gets or sets the sort document.
/// </summary>
public BsonDocument Sort
Copy link
Member

@sanych-sun sanych-sun Oct 16, 2024

Choose a reason for hiding this comment

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

Use SortDefinition<TDocument>.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately UpdateOptions and ReplaceOptions are not generic classes, so we can't parametrize the properties. We can add the parameter, but I think it would be a breaking change.
We could also add the generic UpdateOptions<TDocument> alongside the non generic version and make new methods that use this, while deprecating the methods that are using the non generico version. This would be the long road

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have done this only for ReplaceOptions for now. If all I've done makes sense, I'll do the same for UpdateOptions.
This required to create a new class ReplaceOptions<T> and an implicit conversion operator from ReplaceOptions to ReplaceOptions<T>.

Copy link
Member

@sanych-sun sanych-sun left a comment

Choose a reason for hiding this comment

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

We should decide if we are ready for the breaking change by replacing ReplaceOption with ReplaceOption before reviewing further changes.

WriteArrayFilters(serializationContext, model.ArrayFilters);
WriteHint(serializationContext, model.Hint);
WriteCollation(serializationContext, model.Collation);
WriteSort(serializationContext, model.Sort?.Render(renderArgs.WithNewDocumentType(documentSerializer)));
Copy link
Member

@sanych-sun sanych-sun Oct 30, 2024

Choose a reason for hiding this comment

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

Is it possible to make WriteSort and pass model.Sort there? And move Render there as well.

papafe reacted with thumbs up emoji
/// </returns>
ReplaceOneResult ReplaceOne(FilterDefinition<TDocument> filter, TDocument replacement, ReplaceOptions options = null, CancellationToken cancellationToken = default(CancellationToken));

ReplaceOneResult ReplaceOne(FilterDefinition<TDocument> filter, TDocument replacement, ReplaceOptions<TDocument> options = null, CancellationToken cancellationToken = default(CancellationToken));
Copy link
Member

@sanych-sun sanych-sun Oct 30, 2024

Choose a reason for hiding this comment

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

It will be a code breaking change (client's code has to be rewritten) as far as I understood. Should we create a new overload instead? And support both ReplaceOptions and ReplaceOptions<TDocument>

Copy link
Member

@sanych-sun sanych-sun Oct 30, 2024

Choose a reason for hiding this comment

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

@BorisDog WDYT?

Copy link
Contributor

@BorisDog BorisDog Oct 30, 2024
edited
Loading

Choose a reason for hiding this comment

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

Yup we avoid breaking changes. We'll probably need an extension method.

Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like a binary breaking change but not a source code breaking change.

There is also an implicit conversion operator from ReplaceOptions to ReplaceOptions<TDocument> that provides backward compatibility.

Copy link
Contributor Author

@papafe papafe Oct 31, 2024
edited
Loading

Choose a reason for hiding this comment

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

Yes, I discussed this with Robert and this was an attempt to avoid creating more methods/overloads and keep source compatibility.
I was reading a little bit around online about semver in regards to binary compatibility. It seems that there is not a complete agreement on it, but overall it's recommended to increase major version with a binary-incompatible change.
Personally (irregardless of this case), I think this is a matter of deciding where do we stand on this and then take it from there.

Copy link
Contributor

Choose a reason for hiding this comment

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

I would prefer a solution that doesn't involve doubling (or worse) the number of overloads. Too many overloads is a bad thing.

The current approach uses an implicit conversion from ReplaceOptions to ReplaceOptions<TDocument> to preserve backward compatibility.

Another approach could be to use subclassing: ReplaceOptions<TDocument> : ReplaceOptions. Then we could leave the current overloads unchanged at the expense of some type safety. The code that uses the replaceOptions would have to test whether the passed in options are the subclass or not.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Given what we discussed at Triage today I'll park this ticket until next when we can have a more general discussion about breaking changes in general

Copy link
Member

@sanych-sun sanych-sun Oct 31, 2024

Choose a reason for hiding this comment

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

I have to apologize, I did not notice the implicit conversion. So this is not code breaking change.
On other hand I suppose it's better to follow Robert's suggestion with using of subclasses as clearer way to do the same thing. At least because this way we could avoid changes to public API.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I went for Robert suggestion and made the generic options derive from the base class we have now

/// <summary>
/// Options for replacing a single document.
/// </summary>
public sealed class ReplaceOptions : ReplaceOptionsBase
Copy link
Contributor

Choose a reason for hiding this comment

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

Need to ensure that this change is not breaking.

/// </returns>
ReplaceOneResult ReplaceOne(FilterDefinition<TDocument> filter, TDocument replacement, ReplaceOptions options = null, CancellationToken cancellationToken = default(CancellationToken));

ReplaceOneResult ReplaceOne(FilterDefinition<TDocument> filter, TDocument replacement, ReplaceOptions<TDocument> options = null, CancellationToken cancellationToken = default(CancellationToken));
Copy link
Contributor

@BorisDog BorisDog Oct 30, 2024
edited
Loading

Choose a reason for hiding this comment

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

Yup we avoid breaking changes. We'll probably need an extension method.

: this(CollectionNamespace.FromFullName(collectionNamespace), filter, replacement, collation, hint, isUpsert)
bool isUpsert = false,
SortDefinition<TDocument> sort = null)
: this(CollectionNamespace.FromFullName(collectionNamespace), filter, replacement, collation, hint, isUpsert, sort)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is not binary compatible.
The alternative would be to add other two constructors (given that we already have two) but:

  • Then we have 4 very similar constructors
  • Developers using those constructors will have errors due to constructor ambiguity if they do not specify all parameters. This is theoretically still source compatible, but definitely annoying
    Any suggestions about this? Personally I'd just keep the sort definition out of the constructor if we cannot find another alternative

Copy link
Member

@sanych-sun sanych-sun Nov 6, 2024

Choose a reason for hiding this comment

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

What if we add new constructor with sort as the very last non-optional parameter? In such way it would not be a breaking change and there would not be an ambiguity.

papafe reacted with thumbs up emoji
Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's a good idea, I didn't think about it :)
I've added two new constructor to mirror the ones we have already, and added a new private one that is being called by all the others

sanych-sun reacted with thumbs up emoji
Copy link
Member

@sanych-sun sanych-sun Nov 6, 2024

Choose a reason for hiding this comment

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

Nice! And I suppose the plan should be to eliminate this copied ctors in next major release, by making the sort parameter as optional. So the question is: how we going to track this and not forget to do?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, that would be the plan! I've added the constructors and the non generic options to the list of breaking changes for next version.

@papafe papafe marked this pull request as ready for review November 4, 2024 14:31
@papafe papafe requested a review from a team as a code owner November 4, 2024 14:31
assertReplaceOne();

var replaceOptions = new ReplaceOptions();
var replaceOptions = new ReplaceOptions<Person>();
Copy link
Member

@sanych-sun sanych-sun Nov 6, 2024

Choose a reason for hiding this comment

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

Should we revert this change as we should be backward compatible now?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Definitely

: this(CollectionNamespace.FromFullName(collectionNamespace), filter, replacement, collation, hint, isUpsert)
bool isUpsert = false,
SortDefinition<TDocument> sort = null)
: this(CollectionNamespace.FromFullName(collectionNamespace), filter, replacement, collation, hint, isUpsert, sort)
Copy link
Member

@sanych-sun sanych-sun Nov 6, 2024

Choose a reason for hiding this comment

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

What if we add new constructor with sort as the very last non-optional parameter? In such way it would not be a breaking change and there would not be an ambiguity.

papafe reacted with thumbs up emoji
Copy link
Member

@sanych-sun sanych-sun left a comment

Choose a reason for hiding this comment

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

I'm ready to LGTM the changes, but could you please rebase before that? Because in the current main we already have that apicompat script, just want to be sure we did everything right in terms of backward compatibility.

Copy link
Contributor Author

papafe commented Nov 6, 2024

@sanych-sun just rebased on main, curious to see what will the script say 😁

Copy link
Contributor Author

papafe commented Nov 7, 2024

@sanych-sun it seems the validate-apicompat task gave its ok 😁
Do you think this is mergeable now?

sanych-sun reacted with thumbs up emoji

Copy link
Member

@sanych-sun sanych-sun left a comment

Choose a reason for hiding this comment

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

LGTM

@papafe papafe merged commit 03432b0 into mongodb:main Nov 19, 2024
27 of 30 checks passed
@papafe papafe deleted the csharp5201 branch November 19, 2024 07:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@rstam rstam rstam left review comments

@sanych-sun sanych-sun sanych-sun approved these changes

@BorisDog BorisDog Awaiting requested review from BorisDog

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

AltStyle によって変換されたページ (->オリジナル) /