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

ReadMany: Fixes ReadMany API for None Partition key values #2661

Merged
merged 10 commits into from
Aug 20, 2021
55 changes: 30 additions & 25 deletions Microsoft.Azure.Cosmos/src/ReadManyQueryHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -270,40 +270,45 @@ private QueryDefinition CreateReadManyQueryDefinitionForOther(List<(string, Part

queryStringBuilder.Append("SELECT * FROM c WHERE ( ");
for (int i = startIndex; i < totalItemCount; i++)
{
object[] pkValues;
if (items[i].Item2.IsNone)
{
pkValues = new object[0];
}
else
{
pkValues = items[i].Item2.InternalKey.ToObjectArray();
if (pkValues.Length != this.partitionKeyDefinition.Paths.Count)
{
throw new ArgumentException("Number of components in the partition key value does not match the definition.");
}
}

{
string pkParamName = "@param_pk" + i;
string idParamName = "@param_id" + i;
sqlParameters.Add(new SqlParameter(idParamName, items[i].Item1));

queryStringBuilder.Append("( ");
queryStringBuilder.Append("c.id = ");
queryStringBuilder.Append(idParamName);
for (int j = 0; j < pkValues.Length; j++)
if (items[i].Item2.IsNone)
{
queryStringBuilder.Append(" AND ");
queryStringBuilder.Append("c");
queryStringBuilder.Append(this.partitionKeySelectors[j]);
queryStringBuilder.Append(" = ");

string pkParamNameForSinglePath = pkParamName + j;
sqlParameters.Add(new SqlParameter(pkParamNameForSinglePath, pkValues[j]));
queryStringBuilder.Append(pkParamNameForSinglePath);
for (int j = 0; j < this.partitionKeySelectors.Count; j++)
asketagarwal marked this conversation as resolved.
Show resolved Hide resolved
{
queryStringBuilder.Append(" AND ");
queryStringBuilder.Append("IS_DEFINED(c");
queryStringBuilder.Append(this.partitionKeySelectors[j]);
j82w marked this conversation as resolved.
Show resolved Hide resolved
queryStringBuilder.Append(") = false");
}
}

else
{
object[] pkValues = items[i].Item2.InternalKey.ToObjectArray();
if (pkValues.Length != this.partitionKeyDefinition.Paths.Count)
{
throw new ArgumentException("Number of components in the partition key " +
"value does not match the definition.");
}
for (int j = 0; j < this.partitionKeySelectors.Count; j++)
{
queryStringBuilder.Append(" AND ");
queryStringBuilder.Append("c");
queryStringBuilder.Append(this.partitionKeySelectors[j]);
queryStringBuilder.Append(" = ");

string pkParamNameForSinglePath = pkParamName + j;
sqlParameters.Add(new SqlParameter(pkParamNameForSinglePath, pkValues[j]));
queryStringBuilder.Append(pkParamNameForSinglePath);
}
}

queryStringBuilder.Append(" )");

if (i < totalItemCount - 1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,25 @@ await container.CreateItemAsync(
await database.DeleteAsync();
}

[TestMethod]
public async Task ReadManyWithNonePkValues()
{
for (int i = 0; i < 5; i++)
{
await this.Container.CreateItemAsync(new ActivityWithNoPk("id" + i.ToString()),
PartitionKey.None);
}

List<(string, PartitionKey)> itemList = new List<(string, PartitionKey)>();
for (int i = 0; i < 5; i++)
{
itemList.Add(("id" + i.ToString(), PartitionKey.None));
}

FeedResponse<ActivityWithNoPk> feedResponse = await this.Container.ReadManyItemsAsync<ActivityWithNoPk>(itemList);
Assert.AreEqual(feedResponse.Count, 5);
asketagarwal marked this conversation as resolved.
Show resolved Hide resolved
}

[TestMethod]
public async Task ReadManyItemsFromNonPartitionedContainers()
{
Expand All @@ -408,16 +427,31 @@ public async Task ReadManyItemsFromNonPartitionedContainers()
itemList.Add(("id" + i.ToString(), PartitionKey.None));
}

using (ResponseMessage responseMessage = await container.ReadManyItemsStreamAsync(itemList))
FeedResponse<ActivityWithNoPk> feedResponse = await container.ReadManyItemsAsync<ActivityWithNoPk>(itemList);
Assert.AreEqual(feedResponse.Count, 5);

// Start inserting documents with same id but new pk values
for (int i = 0; i < 5; i++)
{
Assert.IsNotNull(responseMessage);
Assert.IsTrue(responseMessage.Headers.RequestCharge > 0);
Assert.IsNotNull(responseMessage.Diagnostics);
await container.CreateItemAsync(new ActivityWithSystemPk("id" + i.ToString(), "newPK"),
new PartitionKey("newPK"));
}

ToDoActivity[] items = this.cosmosClient.ClientContext.SerializerCore.FromFeedStream<ToDoActivity>(
CosmosFeedResponseSerializer.GetStreamWithoutServiceEnvelope(responseMessage.Content));
Assert.AreEqual(items.Length, 5);
FeedResponse<ActivityWithSystemPk> feedResponseWithPK = await container.ReadManyItemsAsync<ActivityWithSystemPk>(itemList);
Assert.AreEqual(feedResponseWithPK.Count, 5);

for (int i = 0; i < 5; i++)
{
itemList.Add(("id" + i.ToString(), new PartitionKey("newPK")));
}
feedResponseWithPK = await container.ReadManyItemsAsync<ActivityWithSystemPk>(itemList);
Assert.AreEqual(feedResponseWithPK.Count, 10);

// Mixing both None and non-None pk values
itemList = new List<(string, PartitionKey)>
{ ("id0", PartitionKey.None), ("id0", new PartitionKey("newPK")) };
feedResponse = await container.ReadManyItemsAsync<ActivityWithNoPk>(itemList);
Assert.AreEqual(feedResponse.Count, 2);
}

[TestMethod]
Expand Down Expand Up @@ -537,5 +571,32 @@ public override async Task<ResponseMessage> SendAsync(RequestMessage requestMess
return await base.SendAsync(requestMessage, cancellationToken);
}
}

private class ActivityWithNoPk
{
public ActivityWithNoPk(string id)
{
this.id = id;
}

#pragma warning disable IDE1006 // Naming Styles
public string id { get; set; }
#pragma warning restore IDE1006 // Naming Styles
}

private class ActivityWithSystemPk
{
public ActivityWithSystemPk(string id, string pk)
{
this.id = id;
this._partitionKey = pk;
}

#pragma warning disable IDE1006 // Naming Styles
public string id { get; set; }
#pragma warning restore IDE1006 // Naming Styles

public string _partitionKey { get; set; }
}
}
}