在Azure表存储中,如何查询与分区中特定行键匹配的一组实体?
我正在使用Azure表存储,需要检索一组与分区内的一组行键匹配的实体。
基本上如果这是SQL,它可能看起来像这样:
SELECT TOP 1 SomeKey
FROM TableName WHERE SomeKey IN (1, 2, 3, 4, 5);
我想节省成本并减少做一堆表检索操作,我可以使用表批量操作来完成它。出于某种原因,我得到了一个例外:
“具有检索操作的批处理事务不能包含任何其他操作”
这是我的代码:
public async Task<IList<GalleryPhoto>> GetDomainEntitiesAsync(int someId, IList<Guid> entityIds)
{
try
{
var client = _storageAccount.CreateCloudTableClient();
var table = client.GetTableReference("SomeTable");
var batchOperation = new TableBatchOperation();
var counter = 0;
var myDomainEntities = new List<MyDomainEntity>();
foreach (var id in entityIds)
{
if (counter < 100)
{
batchOperation.Add(TableOperation.Retrieve<MyDomainEntityTableEntity>(someId.ToString(CultureInfo.InvariantCulture), id.ToString()));
++counter;
}
else
{
var batchResults = await table.ExecuteBatchAsync(batchOperation);
var batchResultEntities = batchResults.Select(o => ((MyDomainEntityTableEntity)o.Result).ToMyDomainEntity()).ToList();
myDomainEntities .AddRange(batchResultEntities );
batchOperation.Clear();
counter = 0;
}
}
return myDomainEntities;
}
catch (Exception ex)
{
_logger.Error(ex);
throw;
}
}
如何在不手动循环遍历行键并为每个行执行单独的Retrieve表操作的情况下,如何实现我所追求的目标?我不想承担与此相关的成本,因为我可以拥有数百个我想要过滤的行键。
答案 0 :(得分:1)
使用数百个行密钥,使用$filter
排除行密钥列表(无论如何都会导致部分分区扫描)。
由于您遇到错误,批处理似乎包含查询和其他类型的操作(不允许)。我从你的代码片段中看不出你为什么会收到这个错误。
您唯一的其他选择是执行单个查询。您可以异步执行这些操作,因此您不必等待每个返回。表存储在给定分区上提供超过2,000个事务/秒,因此它是一个可行的解决方案。
答案 1 :(得分:1)
我在每个分区的单个请求中创建了一个帮助方法。
像这样使用:
var items = table.RetrieveMany<MyDomainEntity>(partitionKey, nameof(TableEntity.RowKey),
rowKeysList, columnsToSelect);
这是辅助方法:
public static List<T> RetrieveMany<T>(this CloudTable table, string partitionKey,
string propertyName, IEnumerable<string> valuesRange,
List<string> columnsToSelect = null)
where T : TableEntity, new()
{
var enitites = table.ExecuteQuery(new TableQuery<T>()
.Where(TableQuery.CombineFilters(
TableQuery.GenerateFilterCondition(
nameof(TableEntity.PartitionKey),
QueryComparisons.Equal,
partitionKey),
TableOperators.And,
GenerateIsInRangeFilter(
propertyName,
valuesRange)
))
.Select(columnsToSelect))
.ToList();
return enitites;
}
public static string GenerateIsInRangeFilter(string propertyName,
IEnumerable<string> valuesRange)
{
string finalFilter = valuesRange.NotNull(nameof(valuesRange))
.Distinct()
.Aggregate((string)null, (filterSeed, value) =>
{
string equalsFilter = TableQuery.GenerateFilterCondition(
propertyName,
QueryComparisons.Equal,
value);
return filterSeed == null ?
equalsFilter :
TableQuery.CombineFilters(filterSeed,
TableOperators.Or,
equalsFilter);
});
return finalFilter ?? "";
}
我已经在rowKeysList
中测试了少于100个值,但是,如果有更多值,它甚至会抛出异常,我们总是可以将请求拆分为多个部分。
答案 2 :(得分:0)
我不确定我是如何错过这一点的,但这里是TableBatchOperation类型的MSDN文档的片段:
批处理操作最多可包含100个单独的表操作,并要求每个操作实体必须具有相同的分区键。 具有检索操作的批处理不能包含任何其他操作。请注意,批处理操作的总有效负载限制为4MB。
我最终按照David Makogon的建议异步执行单独的检索操作。
答案 3 :(得分:0)
我制作了自己的贫民区链接表。我知道它不是那么有效(也许它很好)但我只是在本地缓存数据时才提出这个请求,这只意味着切换设备。无论如何,这似乎有效。检查两个数组的长度让我推迟context.done();
var query = new azure.TableQuery()
.top(1000)
.where('PartitionKey eq ?', 'link-' + req.query.email.toLowerCase() );
tableSvc.queryEntities('linkUserMarker',query, null, function(error, result, response) {
if( !error && result ){
var markers = [];
result.entries.forEach(function(e){
tableSvc.retrieveEntity('markerTable', e.markerPartition._, e.RowKey._.toString() , function(error, marker, response){
markers.push( marker );
if( markers.length == result.entries.length ){
context.res = {
status:200,
body:{
status:'error',
markers: markers
}
};
context.done();
}
});
});
} else {
notFound(error);
}
});