Elasticsearch 建议合并两种类型的结果

Elasticsearch suggestion is merging result of two types

我在一个索引中有 2 种类型,并且都有建议字段(根据需要)

public class LegalAreaSearchModel : LegalAreaModel
{
    public SuggestField Suggest
    {
        get
        {
            List<string> input = new List<string>();
            string[] childArea = !string.IsNullOrEmpty(this.LegalArea) ? this.LegalArea.Split(' ') : new string[] { "" };
            string[] ParentArea = !string.IsNullOrEmpty(this.Parent) ? this.Parent.Split(' ') : new string[] { "" };
            input.AddRange(childArea);
            input.AddRange(ParentArea);
            return

                new SuggestField
                {
                    Input = input,
                    Output = this.LegalArea,
                    Payload = new
                    {
                        Id = this.RowId,
                        Name = !string.IsNullOrEmpty(this.Parent) ? this.Parent + "/" + this.LegalArea : this.LegalArea,
                        Type = "Specialization"
                    },
                    Weight = !string.IsNullOrEmpty(this.LegalArea) ? this.LegalArea.Length : 0
                };

        }
    }
}

public class LegalDocumentSearchModel : LegalDocumentModel
{


    public LegalDocumentSearchModel()
    {
        //this = ObjectCopier.Clone<LegalDocumentSearchModel>(legalDocumentSearchModel);
        SupplierDetails = new List<LegalDocumentSupplierDetailForSearch>();
        CategoryDetails = new List<CategoryDetails>();

    }

    [Nested()]
    public List<LegalDocumentSupplierDetailForSearch> SupplierDetails { get; set; }

    [Nested()]
    public List<CategoryDetails> CategoryDetails { get; set; }

    public SuggestField Suggest
    {
        get
        {
            return
                    new SuggestField
                    {
                        Input = new List<string>(!string.IsNullOrEmpty(this.Name) ? this.Name.Split(' ') : new string[] { "" }) { this.Name },
                        Output = this.Name,
                        Payload = new
                        {
                            Id = this.RowId,
                            SEOFriendlyURLName = !string.IsNullOrEmpty(this.SEOFriendlyURLName) ? string.Concat(this.SEOFriendlyURLName) : string.Empty,
                            Name = this.Name,
                            ProductType = this.ProductType,
                            Description = this.Description

                        },
                        Weight = !string.IsNullOrEmpty(this.Description) ? this.Description.Length : 0
                    };
        }
    }

}
public class LegalDocumentSupplierDetailForSearch
{
    public string PinCode { get; set; }
    public string Lattitude { get; set; }
    public string Longitude { get; set; }

}
public class CategoryDetails
{
    public int CategoryId { get; set; }
    public string CategoryName { get; set; }
}

现在我的搜索如下

List<AdvocateListingSuggestionModel> lsResult = new List<AdvocateListingSuggestionModel>();
        var result = _searchProvider.Client.Suggest<LegalAreaSearchModel>(s => s
                                                .Index(SearchConfigurationManager.DefaultSearchIndex)
                                                .Completion("ml-la-suggestions", c => c
                                                .Text(Search)
                                                .Field(p => p.Suggest)
                                                .Fuzzy(fz => fz
                                                    .Fuzziness(Fuzziness.Auto))
                                                )
            );

我也从其他类型获得结果(混合)我们如何限制仅来自一种类型的结果

Type1.LegalAreaSearchModel 输入 2.LegalDocumentSearchModel

我的索引创建如下

  public static bool CheckForIndex(ElasticSearchProvider searchProvider)
    {

        Nest.IndexExistsRequest idr = new Nest.IndexExistsRequest(Nest.Indices.Index(new Nest.IndexName() { Name = SearchConfigurationManager.DefaultSearchIndex }));
        var indxres = searchProvider.Client.IndexExists(idr);

        if (!indxres.Exists)
        {
            searchProvider.Client.CreateIndex(SearchConfigurationManager.DefaultSearchIndex, i => i
            .Settings(s => s
                .NumberOfShards(2)
                .NumberOfReplicas(0)
                .Analysis(analysis => analysis
                    .Tokenizers(tokenizers => tokenizers
                        .Pattern("ml-id-tokenizer", p => p.Pattern(@"\W+"))
                    )
                    .TokenFilters(tokenfilters => tokenfilters
                        .WordDelimiter("ml-id-words", wd => wd
                            .SplitOnCaseChange()
                            .PreserveOriginal()
                            .SplitOnNumerics()
                            .GenerateNumberParts(false)
                            .GenerateWordParts()
                        )
                    )
                    .Analyzers(analyzers => analyzers
                        .Custom("ml-id-analyzer", c => c
                            .Tokenizer("ml-id-tokenizer")
                            .Filters("ml-id-words", "lowercase")
                        )
                        .Custom("ml-id-keyword", c => c
                            .Tokenizer("keyword")
                            .Filters("lowercase")
                        )
                    )
                )
            ));
        }
        return true;
    }

正在创建文档类型如下

 public bool CreateDocumentIndex()
    {
        bool retVal = false;
        if (Common.CheckForIndex(_searchProvider))
        {
            var res = _searchProvider.Client.Map<LegalDocumentSearchModel>(m =>
                                                                   m.Index(SearchConfigurationManager.DefaultSearchIndex)
                                                                   .Type(this.type)
                                                                   .AutoMap()
                                                                   .Properties(ps => ps
                                                                   .String(s => s
                                                                          .Name(p => p.Id)
                                                                          .Analyzer("ml-id-analyzer")
                                                                              .Fields(f => f
                                                                                  .String(p => p.Name("keyword").Analyzer("ml-id-keyword"))
                                                                                  .String(p => p.Name("raw").Index(FieldIndexOption.NotAnalyzed))
                                                                              )
                                                                      )
                                                                   .Completion(c => c
                                                                          .Name(p => p.Suggest)
                                                                          .Payloads()
                                                                      )
                                                                   ));

            retVal = res.IsValid;


        }
        return retVal;
    }

如下创建 LegalArea 类型

 public bool CreateLegalAreaIndex()
    {
        bool retVal = false;
        if (Common.CheckForIndex(_searchProvider))
        {
            var res = _searchProvider.Client.Map<LegalAreaSearchModel>(m =>
                                                                    m.Index(SearchConfigurationManager.DefaultSearchIndex)
                                                                    .Type(this.type)
                                                                    .AutoMap()
                                                                    .Properties(ps => ps
                                                                    .String(s => s
                                                                           .Name(p => p.Id)
                                                                           .Analyzer("ml-id-analyzer")
                                                                               .Fields(f => f
                                                                                   .String(p => p.Name("keyword").Analyzer("ml-id-keyword"))
                                                                                   .String(p => p.Name("raw").Index(FieldIndexOption.NotAnalyzed))
                                                                               )
                                                                       )
                                                                    .Completion(c => c
                                                                           .Name(p => p.Suggest)
                                                                           .Payloads()
                                                                       )
                                                                    ));

            retVal = res.IsValid;
        }
        return retVal;
    }

现在当我运行合法区域建议如下

 public List<AdvocateListingSuggestionModel> LegalAreaSuggestion(string Search)
    {
        List<AdvocateListingSuggestionModel> lsResult = new List<AdvocateListingSuggestionModel>();
        var result = _searchProvider.Client.Suggest<LegalAreaSearchModel>(s => s
                                                .Index(SearchConfigurationManager.DefaultSearchIndex)
                                                .Completion("ml-la-suggestions", c => c
                                                .Text(Search)
                                                .Field(p => p.Suggest)
                                                .Fuzzy(fz => fz
                                                    .Fuzziness(Fuzziness.Auto))
                                                )
            );
        if (result.IsValid)
        {
            lsResult = result.Suggestions["ml-la-suggestions"]
                    .FirstOrDefault()
                    .Options
                    .Select(suggest => suggest.Payload<AdvocateListingSuggestionModel>()).ToList();
        }
        return lsResult;
    }

我得到 LegalDocumentSearchModel 的结果作为 well.pls 建议

谢谢

建议在索引级别工作,即 one Finite State Transducer (FST) is created for a field per index 因此,如果您在同一索引中有两个具有相同字段名称的类型(两个字段 必须 相同类型),两种类型都将在一个 FST 中表示数据。

我们可以通过一个简单的例子看出这一点。这里我们有两种带有完成字段的类型,我们将它们索引到同一个索引

public class Band
{
    public int Id { get; set;}

    public  CompletionField<object> Suggest { get; set;}
}

public class Sport
{
    public int Id { get; set;}

    public CompletionField<object> Suggest { get; set; }
}

void Main()
{
    var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
    var defaultIndex = "default-index";
    var connectionSettings = new ConnectionSettings(pool)
            .DefaultIndex(defaultIndex);

    var client = new ElasticClient(connectionSettings);

    // just so we can re-run this example...
    if (client.IndexExists(defaultIndex).Exists)
        client.DeleteIndex(defaultIndex);

    client.CreateIndex(defaultIndex, ci => ci
        .Mappings(m => m
            .Map<Band>(l => l
                .AutoMap()
                .Properties(p => p
                    .Completion(c => c
                        .Name(n => n.Suggest)
                    )
                )
            )
            .Map<Sport>(l => l
                .AutoMap()
                .Properties(p => p
                    .Completion(c => c
                        .Name(n => n.Suggest)
                    )
                )
            )
        )
    );

    var bands = new List<Band>
    {
        new Band { Id = 1, Suggest = new CompletionField<object> { Input = new [] {"Bowling for Soup"} } },
        new Band { Id = 2, Suggest = new CompletionField<object> { Input = new [] {"Fastball"} } },
        new Band { Id = 3, Suggest = new CompletionField<object> { Input = new [] {"Dropkick Murphys"} } },
        new Band { Id = 4, Suggest = new CompletionField<object> { Input = new [] {"Yellowcard"} } },
        new Band { Id = 5, Suggest = new CompletionField<object> { Input = new [] {"American Football"} } },
    };

    client.IndexMany(bands);

    var sports = new List<Sport>
    {
        new Sport { Id = 1, Suggest = new CompletionField<object> { Input = new [] {"Bowling"} } },
        new Sport { Id = 2, Suggest = new CompletionField<object> { Input = new [] {"Football"} } },
        new Sport { Id = 3, Suggest = new CompletionField<object> { Input = new [] {"Baseball"} } },
        new Sport { Id = 4, Suggest = new CompletionField<object> { Input = new [] {"Table Tennis"} } },
        new Sport { Id = 5, Suggest = new CompletionField<object> { Input = new [] {"American Football"} } },
    };

    client.IndexMany(sports);
    client.Refresh(defaultIndex);

    var suggestResponse = client.Suggest<Band>(s => s
        .Completion("suggestion", cs => cs
            .Text("Bo")
            .Field(f => f.Suggest)
        )
    );
}

我们从我们的建议调用中得到以下结果

{
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "suggestion" : [ {
    "text" : "Bo",
    "offset" : 0,
    "length" : 2,
    "options" : [ {
      "text" : "Bowling",
      "score" : 1.0
    }, {
      "text" : "Bowling for Soup",
      "score" : 1.0
    } ]
  } ]
}

我们在结果中得到了 Band 类型和 Sport 类型的建议。也许不是我们所期望或想要的,但这就是建议者的工作方式。请注意,在 .Suggest<T>() 调用中,类型 T 用于提供对可能在查询中使用的属性的强类型访问。在此示例中,类型为 Band.Suggest 字段。

这里推荐的解决方案是为不同的类型设置单独的索引;您仍然可以在需要时跨多个索引进行查询,并避免在同一索引中包含多种类型的陷阱。

除此之外,您可以通过对完成类型映射使用 Context Suggester and taking advantage of category context 来实现它。理想情况下,您希望使用类别文档中存在的字段来为您提供类别的灵活性,但如果需要,您可以使用元数据字段。我建议针对您的领域调查和研究方法。

对于我们之前的示例,如果我们现在在映射中包含类别上下文(与以前相同的代码,但具有此更新的映射

client.CreateIndex(defaultIndex, ci => ci
    .Mappings(m => m
        .Map<Band>(l => l
            .AutoMap()
            .Properties(p => p
                .Completion(c => c
                    .Name(n => n.Suggest)
                    .Context(sc => sc
                        .Category("type", csc => csc
                            // recommend that you use another field on
                            // the document here instead of a metadata field
                            .Field("_type")
                        )
                    )
                )
            )
        )
        .Map<Sport>(l => l
            .AutoMap()
            .Properties(p => p
                .Completion(c => c
                    .Name(n => n.Suggest)
                    .Context(sc => sc
                        .Category("type", csc => csc
                            // recommend that you use another field on
                            // the document here instead of a metadata field
                            .Field("_type")
                        )
                    )
                )
            )
        )
    )
);

并在搜索时使用类别上下文

var suggestResponse = client.Suggest<Band>(s => s
    .Completion("suggestion", cs => cs
        .Text("Bo")
        .Field(f => f.Suggest)
        .Context(d => d.Add("type", "band"))
    )
);

然后我们得到了想要的结果

{
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "suggestion" : [ {
    "text" : "Bo",
    "offset" : 0,
    "length" : 2,
    "options" : [ {
      "text" : "Bowling for Soup",
      "score" : 1.0
    } ]
  } ]
}