Hi,
New SDK 3.4.1 raise exception Exception of type ‘Couchbase.Core.Exceptions.KeyValue.SubdocExceptionException’ was thrown. if path not found even if I just check if this path exists. This exception don’t clarify that is happen. More clear will be with exception Couchbase.Core.Exceptions.KeyValue.PathNotFoundException . It’s new behavior, and SDK will raise this exception in case if path not found?
Hi, @jmorris
my code work for SDK 2.7.3 and couchbase community edition 7.1.1
public async Task<(bool, TSubDocument)> TryGetSubDocumentAsync<TSubDocument>(string topDocumentKey, string subDocumentPath)
{
var value = default(TSubDocument);
if (!(await _bucket.ExistsAsync(topDocumentKey)))
return (false, value);
var contents = await _bucket.LookupIn<dynamic>(topDocumentKey).Exists(subDocumentPath).ExecuteAsync();
var exists = contents.Exists(subDocumentPath);
if (exists)
{
value = (await _bucket.LookupIn<dynamic>(topDocumentKey).Get(subDocumentPath).ExecuteAsync()).Content<TSubDocument>(0);
return (true, value);
}
return (false, value);
}
I try migrate it on 3.4.1
public bool TryGetSubDocument<TSubDocument>(string topDocumentKey, string subDocumentPath, out TSubDocument value)
{
value = default(TSubDocument);
if (!_collection.ExistsAsync(topDocumentKey).Result.Exists)
return false;
var exists = _collection.LookupInAsync<dynamic>(topDocumentKey, spec => spec.Exists(subDocumentPath)).Result.Exists(0);
if (exists)
{
value = _collection.LookupInAsync<dynamic>(topDocumentKey, spec => spec.Get(subDocumentPath)).Result.ContentAs<TSubDocument>(0);
return true;
}
return false;
}
it’s raised exception on row
var exists = _collection.LookupInAsync<dynamic>(topDocumentKey, spec => spec.Exists(subDocumentPath)).Result.Exists(0);
Can your provide a sample JSON doc and the path you are checking? A unit test would be nice. Once I have that I can tell you exactly what is going on.
I noticed in your migration to 3.4.1 code, you should be awaiting the operations instead of blocking on them via Result.
Jeff
thank for replay @jmorris
Totally agree with you, async / await preferable, but I need test both approaches with blocking and not blocking calls.
I prepare unit test to reproduce my issue. Please, replace in it with appropriate username, password, host and backet name.
using Couchbase;
using Couchbase.KeyValue;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Threading.Tasks;
namespace Common.IntegrationTests
{
[TestClass]
public class SampleUnitTest
{
private ICouchbaseCollection _collection;
[DataTestMethod]
[DataRow("strfield0", "test28")]
[DataRow("strfield1", "test29")]
[DataRow("strfield3", "test27")]
[DataRow("strfield4", "test9")]
public async Task TryGetSubDocumentAsyncShouldReturnTrue_IfDocumentDoesAndPathAreExistsAndStringValue(string path, string expectedValue)
{
var testKeyGetAsync = "test-doc-key";
// Arrange
GenerateTestCouchDBData(testKeyGetAsync);
// Act
var (result, value) = await TryGetSubDocumentAsync<string>(testKeyGetAsync, path);
// Assert
Assert.IsTrue(result);
Assert.IsNotNull(value);
Assert.AreEqual(expectedValue, value);
}
public async Task<(bool, TSubDocument)> TryGetSubDocumentAsync<TSubDocument>(string topDocumentKey, string subDocumentPath)
{
var options = new ClusterOptions
{
UserName = "user_name",
Password = "password",
};
var cluster = Cluster.ConnectAsync("couchbase://127.0.0.1", options).Result;
cluster.WaitUntilReadyAsync(TimeSpan.FromSeconds(10000)).Wait();
var bucket = cluster.BucketAsync("backetName").Result;
_collection = bucket.DefaultCollection();
var value = default(TSubDocument);
if (!(await _collection.ExistsAsync(topDocumentKey)).Exists)
return (false, value);
var result = (await _collection.LookupInAsync<dynamic>(topDocumentKey, spec => spec.Exists(subDocumentPath)));
var exists = result.ContentAs<bool>(0);
if (exists)
{
value = (await _collection.LookupInAsync<dynamic>(topDocumentKey, spec => spec.Get(subDocumentPath))).ContentAs<TSubDocument>(0);
return (true, value);
}
return (false, value);
}
public void GenerateTestCouchDBData(string key)
{
Store(key,
new
{
intArray = new[]
{
1000,
2000,
3000,
4000,
5000,
6000
},
strfield0 = "test28",
strfield1 = "test29",
strfield3 = "test27",
strfield4 = "test9",
strfield2 = "test19",
customObject = new
{
intProp = 1,
stringProp = "MyStringProp-1"
},
customObjectList = new[]
{
new
{
intProp = 1,
stringProp = "MyStringProp-1"
},
new
{
intProp = 2,
stringProp = "MyStringProp-2"
},
new
{
intProp = 3,
stringProp = "MyStringProp-3"
},
new
{
intProp = 4,
stringProp = "MyStringProp-4"
}
}
},
100000);
}
public bool Store<T>(string key, T value, int timeInMinutes)
{
var options = new UpsertOptions();
options.Expiry(TimeSpan.FromMinutes(timeInMinutes));
_collection.UpsertAsync(key, value, options).Wait();
return true;
}
}
}
After a few changes your code works as expected. The changes I made were removing the blocking calls and importantly changing:
var exists = result.ContentAs<bool>(0);
To this:
var exists = result.Exists(0);
The call to check for the existence of the path is not done via ContentAs()
but by using Exists()
.
thanks @jmiraglia for positive cases it’s work fine.
Now I try negative test , when path not exists
[DataRow("not-exist-path", default(string))]
test raise Exception of type ‘Couchbase.Core.Exceptions.KeyValue.SubdocExceptionException’ was thrown.
in line
var result = (await _collection.LookupInAsync<dynamic>(topDocumentKey, spec => spec.Exists(subDocumentPath)));
if path not exists I need use something else ?
I see now. Yes, I do believe this is a bug; I added a ticket for resolving in the future. The problem here is that it’s a breaking change to fix this as anybody else using the SDK might be catching the exception and handling it that way. If were to fix it in a minor release, that might cause problems.
Jeff
Thanks @jmorris I’ll follow the ticket