1

I want to be able to bring back all blog posts which have all of the tags I specify.

public class Post
{
    public int Name { get; set; }
    public List<string> Tags { get; set; }
}

I want to bring back all posts with 'c#' AND 'html' tags.

This question is the same as mine, though I am unable to get my example working. Linq query with multiple Contains/Any for RavenDB

I would like to know why the example below is not bringing back any results when there is one post with 'c#' and 'html' in the tags.

It would be great if anyone could shed light on if there is a new, more elegant way to solve this problem now, ideally with the strongly typed query syntax i.e

var query = s.Query<Entity, IndexClass>()

-

using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Raven.Abstractions.Indexing;
using Raven.Client.Embedded;
using Raven.Client.Indexes;

namespace RavenDB
{
    public class Blog
    {
        public string Name { get; set; }
        public List<string> Tags { get; set; }
    }

    public class BlogsByTags : AbstractIndexCreationTask<Blog>
    {
        public BlogsByTags()
        {
            Map = docs => from doc in docs
                          select new
                              {
                                  Tags = doc.Tags
                              };

            Index(x => x.Tags, FieldIndexing.Analyzed);
        }
    }

    [TestFixture]
    public class Runner : UsingEmbeddedRavenStore
    {
        [Test]
        public void Run()
        {
            Open();
            IndexCreation.CreateIndexes(typeof(BlogsByTags).Assembly, Store);

            var blogs = new List<Blog>
                {
                    new Blog{Name = "MVC", Tags = new List<string>{"html","c#"}},
                    new Blog{Name = "HTML5", Tags = new List<string>{"html"}},
                    new Blog{Name = "Version Control", Tags = new List<string>{"git"}},
                };

            using (var session = Store.OpenSession())
            {             
                foreach (var blog in blogs)
                {
                    session.Store(blog);
                }
                session.SaveChanges();
            }

            var tags = new List<string> { "c#", "html" };

            List<Blog> blogQueryResults;

            using (var s = Store.OpenSession())
            {

                blogQueryResults = s.Advanced.LuceneQuery<Blog, BlogsByTags>()
                  .Where(string.Format("Tags:({0})", string.Join(" AND ", tags))).ToList();                
            }

            Assert.AreEqual(1, blogQueryResults.Count());
        }
    }

    public abstract class UsingEmbeddedRavenStore
    {
        protected EmbeddableDocumentStore Store { get; set; }

        protected void Open()
        {
            Store = new EmbeddableDocumentStore
            {
                RunInMemory =
                    true
            };

            Store.Initialize();
        }

        protected void Dispose()
        {
            Store.Dispose();
        }
    }
}
Community
  • 1
  • 1
CountZero
  • 6,171
  • 3
  • 46
  • 59
  • Is there a question in there somewhere? Please reduce your code to just the parts to support your question, and ask the question clearly. Thanks. – Matt Johnson-Pint May 07 '13 at 14:35
  • Thanks for looking an apologies for unclear question. Re-written to use blog post / tags to make question clearer and code has been stripped to only show relevant aspects. – CountZero May 07 '13 at 22:14

1 Answers1

4

The only problem is that since you are in a unit test, you need to explicitly allow time between when the data is written and when you check the index. Otherwise, your index is stale. See these docs.

s.Advanced.LuceneQuery<Blog, BlogsByTags>()

 // Unit tests should wait explicitly.
 // Don't do this outside of a unit test.
 .WaitForNonStaleResults()

 .Where(string.Format("Tags:({0})", string.Join(" AND ", tags)))

You also asked how to perform the same query without resorting to the advanced lucene syntax. You can make used of the .Search extension method, as such:

s.Query<Blog, BlogsByTags>()
 .Customize(x => x.WaitForNonStaleResults())
 .Search(x => x.Tags, string.Join(" AND ", tags))

There's one other thing you should change. When unit testing, you really don't want the unit test to be scanning the assembly for indexes. It may pick up indexes written for other tests.

// Instead of scanning like this
IndexCreation.CreateIndexes(typeof(BlogsByTags).Assembly, store);

// Create the single index like this
store.ExecuteIndex(new BlogsByTags());

And lastly, I'd like to point out the RavenDB.Tests.Helpers nuget package, which you can use to simplify your tests. It does a lot of the setup work for you. It uses XUnit though - so if you're tied to NUnit then you may want to do things your own way.

Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575