Skip to content

Error when adding custom computed property from $compute to $select query option #3385

@habbes

Description

@habbes

Describe the bug

It appears that the library fails to parse a request URL that computes a custom property using the $compute query option and includes the computed property to the $select query option.

Consider the following request URL.

GET /Collection1
  ?$compute=graph.VectorDistance(contentVector, [1, 2, 3]) as SimilarityScore
  &$select=Id, Title, SimilarityScore    

In this case SimilarityScore is not a property defined on the entity type, but computed dynamically from the $compute expression. When I try to parse this URL, I get an error stating that it could not find the SimilarityScore property on the type. Is this a bug, feature gap, or by design?

I also removed the $select query option to see if the error would go away:

GET /Collection1
  ?$compute=graph.VectorDistance(contentVector,[1,2,3]) as SimilarityScore

But I got an error saying that the parameter to the VectorDistance function is not a single-valued parameter. So it seems like the library also does not support having a collection as the parameter of a function.

The argument for an invocation of a function with name 'graph.VectorDistance' is not a single value. All arguments for this function must be single values

If this is not supported by OData or not a bug, is there an alternative construct we can use to represent vector search queries?

Assemblies affected

Microsoft.OData.Core 8.4.0

Steps to Reproduce

Create a new console application with the following code. Make sure to also install Microsoft.OData.Core 8.4.0.

using Microsoft.OData.Edm;
using Microsoft.OData.Edm.Csdl;
using Microsoft.OData.UriParser;
using System.Text;

Console.WriteLine("Hello, World!");

var schema =
"""
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0"
    xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
  <edmx:DataServices>
    <Schema Namespace="graph" xmlns="http://docs.oasis-open.org/odata/ns/edm">

      <!-- Entity Type -->
      <EntityType Name="Item">
        <Key>
          <PropertyRef Name="Id"/>
        </Key>
        <Property Name="Id" Type="Edm.String" Nullable="false"/>
        <Property Name="Title" Type="Edm.String" Nullable="true"/>
        <Property Name="contentVector" Type="Collection(Edm.Double)" Nullable="false"/>
      </EntityType>

      <Function Name="VectorDistance" IsComposable="true">
        <Parameter Name="vector1" Type="Collection(Edm.Double)" />
        <Parameter Name="vector2" Type="Collection(Edm.Double)" />
        <ReturnType Type="Edm.Double" />
      </Function>

      <!-- Entity Container -->
      <EntityContainer Name="DefaultContainer">
        <EntitySet Name="Collection1" EntityType="graph.Item" />
      </EntityContainer>

      <!-- Custom Function -->
      

    </Schema>
  </edmx:DataServices>
</edmx:Edmx>
""";

var stream = new MemoryStream(Encoding.UTF8.GetBytes(schema));
var reader = System.Xml.XmlReader.Create(stream);
var model = CsdlReader.Parse(reader);

var serviceRoot = new Uri("https://example.com/");
var fullUri = new Uri(serviceRoot, "Collection1?$compute=graph.VectorDistance(contentVector,[1,2,3]) as SimilarityScore&$select=Id,Title,SimilarityScore");

// Parse the URI
var fn = model.FindDeclaredOperations("graph.VectorDistance").First();
CustomUriFunctions.AddCustomUriFunction("graph.VectorDistance", new FunctionSignatureWithReturnType(fn.GetReturn().Type, fn.Parameters.Select(p => p.Type).ToArray()));
var parser = new ODataUriParser(model, serviceRoot, fullUri);
var parsedUri = parser.ParseUri();

// Display parsed structure
Console.WriteLine("Parsed Path: " + parsedUri.Path.ToString());

if (parsedUri.Compute != null)
{
    Console.WriteLine("Compute expressions:");
    foreach (var expr in parsedUri.Compute.ComputedItems)
    {
        Console.WriteLine($"  {expr.Expression} AS {expr.Alias}");
    }
}

if (parsedUri.SelectAndExpand != null)
{
    Console.WriteLine("Selected properties:");
    foreach (var sel in parsedUri.SelectAndExpand.SelectedItems)
    {
        Console.WriteLine("  " + sel.ToString());
    }
}

Console.WriteLine("Parsing complete!");

Then run the code.

Expected behaviour

The query options should be correctly parsed, and the sample app above should print the projected select items.

Actual behaviour

I got an exception saying that the property SimilarityScore could not be found. This is surprising, I believe the $select query option should be able to handle properties created by $compute.

Could not find a property named 'SimilarityScore' on type 'graph.Item'.

   at Microsoft.OData.UriParser.SelectPathSegmentTokenBinder.ConvertNonTypeTokenToSegment(PathSegmentToken tokenIn, IEdmModel model, IEdmStructuredType edmType, ODataUriResolver resolver, BindingState state) in D:\a\_work\1\s\src\Microsoft.OData.Core\UriParser\Binders\SelectPathSegmentTokenBinder.cs:line 90
   at Microsoft.OData.UriParser.SelectExpandBinder.ProcessSelectTokenPath(PathSegmentToken tokenIn) in D:\a\_work\1\s\src\Microsoft.OData.Core\UriParser\Binders\SelectExpandBinder.cs:line 694
   at Microsoft.OData.UriParser.SelectExpandBinder.GenerateSelectItem(SelectTermToken tokenIn) in D:\a\_work\1\s\src\Microsoft.OData.Core\UriParser\Binders\SelectExpandBinder.cs:line 276
   at Microsoft.OData.UriParser.SelectExpandBinder.Bind(ExpandToken expandToken, SelectToken selectToken) in D:\a\_work\1\s\src\Microsoft.OData.Core\UriParser\Binders\SelectExpandBinder.cs:line 153
   at Microsoft.OData.UriParser.SelectExpandSemanticBinder.Bind(ODataPathInfo odataPathInfo, ExpandToken expandToken, SelectToken selectToken, ODataUriParserConfiguration configuration, BindingState state) in D:\a\_work\1\s\src\Microsoft.OData.Core\UriParser\Binders\SelectExpandSemanticBinder.cs:line 37
   at Microsoft.OData.UriParser.ODataQueryOptionParser.ParseSelectAndExpandImplementation(String select, String expand, ODataUriParserConfiguration configuration, ODataPathInfo odataPathInfo) in D:\a\_work\1\s\src\Microsoft.OData.Core\UriParser\ODataQueryOptionParser.cs:line 451
   at Microsoft.OData.UriParser.ODataQueryOptionParser.ParseSelectAndExpand() in D:\a\_work\1\s\src\Microsoft.OData.Core\UriParser\ODataQueryOptionParser.cs:line 236
   at Microsoft.OData.UriParser.ODataUriParser.ParseSelectAndExpand() in D:\a\_work\1\s\src\Microsoft.OData.Core\UriParser\ODataUriParser.cs:line 322
   at Microsoft.OData.UriParser.ODataUriParser.ParseUri() in D:\a\_work\1\s\src\Microsoft.OData.Core\UriParser\ODataUriParser.cs:line 485
   at Program.<Main>$(String[] args) in C:\repos\tinker\ODataAggregationExample\ODataAggregationExample\Program.cs:line 58

After removing the $select query option, I got the following exception:

The argument for an invocation of a function with name 'graph.VectorDistance' is not a single value. All arguments for this function must be single values.

at Microsoft.OData.UriParser.FunctionCallBinder.ValidateArgumentsAreSingleValue(String functionName, List`1 argumentNodes) in D:\a\_work\1\s\src\Microsoft.OData.Core\UriParser\Binders\FunctionCallBinder.cs:line 82
   at Microsoft.OData.UriParser.FunctionCallBinder.BindAsUriFunction(FunctionCallToken functionCallToken, List`1 argumentNodes) in D:\a\_work\1\s\src\Microsoft.OData.Core\UriParser\Binders\FunctionCallBinder.cs:line 306
   at Microsoft.OData.UriParser.FunctionCallBinder.BindFunctionCall(FunctionCallToken functionCallToken) in D:\a\_work\1\s\src\Microsoft.OData.Core\UriParser\Binders\FunctionCallBinder.cs:line 238
   at Microsoft.OData.UriParser.MetadataBinder.BindFunctionCall(FunctionCallToken functionCallToken) in D:\a\_work\1\s\src\Microsoft.OData.Core\UriParser\Binders\MetadataBinder.cs:line 346
   at Microsoft.OData.UriParser.MetadataBinder.Bind(QueryToken token) in D:\a\_work\1\s\src\Microsoft.OData.Core\UriParser\Binders\MetadataBinder.cs:line 186
   at Microsoft.OData.UriParser.ComputeBinder.BindComputeExpressionToken(ComputeExpressionToken token) in D:\a\_work\1\s\src\Microsoft.OData.Core\UriParser\Binders\ComputeBinder.cs:line 38
   at Microsoft.OData.UriParser.ComputeBinder.BindCompute(ComputeToken token) in D:\a\_work\1\s\src\Microsoft.OData.Core\UriParser\Binders\ComputeBinder.cs:line 29
   at Microsoft.OData.UriParser.ODataQueryOptionParser.ParseComputeImplementation(String compute, ODataUriParserConfiguration configuration, ODataPathInfo odataPathInfo) in D:\a\_work\1\s\src\Microsoft.OData.Core\UriParser\ODataQueryOptionParser.cs:line 651
   at Microsoft.OData.UriParser.ODataQueryOptionParser.ParseCompute() in D:\a\_work\1\s\src\Microsoft.OData.Core\UriParser\ODataQueryOptionParser.cs:line 367
   at Microsoft.OData.UriParser.ODataUriParser.ParseCompute() in D:\a\_work\1\s\src\Microsoft.OData.Core\UriParser\ODataUriParser.cs:line 471
   at Microsoft.OData.UriParser.ODataUriParser.ParseUri() in D:\a\_work\1\s\src\Microsoft.OData.Core\UriParser\ODataUriParser.cs:line 490
   at Program.<Main>$(String[] args) in C:\repos\ODataAggregationExample\ODataAggregationExample\Program.cs:line 58

Additional details

Optional, details of the root cause if known. Delete this section if you have no additional details to add.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions