Raven DB 对数组项的子集进行过滤,并对最便宜的过滤结果项进行排序

Raven DB filter on subset of array items and sort on the cheapest of the filter results items

假设我有一个父项 class,我根据各种属性进行过滤,其中一个是 属性,它是一个项目数组。 现在说如果我的项目数组高于最小值且低于最大值,我只想 return 父项……没关系,我可以解决这个问题; 如果我想对这些项目的过滤结果集进行排序怎么办

我制作了一个 c# fiddle 示例来展示我试图实现的目标: https://dotnetfiddle.net/mV4d28 (请注意,即使 foo1 的数组中的项目少于 foo2 中的项目,foo2 也会先 returned)

因为我需要使用索引来执行此操作,所以我需要索引能够根据查询中使用的过滤条件计算顺序。

我知道 elasticsearch 有一个内部命中函数可以做到这一点并且 mongo 有管道也可以做到这一点所以我确定 Raven 也必须有办法做到这一点?

我希望只使用索引和婴儿车的转换我可以实现这个所以我试了一下:

我的索引和转换看起来像这样

public class familyTransfrom : AbstractTransformerCreationTask<ParentItem>
{
    public class Result : ParentItem{
        public double[] ChildItemValuesFiltered { get; set; }
    }
    public familyTransfrom(){
        TransformResults = parents => from parent in parents
        let filterMinValue = Convert.ToDouble(ParameterOrDefault("FilterMinValue", Convert.ToDouble(0)).Value<double>())
        let filterMaxValue = Convert.ToDouble(ParameterOrDefault("FilterMaxValue", Convert.ToDouble(9999)).Value<double>())
        select new Result{
            ParentItemId = parent.ParentItemId,
            ParentItemName = parent.ParentItemName,
            ParentItemValue = parent.ParentItemValue,
            //ChildItemValuesFiltered = parent.ChildItems.Where(p => p.ChildItemValues.Any(y => Convert.ToDouble(y) >= Convert.ToDouble(filterMinValue) && Convert.ToDouble(y) <= Convert.ToDouble(filterMaxValue))).SelectMany(t => t.ChildItemValues).ToArray<double>(),
            ChildItemValuesFiltered = parent.ChildItems.SelectMany(p => p.ChildItemValues.Where(y => Convert.ToDouble(y) >= Convert.ToDouble(filterMinValue) && Convert.ToDouble(y) <= Convert.ToDouble(filterMaxValue))).ToArray<double>(),
            ChildItems = Recurse(parent, x => x.ChildItems).Select(y => y).ToArray()    
        };
    }
}
public class familyIndex : AbstractIndexCreationTask<ParentItem>{
        public class Result : ParentItem {
                public double[] ChildItemValues { get; set; }
        }             
        public familyIndex(){
            Map = parents => from parent in parents
                select new Result{
                    ParentItemId = parent.ParentItemId,
                    ParentItemName = parent.ParentItemName,
                    ParentItemValue = parent.ParentItemValue,
                    ChildItemValues = parent.ChildItems.SelectMany(p => p.ChildItemValues.Select(y => y)).ToArray(),  
                    ChildItems = Recurse(parent, x => x.ChildItems).Select(y => y).ToArray()  
                };                                                                              
                Index("ParentItemId", FieldIndexing.Analyzed);
                Index("ParentItemName", FieldIndexing.Analyzed);
                Index("ParentItemValue", FieldIndexing.Analyzed);
        Index("ChildItemValues", FieldIndexing.Analyzed);
        Index("ChildItems", FieldIndexing.Analyzed);
             }
}

我的查询如下,(这是使用 live raven playground,所以它应该开箱即用,你想使用它)

using (IDocumentStore store = new DocumentStore { Url = "http://live-test.ravendb.net/", DefaultDatabase = "altha" })
{
    store.Initialize(); 
    using (IDocumentSession session = store.OpenSession()) 
    {
        if(1 == 2){         
            //foreach (ParentItem element in data.OfType<ParentItem>()) {
            //  session.Store((ParentItem)element);
            //  session.SaveChanges();
            //}
            new familyIndex().Execute(store);
            new familyTransfrom().Execute(store);
        }else{
            double filterMinValue = 3.0;
            double filterMaxValue = 4.0;
            var results =  session
                .Advanced
                .DocumentQuery<familyIndex.Result,familyIndex>()
                .WhereBetweenOrEqual("ChildItemValues", filterMinValue, filterMaxValue)
                .SetResultTransformer<familyTransfrom, familyTransfrom.Result>()
                .SetTransformerParameters(new Dictionary<string, RavenJToken> {
                    { "FilterMinValue", filterMinValue },
                    { "FilterMaxValue", filterMaxValue } })
                .OrderBy("ChildItemValues")
                .OfType<ParentItem>().ToList(); 
                results.Dump();                         
    }}
}

我发现我无法使用转换结果中的 "ChildItemValuesFiltered" 作为其非索引。所以除非我可以通过转换的结果来订购?我无法让它工作,因为虽然它过滤它 dosnt 顺序正确。 有没有其他方法可以使用投影或交集或排名或减少尝试方法来实现我想要的?

我在想如果我必须的话,也许我可以使用 https://ravendb.net/docs/article-page/3.5/csharp/indexes/querying/sorting#custom-sorting

然后做这样的事情:

public class SortByNumberOfCharactersFromEnd : IndexEntriesToComparablesGenerator
{
    private readonly double filterMinValue;
    private readonly double filterMinValue;

    public SortByNumberOfCharactersFromEnd(IndexQuery indexQuery)
        : base(indexQuery)
    {
        filterMinValue = IndexQuery.TransformerParameters["FilterMinValue"].Value<double>();     // using transformer parameters to pass the length explicitly
        filterMaxValue = IndexQuery.TransformerParameters["FilterMaxValue"].Value<double>();
    }

    public override IComparable Generate(IndexReader reader, int doc)
    {
        var document = reader.Document(doc);
        double[] childItemValues = (double[])document.GetValues("ChildItemValuesFiltered").Select(double.Parse).ToArray();          // this field is stored in index
        return childItemValues.Where(x => x >= min && x <= max).Min();
    }
}

然后执行一个 where 过滤器和 order by 子句使用索引和转换传递我在 where 过滤器中使用的相同 prams。但是我不确定这是否有效? 更重要的是,我不确定如何将排序 dll 放入插件中,即 class 应该使用什么名称 space,它需要导入什么名称 spaces,什么程序集需要使用的名称剂量等 根据 https://ravendb.net/docs/article-page/3.5/csharp/server/plugins/what-are-plugins,我只需要将 dll 放入其中,raven 就会这样做,但是我似乎找不到 space 我需要为 IndexEntriesToComparablesGenerator 引用的名称?

我正在使用 linqpad 5 来测试我的东西...所以为了使用自定义顺序我必须参考 class

任何提示或建议或如何 guild/examples 欢迎

所以我没想到我可以在转换中进行过滤

TransformResults = parents => from parent in parents
        let filterMinValue = Convert.ToDouble(ParameterOrDefault("FilterMinValue", Convert.ToDouble(0)).Value<double>())
        let filterMaxValue = Convert.ToDouble(ParameterOrDefault("FilterMaxValue", Convert.ToDouble(9999)).Value<double>())
        select new {
            ParentItemId = parent.ParentItemId,
            ParentItemName = parent.ParentItemName,
            ParentItemValue = parent.ParentItemValue,
            //ChildItemValuesFiltered = parent.ChildItems.Where(p => p.ChildItemValues.Any(y => Convert.ToDouble(y) >= Convert.ToDouble(filterMinValue) && Convert.ToDouble(y) <= Convert.ToDouble(filterMaxValue))).SelectMany(t => t.ChildItemValues).ToArray<double>(),
            ChildItemValuesFiltered = parent.ChildItems.SelectMany(p => p.ChildItemValues.Where(y => Convert.ToDouble(y) >= Convert.ToDouble(filterMinValue) && Convert.ToDouble(y) <= Convert.ToDouble(filterMaxValue))).ToArray<double>(),
            ChildItems = Recurse(parent, x => x.ChildItems).Select(y => y).ToArray()    
        } into r
        where r.ChildItemValuesFiltered.Length > 0
        orderby r.ChildItemValuesFiltered.Min()
        select r;

这给了我想要的,这里是示例查询:

http://live-test.ravendb.net/databases/altha/indexes/familyIndex?start=0&pageSize=25&resultsTransformer=familyTransfrom&tp-FilterMinValue=3&tp-FilterMaxValue=4

我不能把这归功于 raven 的人帮助了我,但为其他人分享了知识