2

我有一组带有 Url 属性的文档,如下所示:

{ Url: www.test.com }  
{ Url: www.test.com/abc }  
{ Url: www.test.com/abc/xyz }

我想使用www.test.com/abc查询文档,以便返回与所有内容(包括查询 url)上的 url 匹配的所有文档。即结果将返回:

{ Url: www.test.com }  
{ Url: www.test.com/abc }

以下是我为完成此任务而编写的查询。如您所见,它依赖于在 queryTerm 上执行“Starts With”,而不是在文档的属性(查询的左侧)上执行。

var queryTerm = "www.test.com/abc";
pages = session.Query<MyDocument>()
               .Where(x => queryTerm.StartsWith(x.Url));

这在 Raven 中不起作用;我收到“不支持表达式类型”错误。有任何想法吗?

4

1 回答 1

1

Raven 的表达方式似乎并不支持这一点。有趣的是,查询x => queryTerm.StartsWith(x.Url)可以分解为以下内容:

x => x.Url == queryTerm ||
     x.Url == queryTerm.Remove(queryTerm.Length - 1) ||
     x.Url == queryTerm.Remove(queryTerm.Length - 2) ||
     x.Url == queryTerm.Remove(queryTerm.Length - 3) ||
     x.Url == queryTerm.Remove(queryTerm.Length - 4) ||
     etc.

可以编码为:

var terms = Enumerable.Range(0, queryTerm.Length)
                      .Skip(1)
                      .Select(i => queryTerm.Remove(i));

pages = session.Query<MyDocument>()
               .Where(x => terms.Contains(x.Url));

除了Raven 也不支持Contains()那样。所以我把这个编码:

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

namespace RavenTest
{
    [TestFixture]
    public class HowToDoBackwardsStartsWithInRaven
    {
        private readonly string[] _testUrls = new[]
            {
                "www.bad.com",
                "www.test.com",
                "www.test.com/abc",
                "www.test.com/abc/xyz"
            };

        private readonly string _queryTerm = "www.test.com/abc";

        private readonly string[] _expectedResults = new[]
            {
                "www.test.com",
                "www.test.com/abc"
            };


        [Test]
        public void FailsWithContains()
        {
            List<MyDocument> results;

            using (var store = InitStore())
            {
                LoadTestData(store);

                using (var session = store.OpenSession())
                {
                    var terms = GetAllStartingSubstrings(_queryTerm);

                    results = session.Query<MyDocument>()
                        .Where(x => terms.Contains(x.Url))
                        .ToList();
                }
            }

            Assert.That(results.Select(p => p.Url), Is.EquivalentTo(_expectedResults));
        }

        [Test]
        public void SucceedsWithSuperWhere()
        {
            List<MyDocument> results;

            using (var store = InitStore())
            {
                LoadTestData(store);

                using (var session = store.OpenSession())
                {
                    var terms = GetAllStartingSubstrings(_queryTerm);

                    var query = session.Query<MyDocument>()
                        .Where(x => x.Url.Length <= _queryTerm.Length);

                    foreach (var term in terms)
                        query = query.Where(x => x.Url == term ||
                                                 x.Url.Length != term.Length);

                    results = query.ToList();
                }
            }

            Assert.That(results.Select(p => p.Url), Is.EquivalentTo(_expectedResults));
        }

        private static IDocumentStore InitStore()
        {
            var store = new EmbeddableDocumentStore
                {
                    RunInMemory = true,
                };

            return store.Initialize();
        }

        private static string[] GetAllStartingSubstrings(string queryTerm)
        {
            return Enumerable.Range(0, queryTerm.Length)
                .Skip(1)
                .Select(i => queryTerm.Remove(i))
                .ToArray();
        }


        private void LoadTestData(IDocumentStore store)
        {
            using (var session = store.OpenSession())
            {
                foreach (var testUrl in _testUrls)
                    session.Store(new MyDocument {Url = testUrl});

                session.SaveChanges();
            }
        }

        public class MyDocument
        {
            public string Id { get; set; }
            public string Url { get; set; }
        }
    }
}

现在......我不知道索引的效率如何,但测试确实通过了。

于 2012-06-27T04:16:15.940 回答