Modify SearchDescriptor to Add field_value_factor using .NEST


(GHohenstatt) #1

I need to revise a method that builds a SearchDescriptor using .Nest so that the score is higher for
product search results for items having a contract price (value of zero).

I captured the serialized version of the query added "field_value_factor" to return the results in the desired order. I have not determined how to achieve this in the .Nest query statement.

Can someone recommend how to revise the .NEST client statements to produce the same search descriptor?

Thank you

Below is the query we want to achieve where you will see field_value_factor at the bottom:

{
	  "from": 0,
  "size": 3000,
  "sort": [
    {
      "_score": {
        "order": "desc"
      }
    },
    {
      "priceStatus": {
        "order": "asc"
      }
    },
    {
      "unitPrice": {
        "order": "asc"
      }
    }
  ],
  "aggs": {
    "PriceStatus": {
      "terms": {
        "field": "priceStatus",
        "size": 5
      }
    },
    "VendorName": {
      "terms": {
        "field": "vendorName",
        "size": 5
      }
    },
    "CatalogName": {
      "terms": {
        "field": "catalogName",
        "size": 5
      }
    },
    "ManufacturerName": {
      "terms": {
        "field": "manufacturerName",
        "size": 5
      }
    },
    "IsGreen": {
      "terms": {
        "field": "isGreen",
        "size": 5
      }
    },
    "IsValuePack": {
      "terms": {
        "field": "isValuePack",
        "size": 5
      }
    },
    "UnitOfMeasure": {
      "terms": {
        "field": "unitOfMeasure",
        "size": 5
      }
    },
    "Attributes": {
      "nested": {
        "path": "attributes"
      },
      "aggs": {
        "TheAttributeName": {
          "terms": {
            "field": "attributes.name",
            "size": 10
          },
          "aggs": {
            "TheAttributeValue": {
              "terms": {
                "field": "attributes.value",
                "size": 5
              }
            }
          }
        }
      }
    }
  },
  "query": {
    "function_score": { 
       "query": {
        "bool": {
          "should": [
            {
              "multi_match": {
                "type": "phrase",
                "query": "pen",
                "slop": 3,
                "boost": 16.0,
                "fields": [
                  "itemNumber*^4",
                  "shortDescription*^4",
                  "subCategory1Name*^1.5",
                  "subCategory2Name*^2.0",
                  "categoryName*^0.9",
                  "longDescription*^0.6",
                  "catalogName*^0.30",
                  "manufactureName*^0.20",
                  "vendorName*^0.15",
                  "upcCode*^0.10"
                ]
              }
            },
            {
              "multi_match": {
                "query": "pen",
                "boost": 15.0,
                "minimum_should_match": "75%",
                "fields": [
                  "itemNumber*^4",
                  "shortDescription*^4",
                  "subCategory1Name*^1.5",
                  "subCategory2Name*^2.0",
                  "categoryName*^0.9",
                  "longDescription*^0.6",
                  "catalogName*^0.30",
                  "manufactureName*^0.20",
                  "vendorName*^0.15",
                  "upcCode*^0.10"
                ]
              }
            },
            {
              "multi_match": {
                "query": "pen",
                "fuzziness": 1.0,
                "slop": 2,
                "minimum_should_match": "75%",
                "fields": [
                  "itemNumber*^4",
                  "shortDescription*^4",
                  "subCategory1Name*^1.5",
                  "subCategory2Name*^2.0",
                  "categoryName*^0.9",
                  "longDescription*^0.6",
                  "catalogName*^0.30",
                  "manufactureName*^0.20",
                  "vendorName*^0.15",
                  "upcCode*^0.10"
                ]
              }
            }
          ]
        }
      },
      "filter": {
        "bool": {
          "must": [
            {
              "terms": {
                "catalogId": [
                  "fbb3dd2c-f81c-4ff3-bd5b-9c2cffc51540"
                ]
              }
            }
          ]
        }
      },
            "field_value_factor": {
                "field": "priceStatus",
                "factor": -1,
                "modifier": "none"
            }
        }
    }
}

Below is the current method that builds the SearchDescriptor:


        private SearchDescriptor<SearchItem> BuildSearchDescriptor(
            string searchTerm,
            IList<Guid> catalogIds,
            int from,
            int size,
            string index,
            string preference,
            int attrSize,
            int valueSize,
            Dictionary<string, string[]> filterProps,
            Dictionary<string, string[]> filterAttrs,
            Guid? categoryId)
        {
            var searchDescriptor = new SearchDescriptor<SearchItem>()
                .From(from)
                .Size(size)
                .Query(q =>
                    q.Filtered(fd => BuildFilterTerms(fd, filterProps, filterAttrs, catalogIds, categoryId)
                        .Query(iq => BuildQueryContainer(iq, searchTerm))
                    )                    
                )
                .Index(index)
                .Preference(preference)
                .Aggregations(agg => BuildAggregationDescriptor(agg, attrSize, valueSize, catalogIds.Count))          
                .Sort(sort => sort.OnField("_score").Descending())
                .SortAscending(p=> p.PriceStatus)
                .SortAscending(p => p.UnitPrice);

            // Debug the raw string that will post to the ES servers i.e. use this in postman
            //var str = System.Text.Encoding.UTF8.GetString(client.Serializer.Serialize(searchDescriptor));

            return searchDescriptor;
        }

(Russ Cam) #2

I answered the same question on StackOverflow. Including here for posterity.

Your JSON query isn't valid; field_value_factor is a function of a function_score query. In NEST 1.x, this would look like

var response = client.Search<Document>(x => x
    .Query(q => q
        .FunctionScore(fs => fs
            .Functions(fu => fu
                .FieldValueFactor(fvf => fvf
                    .Field(f => f.PriceStatus)
                    .Factor(-1)
                    .Modifier(FieldValueFactorModifier.None)
                )
            )
        )
    )
);

public class Document
{
    public string Title { get; set; }

    public int PriceStatus { get; set; }
}

which produces the query

{
  "query": {
    "function_score": {
      "functions": [
        {
          "field_value_factor": {
            "field": "priceStatus",
            "factor": -1.0,
            "modifier": "none"
          }
        }
      ]
    }
  }
}

(system) #3

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.