RavenDB - 防止查询错误异常

RavenDB - prevent exception on query error

我正在尝试在允许用户输入特定字段和术语并查询结果的应用程序中实现一些自定义搜索;因此,例如,有一个文本框,他们可以键入... Name: Stacey 它会运行一些代码。

如果他们给它一个有效的名称,那很好用。但如果他们给它找不到的信息,我就会遇到异常,我不知道如何阻止它。 try 似乎也不起作用;代码不算太复杂,但是有点乱。

基本上,它在从控制器方法接收到零个或两个字符串后运行。第一个字符串是字段,第二个是要查询的值。如果我给它有效的输入,它就可以正常工作;但是其他任何事情都会导致程序崩溃。我能做些什么吗?

冗长的回调过程是因为每个控制器方法需要对结果做出不同的反应,但这与此问题无关。

    private JsonResult JsonDataFromDataSourceRequest<T, TIndexCreator>
        ([DataSourceRequest] DataSourceRequest request,
        Func<IDocumentQuery<T>, string, string, IDocumentQuery<T>> search,
        Func<IDocumentQuery<T>, IDocumentQuery<T>> sort)
        where T : IHasName
        where TIndexCreator : Raven.Client.Indexes.AbstractIndexCreationTask, new() {
        RavenQueryStatistics statistics;

        var query = RavenSession
            .Advanced.DocumentQuery<T, TIndexCreator>()
            .WhereEquals("collection", typeof(T).RavenCollection())
            .WaitForNonStaleResultsAsOfNow()
            .Statistics(out statistics); // output our query statistics

        // we can accept a sorting system, or additional query options here
        if (sort != null)
            query = sort(query);

        if (request.Filters != null) {
            if (request.Filters.Any()) {
                var filters = request.Filters.ToFilters();

                // determine the field/term to search by
                var name = filters.Count > 0 ? filters[0].Value.ToString() : null;
                var field = filters.Count > 0 ? filters[0].Member.ToString() : "Name";

                if (name != null && !String.IsNullOrEmpty(name)) {
                    query = query.AndAlso().Search(field, name);
                }
            }
        }

        var results = query
            .OrderBy(n => n.Name)
            .Skip((request.Page - 1) * request.PageSize)
            .Take(request.PageSize)
            .ToList();

        var totalResults = statistics.TotalResults;

        return Json(new { data = results, total = totalResults });
    }

更新

System.InvalidOperationException

抛出的异常是System.InvalidOperationException。当我深入调试器时,发送到 Raven 的实际查询采用以下形式:

{collection:users AND DisplayName: ( Stacey)}

这是一个大杂烩;让我解释一些事情。

collection 存在是因为此索引用于许多不同的对象,但许多(如果不是全部)共享几个相似的字段。将集合作为查询的一部分似乎比尝试在 Query<T> 调用中找到使用 Type 的方法更简单。

是否可以保持灵活性并为查询提供特定的 TModel 查询?我想避免做几十个重复的索引。

当用户尝试查找不属于正在检查的对象类型的字段时,这似乎确实会发生。

换句话说,即使 Owner 被编入索引,如果用户针对查找 Users 的页面键入 Owner: Ciel,其中 没有 Owner 字段(但很多很多其他 类 有),它会抛出这个错误。如果他们尝试输入未编入索引的字段,它会执行此操作。

是否可以returnnothing如果查询失败

下面是我正在使用的索引。也许没有 collection 字段有一种更简单的方法可以做到这一点,但我并没有意识到它(但我仍然没有经验)。 几乎有 28 个不同的页面,每个页面都具有搜索功能。一些实体之间有太多相似的字段,以至于制作 28 个不同的索引似乎毫无意义。

public class EntityByName : AbstractIndexCreationTask {
    public override IndexDefinition CreateIndexDefinition() {
        return new IndexDefinition {
            Map = @"
                from doc in docs 
                let collection = doc[""@metadata""][""Raven-Entity-Name""] 
                select new { 
                    doc.Id, 
                    doc.Name, 
                    doc.Owner,  
                    doc.Group,
                    doc.Email,
                    Tags = doc.Tags.Select( r => r.Name ),
                    collection 
                };",
            Indexes ={
                {"Id", FieldIndexing.Analyzed},
                {"Name", FieldIndexing.Analyzed},
                {"Owner", FieldIndexing.Analyzed},
                {"Tags", FieldIndexing.Analyzed},
                {"Group", FieldIndexing.Analyzed},
                {"Email", FieldIndexing.Analyzed}
            }
        };
    }

    public override string IndexName {
        get { return "Raven/DocumentsByEntity"; }
    }
}

异常消息

这是完整的异常消息。

Url: "/databases/dev-isolated/indexes/Raven/DocumentsByEntity?&query=collection%3Ausers%20AND%20Number%3A%28%203%29&pageSize=5&sort=Name&SortHint-collection=String&SortHint-Name=String&cutOff=2015-10-02T15%3A12%3A31.5784892Z&waitForNonStaleResultsAsOfNow=true"

System.ArgumentException: The field 'DisplayName' is not indexed, cannot query on fields that are not indexed
   at Raven.Database.Indexing.Index.AssertQueryDoesNotContainFieldsThatAreNotIndexed(IndexQuery indexQuery, AbstractViewGenerator viewGenerator) in c:\Builds\RavenDB-Stable-3.0\Raven.Database\Indexing\Index.cs:line 1130
   at Raven.Database.Indexing.Index.IndexQueryOperation.<Query>d__5a.MoveNext() in c:\Builds\RavenDB-Stable-3.0\Raven.Database\Indexing\Index.cs:line 1252
   at Raven.Database.Util.ActiveEnumerable`1..ctor(IEnumerable`1 enumerable) in c:\Builds\RavenDB-Stable-3.0\Raven.Database\Util\ActiveEnumerable.cs:line 16
   at Raven.Database.Actions.QueryActions.DatabaseQueryOperation.Init() in c:\Builds\RavenDB-Stable-3.0\Raven.Database\Actions\QueryActions.cs:line 245
   at Raven.Database.Actions.QueryActions.<>c__DisplayClasse.<Query>b__a(IStorageActionsAccessor accessor) in c:\Builds\RavenDB-Stable-3.0\Raven.Database\Actions\QueryActions.cs:line 118
   at Raven.Storage.Esent.TransactionalStorage.ExecuteBatch(Action`1 action, EsentTransactionContext transactionContext) in c:\Builds\RavenDB-Stable-3.0\Raven.Database\Storage\Esent\TransactionalStorage.cs:line 843
   at Raven.Storage.Esent.TransactionalStorage.Batch(Action`1 action) in c:\Builds\RavenDB-Stable-3.0\Raven.Database\Storage\Esent\TransactionalStorage.cs:line 807
   at Raven.Database.Actions.QueryActions.Query(String index, IndexQuery query, CancellationToken externalCancellationToken) in c:\Builds\RavenDB-Stable-3.0\Raven.Database\Actions\QueryActions.cs:line 108
   at Raven.Database.Server.Controllers.IndexController.PerformQueryAgainstExistingIndex(String index, IndexQuery indexQuery, Etag& indexEtag, HttpResponseMessage msg, CancellationToken token) in c:\Builds\RavenDB-Stable-3.0\Raven.Database\Server\Controllers\IndexController.cs:line 625
   at Raven.Database.Server.Controllers.IndexController.ExecuteQuery(String index, Etag& indexEtag, HttpResponseMessage msg, CancellationToken token) in c:\Builds\RavenDB-Stable-3.0\Raven.Database\Server\Controllers\IndexController.cs:line 570
   at Raven.Database.Server.Controllers.IndexController.GetIndexQueryResult(String index, CancellationToken token) in c:\Builds\RavenDB-Stable-3.0\Raven.Database\Server\Controllers\IndexController.cs:line 541
   at Raven.Database.Server.Controllers.IndexController.IndexGet(String id) in c:\Builds\RavenDB-Stable-3.0\Raven.Database\Server\Controllers\IndexController.cs:line 182
   at lambda_method(Closure , Object , Object[] )
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.<GetExecutor>b__9(Object instance, Object[] methodParameters)
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Controllers.ExceptionFilterResult.<ExecuteAsync>d__0.MoveNext()

修复

我做了适当的修改,这是该方法的最终结果;

    private JsonResult JsonDataFromDataSourceRequest<T, TIndexCreator>
        ([DataSourceRequest] DataSourceRequest request,
        Func<IDocumentQuery<T>, string, string, IDocumentQuery<T>> search,
        Func<IDocumentQuery<T>, IDocumentQuery<T>> sort)
        where T : IHasName
        where TIndexCreator : Raven.Client.Indexes.AbstractIndexCreationTask, new() {

        try {

            // we need to be able to catch some query statistics to make sure that the
            // grid view is complete and accurate, with paging
            RavenQueryStatistics statistics;

            // try to query the items listing as quickly as we can, getting only the
            // page we want out of it
            var query = RavenSession
                .Advanced.DocumentQuery<T, TIndexCreator>()
                .WhereEquals("collection", typeof(T).RavenCollection())
                .WaitForNonStaleResultsAsOfNow()
                .Statistics(out statistics); // output our query statistics

            // we can accept a sorting system, or additional query options here
            if (sort != null)
                query = sort(query);

            // deserialize the contents of the kendo grid filter
            if (request.Filters != null) {
                if (request.Filters.Any()) {
                    var filters = request.Filters.ToFilters();

                    var name = filters.Count > 0 ? filters[0].Value.ToString() : null;
                    var field = filters.Count > 0 ? filters[0].Member.ToString() : "Name";

                    if (name != null && !String.IsNullOrEmpty(name)) {
                        query = search(query, field, name);
                    }
                }
            }

            // finish constructing the items query
            var results = query
                .OrderBy(n => n.Name)
                .Skip((request.Page - 1) * request.PageSize)
                .Take(request.PageSize)
                .ToList();

            var totalResults = statistics.TotalResults;

            return Json(new { data = results, total = totalResults, errors = "" });
        }
        catch {

            // the user tried to ask for something
            // that doesn't exist, or a field that
            // cannot be queried
            var results = new List<T>();

            // total results is always 0 on an error
            var totalResults = 0;

            var errors = new List<string> {
                "You have attempted to query an invalid field, or given an inappropriate value."
            };

            return Json(new { data = results, total = totalResults, errors = errors });
        }
    }

错误很明显:

The field 'DisplayName' is not indexed, cannot query on fields that are not indexed