|
| 1 | +--- |
| 2 | +title: Full Text Search - Azure Cosmos DB Provider - EF Core |
| 3 | +description: Full text search with the Azure Cosmos DB EF Core Provider |
| 4 | +author: maumar |
| 5 | +ms.date: 04/19/2025 |
| 6 | +uid: core/providers/cosmos/full-text-search |
| 7 | +--- |
| 8 | +# Full text search |
| 9 | + |
| 10 | +Azure Cosmos DB now offers support for [full-text search](/azure/cosmos-db/gen-ai/full-text-search). It enables efficient and effective text searches using advanced techniques like stemming, as well as evaluating the relevance of documents to a given search query. It can be used in combination with vector search (i.e. hybrid search) to improve the accuracy of responses in some AI scenarios. |
| 11 | +EF Core allows for modeling the database with full-text search enabled properties and using full-text search functions inside queries targeting Azure Cosmos DB. |
| 12 | + |
| 13 | +## Model configuration |
| 14 | + |
| 15 | +A property can be configured inside `OnModelCreating` to use full-text search by enabling it for the property and defining a full-text index: |
| 16 | + |
| 17 | +```c# |
| 18 | +public class Blog |
| 19 | +{ |
| 20 | + ... |
| 21 | + |
| 22 | + public string Contents { get; set; } |
| 23 | +} |
| 24 | + |
| 25 | +public class BloggingContext |
| 26 | +{ |
| 27 | + ... |
| 28 | + |
| 29 | + protected override void OnModelCreating(ModelBuilder modelBuilder) |
| 30 | + { |
| 31 | + modelBuilder.Entity<Blog>(b => |
| 32 | + { |
| 33 | + b.Property(x => x.Contents).EnableFullTextSearch(); |
| 34 | + b.HasIndex(x => x.Contents).IsFullTextIndex(); |
| 35 | + }); |
| 36 | + } |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | +> [!NOTE] |
| 41 | +> Configuring the index is not mandatory, but it is recommended as it greatly improves performance of full-text search queries. |
| 42 | +
|
| 43 | +Full-text search operations are language specific, using American English (`en-US`) by default. You can customize the language for individual properties as part of `EnableFullTextSearch` call: |
| 44 | + |
| 45 | +```c# |
| 46 | + protected override void OnModelCreating(ModelBuilder modelBuilder) |
| 47 | + { |
| 48 | + modelBuilder.Entity<Blog>(b => |
| 49 | + { |
| 50 | + b.Property(x => x.Contents).EnableFullTextSearch(); |
| 51 | + b.HasIndex(x => x.Contents).IsFullTextIndex(); |
| 52 | + b.Property(x => x.ContentsGerman).EnableFullTextSearch("de-DE"); |
| 53 | + b.HasIndex(x => x.ContentsGerman).IsFullTextIndex(); |
| 54 | + }); |
| 55 | + } |
| 56 | +``` |
| 57 | + |
| 58 | +You can also set a default language for the container - unless overridden in the `EnableFullTextSearch` method, all full-text properties inside the container will use that language. |
| 59 | + |
| 60 | +```c# |
| 61 | + protected override void OnModelCreating(ModelBuilder modelBuilder) |
| 62 | + { |
| 63 | + modelBuilder.Entity<Blog>(b => |
| 64 | + { |
| 65 | + b.HasDefaultFullTextLanguage("de-DE"); |
| 66 | + b.Property(x => x.ContentsEnglish).EnableFullTextSearch("en-US"); |
| 67 | + b.HasIndex(x => x.ContentsEnglish).IsFullTextIndex(); |
| 68 | + b.Property(x => x.ContentsGerman).EnableFullTextSearch(); |
| 69 | + b.HasIndex(x => x.ContentsGerman).IsFullTextIndex(); |
| 70 | + b.Property(x => x.TagsGerman).EnableFullTextSearch(); |
| 71 | + b.HasIndex(x => x.TagsGerman).IsFullTextIndex(); |
| 72 | + }); |
| 73 | + } |
| 74 | +``` |
| 75 | + |
| 76 | +> [!NOTE] |
| 77 | +> Configuring the index is not mandatory, but it is recommended as it greatly improves performance of full-text search queries. |
| 78 | +
|
| 79 | +## Querying |
| 80 | + |
| 81 | +As part of the full-text search feature, Azure Cosmos DB introduced several built-in functions which allow for efficient querying of content inside the full-text search enabled properties. These functions are: [`FullTextContains`](/azure/cosmos-db/nosql/query/fulltextcontains), [`FullTextContainsAll`](/azure/cosmos-db/nosql/query/fulltextcontainsall), [`FullTextContainsAny`](/azure/cosmos-db/nosql/query/fulltextcontainsany), which look for specific keyword or keywords and [`FullTextScore`](/azure/cosmos-db/nosql/query/fulltextscore), which returns [BM25 score](https://en.wikipedia.org/wiki/Okapi_BM25) based on provided keywords. |
| 82 | + |
| 83 | +> [!NOTE] |
| 84 | +> `FullTextScore` can only be used inside `OrderBy` to rank the documents based on the score. |
| 85 | +
|
| 86 | +EF Core exposes these functions as part of `EF.Functions` so they can be used in queries: |
| 87 | + |
| 88 | +```c# |
| 89 | +var cosmosBlogs = await context.Blogs.Where(x => EF.Functions.FullTextContainsAll(x.Contents, "database", "cosmos")).ToListAsync(); |
| 90 | + |
| 91 | +var keywords = new string[] { "AI", "agent", "breakthrough" }; |
| 92 | +var mostInteresting = await context.Blogs.OrderBy(x => EF.Functions.FullTextScore(x.Contents, keywords)).Take(5).ToListAsync(); |
| 93 | +``` |
| 94 | + |
| 95 | +## Hybrid search |
| 96 | + |
| 97 | +Full-text search can be used with vector search in the same query (i.e. hybrid search), by combining results of `FullTextScore` and `VectorDistance` functions. It can be done using the [`RRF`](/azure/cosmos-db/nosql/query/rrf) (Reciprocal Rank Fusion) function, which EF Core also provides inside `EF.Functions`: |
| 98 | + |
| 99 | +```c# |
| 100 | +public class Blog |
| 101 | +{ |
| 102 | + ... |
| 103 | + |
| 104 | + public float[] Vector { get; set; } |
| 105 | + public string Contents { get; set; } |
| 106 | +} |
| 107 | + |
| 108 | +public class BloggingContext |
| 109 | +{ |
| 110 | + ... |
| 111 | + |
| 112 | + protected override void OnModelCreating(ModelBuilder modelBuilder) |
| 113 | + { |
| 114 | + modelBuilder.Entity<Blog>(b => |
| 115 | + { |
| 116 | + b.Property(x => x.Contents).EnableFullTextSearch(); |
| 117 | + b.HasIndex(x => x.Contents).IsFullTextIndex(); |
| 118 | + |
| 119 | + b.Property(x => x.Vector).IsVectorProperty(DistanceFunction.Cosine, dimensions: 1536); |
| 120 | + b.HasIndex(x => x.Vector).IsVectorIndex(VectorIndexType.Flat); |
| 121 | + }); |
| 122 | + } |
| 123 | +} |
| 124 | + |
| 125 | +float[] myVector = /* generate vector data from text, image, etc. */ |
| 126 | +var hybrid = await context.Blogs.OrderBy(x => EF.Functions.Rrf( |
| 127 | + EF.Functions.FullTextScore(x.Contents, "database"), |
| 128 | + EF.Functions.VectorDistance(x.Vector, myVector))) |
| 129 | + .Take(10) |
| 130 | + .ToListAsync(); |
| 131 | +``` |
| 132 | + |
| 133 | +> [!TIP] |
| 134 | +> You can combine more than two scoring functions inside `Rrf` call, as well as using only `FullTextScore`, or only `VectorDistance`. |
0 commit comments