2016-11-02 12 views
0

(EDITED)ドキュメントのテーブルとドキュメントのトリプルテーブルを使ってEFコアを試しています。各文書には、述語とオブジェクトで表される0以上の(RDF)トリプルを含めることができます。EFコア:リスト内の項目に一致するフィルタレコード

対応するEFエンティティは以下のとおりです。

public sealed class Document 
{ 
    public string Id { get; set; } 
    public string CreatorId { get; set; } 
    public string CategoryId { get; set; } 
    public string Title { get; set; } 
    public string Author { get; set; } 
    // ... 
    public IList<DocumentTriple> DocumentTriples { get; set; } 
} 

と:

public sealed class DocumentTriple 
{ 
    public int Id { get; set; } 
    public string DocumentId { get; set; } 
    public string Predicate { get; set; } 
    public string Object { get; set; } 
    public Document Document { get; set; } 
} 

彼らのSQL定義:

CREATE TABLE [dbo].[Document](
    [Id] [varchar](32) NOT NULL, 
    [CreatorId] [varchar](50) NOT NULL, 
    [CategoryId] [varchar](20) NOT NULL, 
    [Title] [nvarchar](500) NOT NULL, 
    [Author] [nvarchar](500) NOT NULL, 
    /* ... */ 
CONSTRAINT [PK_dbo.Document] PRIMARY KEY CLUSTERED 
(
    [Id] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 
GO 

CREATE TABLE [dbo].[DocumentTriple](
    [Id] [int] IDENTITY(1,1) NOT NULL, 
    [DocumentId] [varchar](32) NOT NULL, 
    [Predicate] [varchar](100) NOT NULL, 
    [Object] [nvarchar](1000) NOT NULL, 
CONSTRAINT [PK_DocumentTriple] PRIMARY KEY CLUSTERED 
(
    [Id] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 
GO 
ALTER TABLE [dbo].[DocumentTriple] WITH CHECK ADD CONSTRAINT [FK_dbo.DocumentTriple_dbo.Document_DocumentId] FOREIGN KEY([DocumentId]) 
REFERENCES [dbo].[Document] ([Id]) 
ON DELETE CASCADE 
GO 
ALTER TABLE [dbo].[DocumentTriple] CHECK CONSTRAINT [FK_dbo.DocumentTriple_dbo.Document_DocumentId] 
GO 

、私は、可変フィルタの数を適用したいと思いますドキュメントには、このパターンを使用します。まず、IQueryabledocuments objecを作成します。 t(トリプルを含む)を入力し、documents = documents.Where(...)のようなフィルタを追加します。

これらのフィルタの1つは、Tupleのリストです。ここで、1は述語のフィルタ値、2はnullまたはobjectのフィルタ値です。私は少なくとも1つのフィルタ述語または述語/値のペアと一致するトリプルのいずれかを持つドキュメントのみを取得したいと思います。

select * from document 
inner join documenttriple on documenttriple.documentid=document.id 
where (documenttriple.[predicate]='somepredicate' and documenttriple.[object]='someobject') 
or (documenttriple.[predicate]='somepredicate') 

がどのように私はEFコードでこれを行うことができます:SQLで

、私はこのような何かをするだろうか?私は試みました:

documents = from d in documents 
    join dt in db.DocumentTriples on d.Id equals dt.DocumentId 
    where filter.Triples.Any(t => t.Item1 == dt.Predicate && 
     (t.Item2 == null || t.Item2 == dt.Object)) 
    select d; 

しかし、これはトリプルをフィルタリングしていないようです。私はより良いより多くのコードサンプルを使用して自分自身を説明しようと

EDIT

。ここに私の(短縮)コードです:あなたが見ることができるように

private IQueryable<Document> ApplyDocumentFilters(IQueryable<Document> documents, 
    DocumentFilter filter) 
{ 
    if (!String.IsNullOrEmpty(filter.CreatorId)) 
     documents = documents.Where(d => d.CreatorId == filter.CreatorId); 
    if (!String.IsNullOrEmpty(filter.CategoryId)) 
     documents = documents.Where(d => d.CategoryId == filter.CategoryId); 
    if (!String.IsNullOrEmpty(filter.Title)) 
     documents = documents.Where(d => d.Title.Contains(filter.Title)); 
    // ...and so forth for all the filter properties... 
    // here I want to filter only the documents which have at least 1 of their triples 
    // matching any of the filter's triples. DOES NOT WORK 
    if (filter.Triples.Count > 0) 
    { 
     documents = from d in documents 
        join dt in db.DocumentTriples on d.Id equals dt.DocumentId 
        where filter.Triples.Any(t => t.Item1 == dt.Predicate && 
                (t.Item2 == null || t.Item2 == dt.Object)) 
        select d; 
    } 
    return documents; 
} 

public PagedData<Document> GetDocuments(DocumentFilter filter) 
{ 
    if (filter == null) throw new ArgumentNullException(nameof(filter)); 
    using (CatalogContext db = new CatalogContext(_options)) 
    { 
     IQueryable<Document> documents = db.Documents 
         .Include(d => d.DocumentTriples) 
         .AsNoTracking().AsQueryable(); 
     documents = ApplyDocumentFilters(documents, filter); 
     int total = documents.Count(); 
     documents = documents.OrderBy(d => d.Title).ThenBy(d => d.PublicationYear); 
     return new PagedData<Document>(total, 
      documents.Skip((filter.PageNumber - 1) * filter.PageSize).Take(filter.PageSize).ToList()); 
    } 
} 

は、私が適用したいすべてのフィルタとフィルタオブジェクトを受け取り、その後、プロパティでのIQueryableプロパティを構築します。トリプルについては、フィルタのトリプルのANYに一致する少なくとも1つのトリプルを持つすべてのドキュメントを取得したい。

答えて

0

のLINQであなたのSQLクエリが

var i = from x in Db.Documents // SELECT * FROM Document 
     join y in Db.DcumentTriple // JOIN DocumentTriples 
     on x.Id equals y.DocumentId // ON DocumentId = DocumentId 
     where (y.Predicate.Equals("somepredicate") && // WHERE (DocumentTriple.[Predicate] = 'somepredicate' && 
     y.Object.Equals("someobject")) || // DocumentTriple.[Object] = 'someobject') || 
     y.Predicate.Equals("somepredict") // DocumentTriple.[Predicate] = 'somepredicate' 
     select new { x, y }; 

あなたがそのような長い道のりを(私はクエリが悪いクエリあるクエリ、翻訳)行く必要はありませんしかし、それは可能です

var i = Db.DocumenTriple.Where(x=> (x.Predicate.Equals("something") && x.Object.Equals("something")) || x.Predicate.Equals("something"); 

これはDb.Documentナビゲーションプロパティを遅延ロードします。あなたが熱心に読みたい場合は、.Include(x=> x.Document)を最後に呼び出します。

更新

あなたがあなたのコメントで述べたように、私は間違っていない場合、あなたは多くのwhere文を持っています。その場合には、以下のが可能です:

public IEnumerable<Expression<Func<TestEntity, Boolean>>> filters = new List<Expression<Func<TestEntity, Boolean>>>() { }; 
public IQueryable<TestEntity> filter(IQueryable<TestEntity> input, IEnumerable<Expression<Func<TestEntity, Boolean>>> filters, int i) { 
     if (i == 0) 
      return input; 
     return filter(input.Where(filters.ElementAt(i)), filters, i - 1); 
} 

ことは、このアプローチで問題におけるフィルタの順序に注意してください。これを望まない場合は、foreachループを使用し、concat結果を別のリストに使用できます。

+0

私は自分自身をうまく説明していないと悪いですが、フィルタを表すタプルの配列があり、同じIQueryable上の条件がますます連結されています。 SQLサンプルはフィルタリングのタイプを示すことを意図していました。私のシナリオではコードが配列を取得し、その中のタプルのいずれかと一致するだけで十分です。結局、私はIQueryable を持っていなければなりません。 db.Documents.Include(d => d.DocumentTriples).AsNoTracking()。AsQueryable()から、いくつかのフィルタを追加し、ソートとページングで終了します。 – Naftis

+0

私はまだこれについて混乱していますが、問題を正しく理解していれば、更新された回答に追加することができます。 – Transcendent

+0

具体的なコードを追加して質問を編集しました。これは私の質問を明確にするはずです。ありがとうございました – Naftis

関連する問題