对azure表的动态查询

时间:2012-08-09 13:40:44

标签: c# azure azure-table-storage

我正在使用azure表存储来存储博客文章。每篇博文都可以有不同的标签。 所以我将有三个不同的表。

  • 将存储博客文章的人。
  • 一个用于存储标签
  • 将存储标签和帖子之间关系的文件

所以我的问题如下,是否有可能创建动态搜索队列?因为直到运行时才知道我要搜索多少个标签。 据我了解,您只能使用LINQ查询azure表。或者我可以输入一个可以动态更改的字符串查询吗?

更新

以下是博客表中的一些示例数据

PartitionKey,RowKey,Timestamp,Content,FromUser,Tags
user1, 1, 2012-08-08 13:57:23, "Hello World", "root", "yellow,red"

blogTag表

PartitionKey,RowKey,Timestamp,TagId,TagName
"red", "red", 2012-08-08 11:40:29, 1, red
"yellow", "yellow", 2012-08-08 11:40:29, 2, yellow

关系表

PartitionKey,RowKey,Timestamp,DataId,TagId
1, 1, 2012-08-08 11:40:29, 1, 1
2, 1, 2012-08-08 13:57:23, 1, 2

这些表的一个用法示例是例如当我想要获得具有特定标记的所有博客文章时。

  • 我必须从blogTag表中查询tagId
  • 我需要在关系表中搜索dataId
  • 最后,我需要使用该dataId
  • 在博客表中搜索博客文章

我正在使用LINQ来执行查询,它看起来像是

CloudTableQuery<DataTag> tagIds = (from e in ctx2.CreateQuery<DataTag>("datatags")
                                   where e.PartitionKey == tags
                                   select e).AsTableServiceQuery<DataTag>();

我尝试了Gaurav Mantri使用过滤器的建议,它确实有效。但我担心这会有多大的效率。并且关于仅允许的15个离散比较的限制。

4 个答案:

答案 0 :(得分:2)

您可以简单地构建 where子句并传递给where方法,例如:

var whereClause="(PartitionKey eq 'Key1') and (PartitionKey eq 'Key2')"

CloudStorageAccount storageAccount = CloudStorageAccount.Parse("AccountDetails");
            CloudTableClient tableClient = storageAccount.CreateCloudTableClient();

            CloudTable table = tableClient.GetTableReference(<TableName>);
            table.CreateIfNotExists();

            TableQuery<YourAzureTableEntity> query =
               new TableQuery<YourAzureTableEntity>()
                  .Where(whereClause));
            var list = table.ExecuteQuery(query).ToList();

答案 1 :(得分:1)

我也面临着同样的问题。我确实找到了一个我在下面粘贴的解决方案:

public static IEnumerable<T> Get(CloudStorageAccount storageAccount, string tableName, string filter)
    {
        string tableEndpoint = storageAccount.TableEndpoint.AbsoluteUri;
        var tableServiceContext = new TableServiceContext(tableEndpoint, storageAccount.Credentials);
        string query = string.Format("{0}{1}()?filter={2}", tableEndpoint, tableName, filter);
        var queryResponse = tableServiceContext.Execute<T>(new Uri(query)) as QueryOperationResponse<T>;
        return queryResponse.ToList();
    }

基本上它使用DataServiceContext的Execute(Uri)方法:http://msdn.microsoft.com/en-us/library/cc646700.aspx

如果您通过REST API调用查询功能(例如PartitionKey eq'mypk'和RowKey ge'myrk'),则需要指定过滤条件。 不确定这是否是最佳解决方案:)期待对此发表评论。

答案 2 :(得分:0)

这是可能的,但这可能不是一个好主意。添加多个查询参数总是会导致表扫描。这可能在一张小桌子上没问题,但是如果你的桌子很大,那将非常慢。对于大型表格,您最好为每个组合键运行单独的查询。

也就是说,您可以使用一些LINQ魔法构建动态查询。这是我用过的辅助课程:

public class LinqBuilder
{
    /// <summary>
    /// Build a LINQ Expression that roughly matches the SQL IN() operator
    /// </summary>
    /// <param name="columnValues">The values to filter for</param>
    /// <returns>An expression that can be passed to the LINQ  .Where() method</returns>
    public static Expression<Func<RowType, bool>> BuildListFilter<RowType, ColumnType>(string filterColumnName, IEnumerable<ColumnType> columnValues)
    {
        ParameterExpression rowParam = Expression.Parameter(typeof(RowType), "r");
        MemberExpression column = Expression.Property(rowParam, filterColumnName);

        BinaryExpression filter = null;
        foreach (ColumnType columnValue in columnValues)
        {
            BinaryExpression newFilterClause = Expression.Equal(column, Expression.Constant(columnValue));
            if (filter != null)
            {
                filter = Expression.Or(filter, newFilterClause);
            }
            else
            {
                filter = newFilterClause;
            }
        }

        return Expression.Lambda<Func<RowType, bool>>(filter, rowParam);
    }

    public static Expression<Func<RowType, bool>> BuildComparisonFilter<RowType, ColumnType>(string filterColumnName, Func<MemberExpression, BinaryExpression> buildComparison)
    {
        ParameterExpression rowParam = Expression.Parameter(typeof(RowType), "r");
        MemberExpression column = Expression.Property(rowParam, filterColumnName);

        BinaryExpression filter = buildComparison(column);
        return Expression.Lambda<Func<RowType, bool>>(filter, rowParam);
    }

}

您可以使用以下内容:

var whereClause = BuildListFilter(queryColumnName, columnValues);
CloudTableQuery<RowType> query = (from r in tableServiceContext.CreateQuery<MyRow>("MyTable")
                                    where r.PartitionKey == partitionKey
                                    select r)
                                    .Where(whereClause) //Add in our multiple where clauses
                                    .AsTableServiceQuery(); //Convert to table service query
var results = query.ToList();

另请注意,Table服务会对每个查询强制执行最大数量的约束。记录的最大值是每个查询15个,但是当我上次尝试这个时(前一段时间),实际最大值是14。

答案 3 :(得分:0)

在表格存储中构建这样的东西是非常麻烦的;类似于在圆孔中强制方形钉。

相反,您可以考虑使用Blob存储来存储您的Blogs和Lucene.NET来实现您对标记的搜索。 Lucene还允许更复杂的搜索,如(Tag =“A”和Tag =“B”和Tag!=“C”),此外还允许搜索博客文本本身,如果您愿意的话。

http://code.msdn.microsoft.com/windowsazure/Azure-Library-for-83562538