3

私は多対多の関係を持っています:

  • 投稿には多くのタグを付けることができます
  • タグには多くの投稿を含めることができます

モデル:

public class Post
{
    public virtual string Title { get; set; }
    public virtual string Content{ get; set; }
    public virtual User User { get; set; }
    public virtual ICollection<Tag> Tags { get; set; }
}

public class Tag
{
    public virtual string Title { get; set; }
    public virtual string Description { get; set; }
    public virtual User User { get; set; }
    public virtual ICollection<Post> Posts { get; set; }
}

複数のタグに属するすべての投稿をカウントしたいのですが、NHibernate でこれを行う方法がわかりません。これが最善の方法かどうかはわかりませんが、MS SQL で次のクエリを使用しました。

SELECT COUNT(*) 
    FROM 
    (
    SELECT Posts.Id FROM Posts
    INNER JOIN Users ON Posts.UserId=Users.Id 
    LEFT JOIN TagsPosts ON Posts.Id=TagsPosts.PostId 
    LEFT JOIN Tags ON TagsPosts.TagId=Tags.Id 
    WHERE Users.Username='mr.nuub' AND (Tags.Title in ('c#', 'asp.net-mvc')) 
    GROUP BY Posts.Id 
    HAVING COUNT(Posts.Id)=2
    )t

しかし、NHibernate は from 句でのサブクエリを許可しません。誰かがHQLでこれを行う方法を教えてくれたらうれしいです.

4

3 に答える 3

1

サブクエリなしでこの結果を取得する方法を見つけました。これは nHibernate Linq で機能します。nHibernate でサポートされている linq 式のサブセットのため、実際にはそれほど簡単ではありませんでした...しかしとにかく

クエリ:

var searchTags = new[] { "C#", "C++" };
var result = session.Query<Post>()
        .Select(p => new { 
            Id = p.Id, 
            Count = p.Tags.Where(t => searchTags.Contains(t.Title)).Count() 
        })
        .Where(s => s.Count >= 2)
        .Count();

次の sql ステートメントが生成されます。

select cast(count(*) as INT) as col_0_0_ 
from Posts post0_ 
where (
    select cast(count(*) as INT)
    from PostsToTags tags1_, Tags tag2_ 
    where post0_.Id=tags1_.Post_id 
    and tags1_.Tag_id=tag2_.Id 
    and (tag2_.Title='C#' or tag2_.Title='C++'))>=2

これにユーザー制限を組み込むことができるはずです。

以下は私のテスト設定と生成されたランダムデータです

public class Post
{
    public Post()
    {
        Tags = new List<Tag>();
    }
    public virtual void AddTag(Tag tag)
    {
        this.Tags.Add(tag);
        tag.Posts.Add(this);
    }
    public virtual string Title { get; set; }
    public virtual string Content { get; set; }
    public virtual ICollection<Tag> Tags { get; set; }
    public virtual int Id { get; set; }
}

public class PostMap : ClassMap<Post>
{
    public PostMap()
    {
        Table("Posts");

        Id(p => p.Id).GeneratedBy.Native();

        Map(p => p.Content);

        Map(p => p.Title);

        HasManyToMany<Tag>(map => map.Tags).Cascade.All();
    }
}

public class Tag
{
    public Tag()
    {
        Posts = new List<Post>();
    }
    public virtual string Title { get; set; }
    public virtual string Description { get; set; }
    public virtual ICollection<Post> Posts { get; set; }
    public virtual int Id { get; set; }
}

public class TagMap : ClassMap<Tag>
{
    public TagMap()
    {
        Table("Tags");
        Id(p => p.Id).GeneratedBy.Native();

        Map(p => p.Description);
        Map(p => p.Title);
        HasManyToMany<Post>(map => map.Posts).LazyLoad().Inverse();
    }
}

テスト走行:

var sessionFactory = Fluently.Configure()
    .Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2012
        .ConnectionString(@"Server=.\SQLExpress;Database=TestDB;Trusted_Connection=True;")
        .ShowSql)
    .Mappings(m => m.FluentMappings
        .AddFromAssemblyOf<PostMap>())
    .ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(false, true))
    .BuildSessionFactory();

using (var session = sessionFactory.OpenSession())
{
    var t1 = new Tag() { Title = "C#", Description = "C#" };
    session.Save(t1);
    var t2 = new Tag() { Title = "C++", Description = "C/C++" };
    session.Save(t2);
    var t3 = new Tag() { Title = ".Net", Description = "Net" };
    session.Save(t3);
    var t4 = new Tag() { Title = "Java", Description = "Java" };
    session.Save(t4);
    var t5 = new Tag() { Title = "lol", Description = "lol" };
    session.Save(t5);
    var t6 = new Tag() { Title = "rofl", Description = "rofl" };
    session.Save(t6);
    var tags = session.Query<Tag>().ToList();
    var r = new Random();

    for (int i = 0; i < 1000; i++)
    {
        var post = new Post()
        {
            Title = "Title" + i,
            Content = "Something awesome" + i,
        };

        var manyTags = r.Next(1, 3);

        while (post.Tags.Count() < manyTags)
        {
            var index = r.Next(0, 6);
            if (!post.Tags.Contains(tags[index]))
            {
                post.AddTag(tags[index]);
            }
        }

        session.Save(post);
    }
    session.Flush();

    /* query test */
    var searchTags = new[] { "C#", "C++" };
    var result = session.Query<Post>()
            .Select(p => new { 
                Id = p.Id, 
                Count = p.Tags.Where(t => searchTags.Contains(t.Title)).Count() 
            })
            .Where(s => s.Count >= 2)
            .Count();

    var resultOriginal = session.CreateQuery(@"
       SELECT COUNT(*) 
        FROM 
        (
        SELECT count(Posts.Id)P FROM Posts
        LEFT JOIN PostsToTags ON Posts.Id=PostsToTags.Post_id 
        LEFT JOIN Tags ON PostsToTags.Tag_id=Tags.Id 
        WHERE Tags.Title in ('c#', 'C++')
        GROUP BY Posts.Id 
        HAVING COUNT(Posts.Id)>=2
        )t
    ").List()[0];

    var isEqual = result == (int)resultOriginal;
}

最後にわかるように、元のクエリ (ユーザーなし) に対してテストを行いますが、実際には同じカウントです。

于 2013-10-02T18:26:13.890 に答える
0

編集:将来的には、質問を最後の言葉まで読む必要があります。私は見ていただろうin HQL...


いくつかRowCountの検索の後、クエリ内のグループ化が削除されることに気付きました ( https://stackoverflow.com/a/8034921/1236044 )。QueryOverを使用して解決策を見つけ、SubQueryここに情報として投稿しました。このソリューションは、ある程度のモジュール性を提供し、サブクエリ自体からカウントを分離し、そのまま再利用できるため、興味深いと思います。

var searchTags = new[] { "tag1", "tag3" };
var userNames = new[] { "mr.nuub" };
Tag tagAlias = null;
Post postAlias = null;
User userAlias = null;

var postsSubquery =
    QueryOver.Of<Post>(() => postAlias)
            .JoinAlias(() => postAlias.Tags, () => tagAlias)
            .JoinAlias(() => postAlias.User, () => userAlias)
            .WhereRestrictionOn(() => tagAlias.Title).IsIn(searchTags)
            .AndRestrictionOn(() => userAlias.UserName).IsIn(userNames)
            .Where(Restrictions.Gt(Projections.Count<Post>(p => tagAlias.Title), 1));

var numberOfPosts = session.QueryOver<Post>()
            .WithSubquery.WhereProperty(p => p.Id).In(postsSubquery.Select(Projections.Group<Post>(p => p.Id)))
            .RowCount();

これが役立つことを願っています

于 2013-10-02T13:47:45.783 に答える
0

HQL では:

var hql = "select count(p) from Post p where p in " +
    "(select t.Post from Tag t group by t.Post having count(t.Post) > 1)";
var result = session.Query(hql).UniqueResult<long>();

タグやその他の条件を指定する必要がある場合は、サブクエリに条件を追加できます。

于 2013-10-02T18:45:21.057 に答える