From 29c8c2cde11421f81093670a1db8cf1a89db1b1d Mon Sep 17 00:00:00 2001 From: Danielle9897 Date: Mon, 11 May 2026 21:51:27 +0300 Subject: [PATCH 1/2] RDoc-3826 Searching on dynamic fields (v7.2) --- .../indexes/assets/dynamic-index-fields-1.png | Bin 26390 -> 51136 bytes .../indexes/assets/dynamic-index-fields-2.png | Bin 43570 -> 67121 bytes .../content/_using-analyzers-csharp.mdx | 2 +- .../content/_using-analyzers-nodejs.mdx | 2 +- .../content/_using-dynamic-fields-csharp.mdx | 1210 ++++++++++------ .../content/_using-dynamic-fields-java.mdx | 1191 ++++++++++----- .../content/_using-dynamic-fields-nodejs.mdx | 1113 ++++++++------ .../content/_using-dynamic-fields-php.mdx | 1276 +++++++++++------ .../content/_using-dynamic-fields-python.mdx | 1180 ++++++++++----- .../querying/content/_searching-csharp.mdx | 16 +- .../querying/content/_searching-java.mdx | 7 + .../querying/content/_searching-nodejs.mdx | 19 +- .../querying/content/_searching-php.mdx | 20 +- .../querying/content/_searching-python.mdx | 19 +- docs/indexes/using-dynamic-fields.mdx | 5 +- .../configuration/indexing-configuration.mdx | 28 + 16 files changed, 3999 insertions(+), 2089 deletions(-) diff --git a/docs/indexes/assets/dynamic-index-fields-1.png b/docs/indexes/assets/dynamic-index-fields-1.png index b13d9076f89e3363e97e2a4a6c01aecc4d101b67..a20adda0ece521b8d27172708c0a11aeea13f887 100644 GIT binary patch literal 51136 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 literal 26390 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 diff --git a/docs/indexes/assets/dynamic-index-fields-2.png b/docs/indexes/assets/dynamic-index-fields-2.png index dd2b4082eaa04bd406f9fac609adccde69e21b42..c5b1bc14bca3b86dd281347ae4c56e6545ff1bba 100644 GIT binary patch literal 67121 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%0Dmpj<&g2>Q& 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 X>}(5)fwv}4d^z}m?)_4g$D#iV5#_1q literal 43570 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

MYQ6EJKjSRtlshL!QySY5<Da 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 -* In RavenDB different documents can have different shapes. - Documents are schemaless - new fields can be added or removed as needed. - -* For such dynamic data, you can define indexes with **dynamic-index-fields**. - -* This allows querying the index on fields that aren't yet known at index creation time, - which is very useful when working on highly dynamic systems. - -* Any value type can be indexed, string, number, date, etc. - -* An index definition can contain both dynamic-index-fields and regular-index-fields. - -* In this page: - - * [Indexing documents fields KEYS](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-keys) - * [Example - index any field under object](../../indexes/using-dynamic-fields.mdx#example---index-any-field-under-object) - * [Example - index any field](../../indexes/using-dynamic-fields.mdx#example---index-any-field) - * [Indexing documents fields VALUES](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-values) - * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) - * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) - * [CreateField syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) - * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields--terms-view) +* In RavenDB, different documents can have different shapes. + Documents are schemaless - new fields can be added or removed as needed. + For such dynamic data, you can define indexes with **dynamic-index-fields**. + +* Dynamic-index-fields are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time, + which is very useful when working with highly dynamic systems. + +* Any value type can be indexed, string, number, date, etc. + An index definition can contain both dynamic-index-fields and regular-index-fields. + +* In this article: + * [Indexing field KEYS](../../indexes/using-dynamic-fields.mdx#indexing-field-keys) + * [Example - index every field under an object](../../indexes/using-dynamic-fields.mdx#example---index-every-field-under-an-object) + * [Example - index every field](../../indexes/using-dynamic-fields.mdx#example---index-every-field) + * [Indexing field VALUES](../../indexes/using-dynamic-fields.mdx#indexing-field-values) + * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) + * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) + * [Indexing dynamic fields for full-text search](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + * [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) + * [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + * [`CreateField` syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) + * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields-terms-view) -## Indexing documents fields KEYS - -## Example - index any field under object - - + -* Index any field that is under the some object from the document. -* After index is deployed, any new field added to the this object will be indexed as well. + + +### Example - index every field under an object + +The following example allows you to: + * Index any field that is under the same object from the document. + * After index is deployed, any new field added to this object will be indexed as well. * **The document**: - - -{`public class Product -\{ - public string Id \{ get; set; \} - - // The KEYS under the Attributes object will be dynamically indexed - // Fields added to this object after index creation time will also get indexed - public Dictionary Attributes \{ get; set; \} -\} -`} - - - - -{`// Sample document content -\{ - "Attributes": \{ - "Color": "Red", - "Size": 42 - \} -\} -`} - - + + + ```csharp + public class Product + { + public string Id { get; set; } + + // The KEYS under the Attributes object will be dynamically indexed + // Fields added to this object after index creation time will also get indexed + public Dictionary Attributes { get; set; } + } + ``` + + + + ```json + // Sample document content + { + "Attributes": { + "Color": "Red", + "Size": 42 + } + } + ``` + * **The index**: - The below index will index any field under the `Attributes` object from the document, + + The following index will index any field under the `Attributes` object from the document, a dynamic-index-field will be created for each such field. New fields added to the object after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the attribute field **key**. - E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. + The actual dynamic-index-field name on which you can query will be the attribute field **key**. + E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. - - - -{`public class Products_ByAttributeKey : AbstractIndexCreationTask -{ - public Products_ByAttributeKey() + + + ```csharp + public class Products_ByAttributeKey : AbstractIndexCreationTask { - Map = products => from p in products - select new - { - // Call 'CreateField' to generate dynamic-index-fields from the Attributes object keys - // Using '_' is just a convention. Any other string can be used instead of '_' - - // The actual field name will be item.Key - // The actual field terms will be derived from item.Value - _ = p.Attributes.Select(item => CreateField(item.Key, item.Value)) - }; + public Products_ByAttributeKey() + { + Map = products => from p in products + select new + { + // Call 'CreateField' to generate dynamic-index-fields + // from the Attributes object keys. + + // Using '_' is just a convention. + // Any other string can be used instead of '_' + + // The actual field name will be 'item.Key' + // The actual field terms will be derived from 'item.Value' + _ = p.Attributes.Select(item => CreateField(item.Key, item.Value)) + }; + } } -} -`} - - - - -{`public class Products_ByAttributeKey_JS : AbstractJavaScriptIndexCreationTask -{ - public Products_ByAttributeKey_JS() + ``` + + + ```csharp + public class Products_ByAttributeKey_JS : AbstractJavaScriptIndexCreationTask { - Maps = new HashSet + public Products_ByAttributeKey_JS() { - @"map('Products', function (p) { - return { - _: Object.keys(p.Attributes).map(key => createField(key, p.Attributes[key], - { indexing: 'Search', storage: true, termVector: null })) - }; - })" - }; + Maps = new HashSet + { + @"map('Products', function (p) { + return { + // The field name will be the key + // The field terms will be derived from the corresponding value + _: Object.keys(p.Attributes) + .map(key => createField(key, p.Attributes[key])) + }; + })" + }; + } } -} -`} - - - - + ``` + + * **The query**: - * You can now query the generated dynamic-index fields. - The `_` property is Not queryable but used only in the index definition syntax. - * To get all documents with some 'Size' use: - - - -{`IList matchingDocuments = session - .Advanced - .DocumentQuery() - // 'Size' is a dynamic-index-field that was indexed from the Attributes object - .WhereEquals("Size", 42) - .ToList(); -`} - - - - -{`// 'Size' is a dynamic-index-field that was indexed from the Attributes object -from index 'Products/ByAttributeKey' where Size = 42 -`} - - - + + You can now query the generated dynamic-index fields. + The `_` property is Not queryable but used only in the index definition syntax. + + To get all documents with a specific _Size_ use: + + + + ```csharp + IList matchingDocuments = session + .Advanced + .DocumentQuery() + // 'Size' is a dynamic-index-field that was indexed from the Attributes object + .WhereEquals("Size", 42) + .ToList(); + ``` + + + ```sql + // 'Size' is a dynamic-index-field that was indexed from the Attributes object + from index 'Products/ByAttributeKey' where Size = 42 + ``` + + + + + Dynamic index fields are queried with _DocumentQuery_ or _RQL_ because the field names (e.g. `Size`, `Color`) + are generated at indexing time and are not properties on the _Product_ class. + + A strongly typed LINQ query such as `Query().Where(p => p.Size == 42)` would not compile. + Use the string-based `WhereEquals("Size", 42)` or RQL `where Size = 42` instead. + -## Example - index any field + - + + +### Example - index every field + +The following example allows you to: * Define an index on a collection **without** needing any common structure between the indexed documents. - * After index is deployed, any new field added to the document will be indexed as well. - + * After the index is deployed, any new field added to any document in the collection will be indexed as well. - + -Consider whether this is really necessary, as indexing every single field can end up costing time and disk space. +* Use this approach with care. +* Indexing every field can increase indexing time, memory usage, and disk space, + especially for large or frequently updated documents. * **The document**: - - -{`public class Product -\{ - public string Id \{ get; set; \} - - // All KEYS in the document will be dynamically indexed - // Fields added to the document after index creation time will also get indexed - public string FirstName \{ get; set; \} - public string LastName \{ get; set; \} - public string Title \{ get; set; \} - // ... -\} -`} - - - - - -{`// Sample document content - \{ - "FirstName": "John", - "LastName": "Doe", - "Title": "Engineer", + + + ```csharp + public class Product + { + public string Id { get; set; } + + // All KEYS in the document will be dynamically indexed + // Fields added to the document after index creation time will also get indexed + public string FirstName { get; set; } + public string LastName { get; set; } + public string Title { get; set; } // ... -\} -`} - - + } + ``` + + + + ```json + // Sample document content + { + "FirstName": "John", + "LastName": "Doe", + "Title": "Engineer", + // ... + } + ``` + * **The index**: - The below index will index any field from the document, + + The following index will index any field from the document, a dynamic-index-field will be created for each field. New fields added to the document after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the field **key**. - E.g., Keys `FirstName` & `LastName` will become the actual dynamic-index-fields. + The actual dynamic-index-field name on which you can query will be the field **key**. + E.g., Keys `FirstName` & `LastName` will become the actual dynamic-index-fields. + + The example uses a JavaScript index because it enumerates all top-level document fields at indexing time with `Object.keys(...)`. + LINQ indexes typically create dynamic fields from a known enumerable structure, such as a dictionary or collection. - - - -{`public class Products_ByAnyField_JS : AbstractJavaScriptIndexCreationTask -{ - public Products_ByAnyField_JS() + + + ```csharp + public class Products_ByAnyField_JS : AbstractJavaScriptIndexCreationTask { - // This will index EVERY FIELD under the top level of the document - Maps = new HashSet + public Products_ByAnyField_JS() { - @"map('Products', function (p) { - return { - _: Object.keys(p).map(key => createField(key, p[key], - { indexing: 'Search', storage: true, termVector: null })) - } - })" - }; + // This will index EVERY FIELD under the top level of the document + Maps = new HashSet + { + @"map('Products', function (p) { + return { + _: Object.keys(p).map(key => createField(key, p[key])) + }; + })" + }; + } } -} -`} - - - + ``` + + * **The query**: - * To get all documents with some 'LastName' use: - - - -{`IList matchingDocuments = session - .Advanced - .DocumentQuery() - // 'LastName' is a dynamic-index-field that was indexed from the document - .WhereEquals("LastName", "Doe") - .ToList(); -`} - - - - -{`// 'LastName' is a dynamic-index-field that was indexed from the document -from index 'Products/ByAnyField/JS' where LastName = "Doe" -`} - - - + + To get all documents with a specific _LastName_ use: + + + + ```csharp + IList matchingDocuments = session + .Advanced + .DocumentQuery() + // 'LastName' is a dynamic-index-field that was indexed from the document + .WhereEquals("LastName", "Doe") + .ToList(); + ``` + + + ```sql + // 'LastName' is a dynamic-index-field that was indexed from the document + from index 'Products/ByAnyField/JS' where LastName = "Doe" + ``` + + + + + + + + + +### Example - basic -## Indexing documents fields VALUES + +The following example shows: + * Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. + * Documents can then be queried based on those indexed values. + * For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---list) below. + -## Example - basic +* **The document**: + + + ```csharp + public class Product + { + public string Id { get; set; } + + // The VALUE of ProductType will be dynamically indexed + public string ProductType { get; set; } + public int PricePerUnit { get; set; } + } + ``` + + + + ```json + // Sample document content + { + "ProductType": "Electronics", + "PricePerUnit": 23 + } + ``` + - +* **The index**: + + The following index will index the **value** of document field 'ProductType'. + + This value will be the dynamic-index-field name on which you can query. + E.g., Field value `Electronics` will be the dynamic-index-field. + + + + ```csharp + public class Products_ByProductType : AbstractIndexCreationTask + { + public Products_ByProductType() + { + Map = products => from p in products + select new + { + // Call 'CreateField' to generate the dynamic-index-fields + // The field name will be the value of document field 'ProductType' + // The field terms will be derived from document field 'PricePerUnit' + _ = CreateField(p.ProductType, p.PricePerUnit) + }; + } + } + ``` + + + ```csharp + public class Products_ByProductType_JS : AbstractJavaScriptIndexCreationTask + { + public Products_ByProductType_JS() + { + Maps = new HashSet + { + @"map('Products', function (p) { + return { + // The field name will be the value of document field 'ProductType' + // The field terms will be derived from document field 'PricePerUnit' + _: createField(p.ProductType, p.PricePerUnit) + }; + })" + }; + } + } + ``` + + + +* **The query**: + + To get all documents of some product type having a specific price per unit use: + + + + ```csharp + IList matchingDocuments = session + .Advanced + .DocumentQuery() + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'ProductType' + .WhereEquals("Electronics", 23) + .ToList(); + ``` + + + ```sql + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'ProductType' + from index 'Products/ByProductType' where Electronics = 23 + ``` + + + + + + -* Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. -* Documents can then be queried based on those indexed values. -* For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---index-a-list-of-properties) below. +### Example - list + +The following example allows you to: + * Index **values** from items in a list + * After index is deployed, any item added to this list in the document will be dynamically indexed as well. * **The document**: - - -{`public class Product -\{ - public string Id \{ get; set; \} - - // The VALUE of ProductType will be dynamically indexed - public string ProductType \{ get; set; \} - public int PricePerUnit \{ get; set; \} -\} -`} - - - - - -{`// Sample document content -\{ - "ProductType": "Electronics", - "PricePerUnit": 23 -\} -`} - - + + + ```csharp + public class Product + { + public string Id { get; set; } + public string Name { get; set; } + + // For each element in this list, + // the VALUE of property 'PropName' will be dynamically indexed. + // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields. + public List Attributes { get; set; } + } + + public class Attribute + { + public string PropName { get; set; } + public string PropValue { get; set; } + } + ``` + + + + ```json + // Sample document content + { + "Name": "SomeName", + "Attributes": [ + { + "PropName": "Color", + "PropValue": "Blue" + }, + { + "PropName": "Width", + "PropValue": "10" + }, + { + "PropName": "Length", + "PropValue": "20" + }, + ... + ] + } + ``` + * **The index**: - The below index will index the **value** of document field 'ProductType'. + + The following index will create a dynamic-index-field per item in the document's `Attributes` list. + New items added to the Attributes list after index creation time will be dynamically indexed as well. - This value will be the dynamic-index-field name on which you can query. - E.g., Field value `Electronics` will be the dynamic-index-field. + The actual dynamic-index-field name on which you can query will be the item's PropName **value**. + E.g., 'PropName' value `Width` will be a dynamic-index-field. + + + + ```csharp + public class Attributes_ByName : AbstractIndexCreationTask + { + public Attributes_ByName() + { + Map = products => from a in products + select new + { + // Define the dynamic-index-fields by calling 'CreateField' + // A dynamic-index-field will be generated for each item in 'Attributes' + + // For each item, the field name will be the value of field 'PropName' + // The field terms will be derived from field 'PropValue' + _ = a.Attributes.Select(item => + CreateField(item.PropName, item.PropValue)), + + // A regular index field can be defined as well: + Name = a.Name + }; + } + } + ``` + + + ```csharp + public class Attributes_ByName_JS : AbstractJavaScriptIndexCreationTask + { + public Attributes_ByName_JS() + { + Maps = new HashSet + { + @"map('Products', function (p) { + return { + // For each item, + // the field name will be the value of field 'PropName' + // The field terms will be derived from field 'PropValue' + _: p.Attributes.map(item => createField( + item.PropName, item.PropValue)), + + // A regular index field can be defined as well: + Name: p.Name + }; + })" + }; + } + } + ``` + + + +* **The query**: + + To get all documents matching a specific attribute property use: + + + + ```csharp + IList matchingDocuments = session + .Advanced + .DocumentQuery() + // 'Width' is a dynamic-index-field that was indexed from the Attributes list + .WhereEquals("Width", 10) + .ToList(); + ``` + + + ```sql + // 'Width' is a dynamic-index-field that was indexed from the Attributes list + from index 'Attributes/ByName' where Width = 10 + ``` + + + + + + + + + + + +Dynamic fields can be indexed for [Full-text search](../../indexes/querying/searching.mdx) by setting `FieldIndexing.Search` in `CreateFieldOptions`. + +* **At indexing time**: + At indexing time, RavenDB analyzes the dynamic field value and creates the indexed terms. + The analyzer used at indexing time is resolved from the index definition. + You can configure an analyzer for a specific generated field name, or configure `AllFields` as a fallback. + See [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) below. + + * If the generated dynamic field name has an explicit analyzer configuration, that analyzer is used. + * Otherwise, if `AllFields` has an analyzer configuration, RavenDB uses it as a fallback. + * If no analyzer is configured for the field, RavenDB uses the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + +* **At querying time**: + For full-text search to return the expected results, the analyzer used at indexing time and the analyzer used for the query term should produce compatible terms. + + When a `search()` query targets a dynamic field that does **not** have an explicit analyzer configuration for its generated field name, + RavenDB uses the [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) to analyze the query term by default. + + Set the [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) + configuration key to `true` to make RavenDB use the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) as the fallback analyzer for the query term instead. + + This configuration can be set at different scopes: server-wide, per database, or per index. + The example below sets it at the index scope, in the index definition. + + For a summary of how RavenDB resolves the analyzer used for the query term, + see [Which analyzer is used for the query term](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) below. + + + +--- + +The following example indexes localized product descriptions as dynamic fields and queries one of those generated fields using full-text search: + +* **The document**: + + + ```csharp + public class Product + { + public string Id { get; set; } + + public Dictionary Descriptions { get; set; } + } + ``` + + + + ```json + // Sample document content + { + "Descriptions": { + "English": "North wind jacket", + "French": "Veste vent du nord" + } + } + ``` + + +* **The index**: + + The following index creates one dynamic field for each entry in the `Descriptions` dictionary. + For example, the key `English` creates the dynamic field `Description_English`. + + Each generated dynamic field is indexed for **full-text search**. + + + + ```csharp + public class Products_ByLocalizedDescription : AbstractIndexCreationTask + { + private const string UseSearchAnalyzerForDynamicFields = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public Products_ByLocalizedDescription() + { + Map = products => from product in products + select new + { + _ = product.Descriptions.Select(x => + CreateField( + "Description_" + x.Key, + x.Value, + new CreateFieldOptions + { + // Index each generated dynamic field for FTS + Indexing = FieldIndexing.Search, + Storage = FieldStorage.No + })) + }; + + StoreAllFields(FieldStorage.No); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + Configuration[UseSearchAnalyzerForDynamicFields] = "true"; + } + } + ``` + + + ```csharp + public class Products_ByLocalizedDescription_JS : AbstractJavaScriptIndexCreationTask + { + private const string UseSearchAnalyzerForDynamicFields = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public Products_ByLocalizedDescription_JS() + { + Maps = new HashSet + { + @"map('Products', function (product) { + return { + _: Object.keys(product.Descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + // Index each generated dynamic field for FTS + indexing: 'Search', + storage: 'No' + }); + }) + }; + })" + }; + + Fields[Constants.Documents.Indexing.Fields.AllFields] = new IndexFieldOptions + { + Storage = FieldStorage.No + }; + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + Configuration[UseSearchAnalyzerForDynamicFields] = "true"; + } + } + ``` + + + +* **Full-text search query**: + + Query the generated dynamic field by its field name. + + In this example, the query targets the generated field `Description_English`. + + Since `Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery` is set to `true`, + the searched term `"north wind"` is analyzed with the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + + + + ```csharp + List results = session.Advanced + .DocumentQuery() + .Search("Description_English", "north wind") + .ToList(); + ``` + + + + ```sql + from index "Products/ByLocalizedDescription" + where search(Description_English, "north wind") + ``` + + + +--- + + + +### Configuring analyzers for dynamic fields - - - -{`public class Products_ByProductType : AbstractIndexCreationTask +`CreateFieldOptions` does **not** let you specify a custom analyzer directly. +However, you can still configure which analyzer RavenDB will use for dynamic fields in the index definition by either: + +* Configuring an analyzer for a specific generated dynamic field name. +* Configuring `AllFields` as a fallback for fields that do not have their own explicit analyzer configuration. + +Use `AllFields` with care, since it can affect any field that does not have its own explicit analyzer configuration. + +--- + +#### Configure analyzer for a specific dynamic field + + + +```csharp +public class Products_ByLocalizedDescription : AbstractIndexCreationTask { - public Products_ByProductType() + public Products_ByLocalizedDescription() { - Map = products => from p in products + Map = products => from product in products select new { - // Call 'CreateField' to generate the dynamic-index-fields - // The field name will be the value of document field 'ProductType' - // The field terms will be derived from document field 'PricePerUnit' - _ = CreateField(p.ProductType, p.PricePerUnit) + _ = product.Descriptions.Select(x => + CreateField( + "Description_" + x.Key, + x.Value, + new CreateFieldOptions + { + Indexing = FieldIndexing.Search, + Storage = FieldStorage.No + })) }; + + StoreAllFields(FieldStorage.No); + + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + Analyze("Description_English", "StopAnalyzer"); } } -`} - +``` - - -{`public class Products_ByProductType_JS : AbstractJavaScriptIndexCreationTask + +```csharp +public class Products_ByLocalizedDescription_JS : AbstractJavaScriptIndexCreationTask { - public Products_ByProductType_JS() + public Products_ByLocalizedDescription_JS() { Maps = new HashSet { - @"map('Products', function (p) { + @"map('Products', function (product) { return { - _: createField(p.ProductType, p.PricePerUnit, - { indexing: 'Search', storage: true, termVector: null }) + _: Object.keys(product.Descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; })" }; + + Fields[Constants.Documents.Indexing.Fields.AllFields] = new IndexFieldOptions + { + Storage = FieldStorage.No + }; + + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + Fields["Description_English"] = new IndexFieldOptions + { + Analyzer = "StopAnalyzer" + }; } } -`} - - - - -* **The query**: - * To get all documents of some product type having a specific price per unit use: - - - -{`IList matchingDocuments = session - .Advanced - .DocumentQuery() - // 'Electronics' is the dynamic-index-field that was indexed from document field 'ProductType' - .WhereEquals("Electronics", 23) - .ToList(); -`} - - - - -{`// 'Electronics' is the dynamic-index-field that was indexed from document field 'ProductType' -from index 'Products/ByProductType' where Electronics = 23 -`} - +``` -## Example - list - - +#### Configure a fallback analyzer for all fields -* Index **values** from items in a list -* After index is deployed, any item added this list in the document will be dynamically indexed as well. - - - -* **The document**: - - -{`public class Product -\{ - public string Id \{ get; set; \} - public string Name \{ get; set; \} - - // For each element in this list, the VALUE of property 'PropName' will be dynamically indexed - // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields - public List Attributes \{ get; set; \} -\} - -public class Attribute -\{ - public string PropName \{ get; set; \} - public string PropValue \{ get; set; \} -\} -`} - - - - - -{`// Sample document content -\{ -Name": "SomeName", -Attributes": [ - \{ - "PropName": "Color", - "PropValue": "Blue" - \}, - \{ - "PropName": "Width", - "PropValue": "10" - \}, - \{ - "PropName": "Length", - "PropValue": "20" - \}, - ... - -\} -`} - - - -* **The index**: - The below index will create a dynamic-index-field per item in the document's `Attributes` list. - New items added to the Attributes list after index creation time will be dynamically indexed as well. - - The actual dynamic-index-field name on which you can query will be the item's PropName **value**. - E.g., 'PropName' value `Width` will be a dynamic-index-field. - - - - -{`public class Attributes_ByName : AbstractIndexCreationTask + + +```csharp +public class Products_ByLocalizedDescription : AbstractIndexCreationTask { - public Attributes_ByName() + public Products_ByLocalizedDescription() { - Map = products => from a in products + Map = products => from product in products select new { - // Define the dynamic-index-fields by calling CreateField - // A dynamic-index-field will be generated for each item in the Attributes list - - // For each item, the field name will be the value of field 'PropName' - // The field terms will be derived from field 'PropValue' - _ = a.Attributes.Select(item => CreateField(item.PropName, item.PropValue)), - - // A regular index field can be defined as well: - Name = a.Name + _ = product.Descriptions.Select(x => + CreateField( + "Description_" + x.Key, + x.Value, + new CreateFieldOptions + { + Indexing = FieldIndexing.Search, + Storage = FieldStorage.No + })) }; + + StoreAllFields(FieldStorage.No); + + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + Analyze(Constants.Documents.Indexing.Fields.AllFields, "StopAnalyzer"); } } -`} - +``` - - -{`public class Attributes_ByName_JS : AbstractJavaScriptIndexCreationTask + +```csharp +public class Products_ByLocalizedDescription_JS : AbstractJavaScriptIndexCreationTask { - public Attributes_ByName_JS() + public Products_ByLocalizedDescription_JS() { Maps = new HashSet { - @"map('Products', function (p) { + @"map('Products', function (product) { return { - _: p.Attributes.map(item => createField(item.PropName, item.PropValue, - { indexing: 'Search', storage: true, termVector: null })), - Name: p.Name + _: Object.keys(product.Descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; })" }; + + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + Fields[Constants.Documents.Indexing.Fields.AllFields] = new IndexFieldOptions + { + Storage = FieldStorage.No, + Analyzer = "StopAnalyzer" + }; } } -`} - - - - -* **The query**: - * To get all documents matching a specific attribute property use: - - - -{`IList matchingDocuments = session - .Advanced - .DocumentQuery() - // 'Width' is a dynamic-index-field that was indexed from the Attributes list - .WhereEquals("Width", 10) - .ToList(); -`} - - - - -{`// 'Width' is a dynamic-index-field that was indexed from the Attributes list -from index 'Attributes/ByName' where Width = 10 -`} - +``` + + + + +### Which analyzer is used for the query term? + +The following applies when a `search()` query targets a **dynamic field**: + +| Index configuration | [Configuration key](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) | Analyzer used for the query term | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| No analyzer is explicitly configured for the generated field name | `false` | The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) | +| No analyzer is explicitly configured for the generated field name | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| `AllFields` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `false` | The analyzer resolved by the normal field fallback path,
e.g. `WhitespaceAnalyzer` | +| `AllFields` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| The generated field name is explicitly configured,
e.g. `Analyze("Description_English", "StopAnalyzer")` | `false` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +| The generated field name is explicitly configured,
e.g. `Analyze("Description_English", "StopAnalyzer")` | `true` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +
-## CreateField syntax +
+ + #### Syntax for LINQ-index: - - -{`object CreateField(string name, object value); + +```csharp +object CreateField(string name, object value); object CreateField(string name, object value, bool stored, bool analyzed); object CreateField(string name, object value, CreateFieldOptions options); -`} - +``` #### Syntax for JavaScript-index: - - -{`createField(fieldName, fieldValue, options); // returns object -`} - + +```js +createField(fieldName, fieldValue, options); // returns object +``` | Parameters | Type | Description | |----------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| | **fieldName** | `string` | Name of the dynamic-index-field | -| **fieldValue** | `object` | Value of the dynamic-index-field
The field Terms are derived from this value. | -| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorate.Yes` | +| **fieldValue** | `object` | Value of the dynamic-index-field
The field terms are derived from this value. | +| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorage.Yes` | | **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing.Default` (default value)
`false` - `FieldIndexing.Exact`
`true` - `FieldIndexing.Search` | | **options** | `CreateFieldOptions` | Dynamic-index-field options | -| CreateFieldOptions | | | -|--------------------|--------------------|----------------------------------------------------------------------------| +| CreateFieldOptions | | | +|--------------------|--------------------|-----------------------------------------------------------------------------------| | **Storage** | `FieldStorage?` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | -| **Indexing** | `FieldIndexing?` | Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | -| **TermVector** | `FieldTermVector?` | Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | +| **Indexing** | `FieldIndexing?` | Sets the field indexing behavior.
Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | +| **TermVector** | `FieldTermVector?` | Sets the field term-vector behavior.
Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | @@ -540,11 +930,15 @@ object CreateField(string name, object value, CreateFieldOptions options); -## Indexed fields & terms view - -The generated dynamic-index-fields and their indexed terms can be viewed in the **Terms View**. -Below are sample index fields & their terms generated from the last example. +
+ + +The generated dynamic index fields and their indexed terms can be viewed in the **Terms View** in Studio. +Below are sample index fields & their terms generated from [this example](../../indexes/using-dynamic-fields.mdx#example---list). + ![Figure 1. Go to terms view](../assets/dynamic-index-fields-1.png) - + ![Figure 2. Indexed fields & terms](../assets/dynamic-index-fields-2.png) + + \ No newline at end of file diff --git a/docs/indexes/content/_using-dynamic-fields-java.mdx b/docs/indexes/content/_using-dynamic-fields-java.mdx index 17f3cbe360..d54ef6bea8 100644 --- a/docs/indexes/content/_using-dynamic-fields-java.mdx +++ b/docs/indexes/content/_using-dynamic-fields-java.mdx @@ -2,479 +2,918 @@ import Admonition from '@theme/Admonition'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; +import ContentFrame from '@site/src/components/ContentFrame'; +import Panel from '@site/src/components/Panel'; -* In RavenDB different documents can have different shapes. - Documents are schemaless - new fields can be added or removed as needed. +* In RavenDB, different documents can have different shapes. + Documents are schemaless - new fields can be added or removed as needed. + For such dynamic data, you can define indexes with **dynamic-index-fields**. + +* Dynamic-index-fields are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time, + which is very useful when working with highly dynamic systems. + +* Any value type can be indexed, string, number, date, etc. + An index definition can contain both dynamic-index-fields and regular-index-fields. + +* In this article: + * [Indexing field KEYS](../../indexes/using-dynamic-fields.mdx#indexing-field-keys) + * [Example - index every field under an object](../../indexes/using-dynamic-fields.mdx#example---index-every-field-under-an-object) + * [Example - index every field](../../indexes/using-dynamic-fields.mdx#example---index-every-field) + * [Indexing field VALUES](../../indexes/using-dynamic-fields.mdx#indexing-field-values) + * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) + * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) + * [Indexing dynamic fields for full-text search](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + * [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) + * [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + * [`CreateField` syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) + * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields-terms-view) -* For such dynamic data, you can define indexes with **dynamic-index-fields**. + -* This allows querying the index on fields that aren't yet known at index creation time, - which is very useful when working on highly dynamic systems. + -* Any value type can be indexed, string, number, date, etc. + + +### Example - index every field under an object -* An index definition can contain both dynamic-index-fields and regular-index-fields. + +The following example allows you to: + * Index any field that is under the same object from the document. + * After index is deployed, any new field added to this object will be indexed as well. + -* In this page: +* **The document**: + + + ```java + public class Product { + private String id; - * [Indexing documents fields KEYS](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-keys) - * [Example - index any field under object](../../indexes/using-dynamic-fields.mdx#example---index-any-field-under-object) - * [Example - index any field](../../indexes/using-dynamic-fields.mdx#example---index-any-field) - * [Indexing documents fields VALUES](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-values) - * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) - * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) - * [CreateField syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) - * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields--terms-view) + // The KEYS under the attributes object will be dynamically indexed + // Fields added to this object after index creation time will also get indexed + private Map attributes; -
+ // getters and setters ... + } + ``` + + + + ```json + // Sample document content + { + "attributes": { + "color": "Red", + "size": 42 + } + } + ``` + -## Indexing documents fields KEYS +* **The index**: + + The following index will index any field under the `attributes` object from the document, + a dynamic-index-field will be created for each such field. + New fields added to the object after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the attribute field **key**. + E.g., Keys `color` & `size` will become the actual dynamic-index-fields. + + + + ```java + public class Products_ByAttributeKey extends AbstractIndexCreationTask { + public Products_ByAttributeKey() { + + // Call 'CreateField' to generate dynamic-index-fields + // from the attributes object keys. + + // Using '_' is just a convention. + // Any other string can be used instead of '_'. + + // The actual field name will be 'item.Key' + // The actual field terms will be derived from 'item.Value' + map = "docs.Products.Select(p => new { " + + " _ = p.attributes.Select(item => " + + " this.CreateField(item.Key, item.Value)) " + + "})"; + } + } + ``` + + + ```java + public class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask { + public Products_ByAttributeKey_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (p) { " + + " return { " + + // The field name will be the key + // The field terms will be derived from the corresponding value + " _: Object.keys(p.attributes) " + + " .map(key => createField(key, p.attributes[key])) " + + " }; " + + "})" + )); + } + } + ``` + + -## Example - index any field under object +* **The query**: + + You can now query the generated dynamic-index fields. + The `_` property is Not queryable but used only in the index definition syntax. + + To get all documents with a specific _size_ use: + + + + ```java + List matchingDocuments = session + .advanced() + .documentQuery(Product.class, Products_ByAttributeKey.class) + // 'size' is a dynamic-index-field that was indexed from the attributes object + .whereEquals("size", 42) + .toList(); + ``` + + + ```sql + // 'size' is a dynamic-index-field that was indexed from the attributes object + from index 'Products/ByAttributeKey' where size = 42 + ``` + + + + + Dynamic index fields are queried with _DocumentQuery_ or _RQL_ because the field names (e.g. `size`, `color`) + are generated at indexing time and are not properties on the _Product_ class. + + Use the string-based `whereEquals("size", 42)` or RQL `where size = 42` instead. + - + -* Index any field that is under the some object from the document. -* After index is deployed, any new field added to the this object will be indexed as well. + + +### Example - index every field + +The following example allows you to: + * Define an index on a collection **without** needing any common structure between the indexed documents. + * After the index is deployed, any new field added to any document in the collection will be indexed as well. + + + +* Use this approach with care. +* Indexing every field can increase indexing time, memory usage, and disk space, + especially for large or frequently updated documents. * **The document**: - - -{`public class Product \{ - private String id; - - // The KEYS under the attributes object will be dynamically indexed - // Fields added to this object after index creation time will also get indexed - private Dictionary attributes; - - // get + set implementation ... -\} -`} - - - - -{`// Sample document content -\{ - "attributes": \{ - "color": "Red", - "size": 42 - \} -\} -`} - - + + + ```java + public class Product { + private String id; + + // All KEYS in the document will be dynamically indexed + // Fields added to the document after index creation time will also get indexed + private String firstName; + private String lastName; + private String title; + // ... + + // getters and setters ... + } + ``` + + + + ```json + // Sample document content + { + "firstName": "John", + "lastName": "Doe", + "title": "Engineer" + // ... + } + ``` + * **The index**: - The following index will index any field under the `attributes` object from the document, - a dynamic-index-field will be created for each such field. - New fields added to the object after index creation time will be dynamically indexed as well. + + The following index will index any field from the document, + a dynamic-index-field will be created for each field. + New fields added to the document after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the field **key**. + E.g., Keys `firstName` & `lastName` will become the actual dynamic-index-fields. + + The example uses an `AbstractJavaScriptIndexCreationTask` because it enumerates all top-level document fields at indexing time with `Object.keys(...)`. + LINQ indexes typically create dynamic fields from a known enumerable structure, such as a map or collection. + + + + ```java + public class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask { + public Products_ByAnyField_JS() { + + // This will index EVERY FIELD under the top level of the document + setMaps(Sets.newHashSet( + "map('Products', function (p) { " + + " return { " + + " _: Object.keys(p).map(key => createField(key, p[key])) " + + " }; " + + "})" + )); + } + } + ``` + + - The actual dynamic-index-field name on which you can query will be the attribute field **key**. - e.g. Keys `color` & `size` will become the actual dynamic-index-fields. +* **The query**: + + To get all documents with a specific _lastName_ use: + + + + ```java + List matchingDocuments = session + .advanced() + .documentQuery(Product.class, Products_ByAnyField_JS.class) + // 'lastName' is a dynamic-index-field that was indexed from the document + .whereEquals("lastName", "Doe") + .toList(); + ``` + + + ```sql + // 'lastName' is a dynamic-index-field that was indexed from the document + from index 'Products/ByAnyField/JS' where lastName = "Doe" + ``` + + + + + + - - - -{`public class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask { - public Products_ByAttributeKey_JS() { - // Call 'createField' to generate dynamic-index-fields from the attributes object keys - // Using '_' is just a convention. Any other string can be used instead of '_' + - // The actual field name will be the key - // The actual field terms will be derived from p.attributes[key] - setMaps(Sets.newHashSet( - "map('Products', function (p) { " + - " return { " + - " _: Object.keys(p.attributes).map(key => createField(key, p.attributes[key], " + - " { indexing: 'Search', storage: false, termVector: null })) " + - " }; " + - "}) " - )); - } -} -`} - - - + + +### Example - basic -* **The query**: - * You can now query the generated dynamic-index fields. - Property `_` is Not queryable, it is only used in the index definition syntax. - * To get all documents with some 'size' use: - - - -{`List matchingDocuments = session - .query(Product.class, Products_ByAttributeKey_JS.class) - .whereEquals("size", 42) - .toList(); -`} - - - - -{`// 'size' is a dynamic-index-field that was indexed from the Attributes object -from index 'Products/ByAttributeKey/JS' where size = 42 -`} - - - + +The following example shows: + * Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. + * Documents can then be queried based on those indexed values. + * For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---list) below. + -## Example - index any field +* **The document**: + + + ```java + public class Product { + private String id; - + // The VALUE of productType will be dynamically indexed + private String productType; + private int pricePerUnit; -* Define an index on a collection **without** needing any common structure between the indexed documents. -* After index is deployed, any new field added to the document will be indexed as well. + // getters and setters ... + } + ``` + + + + ```json + // Sample document content + { + "productType": "Electronics", + "pricePerUnit": 23 + } + ``` + - +* **The index**: + + The following index will index the **value** of document field 'productType'. + + This value will be the dynamic-index-field name on which you can query. + E.g., Field value `Electronics` will be the dynamic-index-field. + + + + ```java + public class Products_ByProductType extends AbstractIndexCreationTask { + public Products_ByProductType() { + + // Call 'CreateField' to generate the dynamic-index-field. + // The field name will be the value of document field 'productType'. + // The field terms will be derived from document field 'pricePerUnit'. + map = "docs.Products.Select(p => new { " + + " _ = this.CreateField(p.productType, p.pricePerUnit) " + + "})"; + } + } + ``` + + + ```java + public class Products_ByProductType_JS extends AbstractJavaScriptIndexCreationTask { + public Products_ByProductType_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (p) { " + + " return { " + + // The field name will be the value of document field 'productType' + // The field terms will be derived from document field 'pricePerUnit' + " _: createField(p.productType, p.pricePerUnit) " + + " }; " + + "})" + )); + } + } + ``` + + - -Consider if that is a true necessity, as indexing every single field can end up costing time and disk space. +* **The query**: + + To get all documents of some product type having a specific price per unit use: + + + + ```java + List matchingDocuments = session + .advanced() + .documentQuery(Product.class, Products_ByProductType.class) + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + .whereEquals("Electronics", 23) + .toList(); + ``` + + + ```sql + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + from index 'Products/ByProductType' where Electronics = 23 + ``` + + + + + + + +### Example - list + + +The following example allows you to: + * Index **values** from items in a list + * After index is deployed, any item added to this list in the document will be dynamically indexed as well. * **The document**: - - -{`public class Product \{ - private String id; - - // All KEYS in the document will be dynamically indexed - // Fields added to the document after index creation time will also get indexed - private String firstName; - private String lastName; - private String title; - // ... - - // get + set implementation ... -\} -`} - - - - - -{`// Sample document content -\{ - "firstName": "John", - "lastName": "Doe", - "title": "Engineer", - // ... -\} -`} - - + + + ```java + public class Product { + private String id; + private String name; + + // For each element in this list, + // the VALUE of property 'propName' will be dynamically indexed. + // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields. + private List attributes; + + // getters and setters ... + } -* **The index**: - The following index will index any field from the document, - a dynamic-index-field will be created for each field. - New fields added to the document after index creation time will be dynamically indexed as well. + public class Attribute { + private String propName; + private String propValue; - The actual dynamic-index-field name on which you can query will be the field **key**. - E.g., Keys `firstName` & `lastName` will become the actual dynamic-index-fields. + // getters and setters ... + } + ``` + + + + ```json + // Sample document content + { + "name": "SomeName", + "attributes": [ + { + "propName": "Color", + "propValue": "Blue" + }, + { + "propName": "Width", + "propValue": "10" + }, + { + "propName": "Length", + "propValue": "20" + } + // ... + ] + } + ``` + - - - -{`public class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask { - public Products_ByAnyField_JS() { +* **The index**: - // This will index EVERY FIELD under the top level of the document - setMaps(Sets.newHashSet( - "map('Products', function (p) { " + - " return { " + - " _: Object.keys(p).map(key => createField(key, p[key], " + - " { indexing: 'Search', storage: true, termVector: null })) " + - " }; " + - "}) " - )); + The following index will create a dynamic-index-field per item in the document's `attributes` list. + New items added to the attributes list after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the item's propName **value**. + E.g., 'propName' value `Width` will be a dynamic-index-field. + + + + ```java + public class Attributes_ByName extends AbstractIndexCreationTask { + public Attributes_ByName() { + + // Define the dynamic-index-fields by calling 'CreateField'. + // A dynamic-index-field will be generated for each item in 'attributes'. + + // For each item, the field name will be the value of field 'propName' + // The field terms will be derived from field 'propValue'. + // A regular index field (Name) is defined as well. + map = "docs.Products.Select(p => new { " + + " _ = p.attributes.Select(item => " + + " this.CreateField(item.propName, item.propValue)), " + + " Name = p.name " + + "})"; + } } -} -`} - - - + ``` + + + ```java + public class Attributes_ByName_JS extends AbstractJavaScriptIndexCreationTask { + public Attributes_ByName_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (p) { " + + " return { " + + // For each item, + // the field name will be the value of field 'propName'. + // The field terms will be derived from field 'propValue'. + " _: p.attributes.map(item => " + + " createField(item.propName, item.propValue)), " + + // A regular index field can be defined as well: + " Name: p.name " + + " }; " + + "})" + )); + } + } + ``` + + * **The query**: - * To get all documents with some 'lastName' use: - - - -{`List matchingDocuments = session - .query(Product.class, Products_ByAnyField_JS.class) - .whereEquals("lastName", "Doe") - .toList(); -`} - - - - -{`// 'lastName' is a dynamic-index-field that was indexed from the document -from index 'Products/ByAnyField/JS' where lastName = "Doe" -`} - - - + + To get all documents matching a specific attribute property use: + + + + ```java + List matchingDocuments = session + .advanced() + .documentQuery(Product.class, Attributes_ByName.class) + // 'Width' is a dynamic-index-field that was indexed from the attributes list + .whereEquals("Width", 10) + .toList(); + ``` + + + ```sql + // 'Width' is a dynamic-index-field that was indexed from the attributes list + from index 'Attributes/ByName' where Width = 10 + ``` + + + + + + + + + + +Dynamic fields can be indexed for [Full-text search](../../indexes/querying/searching.mdx) by setting `indexing: 'Search'` in the `createField` options object (`AbstractJavaScriptIndexCreationTask`) +or `FieldIndexing.Search` in the `CreateFieldOptions` (`AbstractIndexCreationTask`). +* **At indexing time**: + At indexing time, RavenDB analyzes the dynamic field value and creates the indexed terms. + The analyzer used at indexing time is resolved from the index definition. + You can configure an analyzer for a specific generated field name, or configure `ALL_FIELDS` as a fallback. + See [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) below. -## Indexing documents fields VALUES + * If the generated dynamic field name has an explicit analyzer configuration, that analyzer is used. + * Otherwise, if `ALL_FIELDS` has an analyzer configuration, RavenDB uses it as a fallback. + * If no analyzer is configured for the field, RavenDB uses the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). -## Example - basic +* **At querying time**: + For full-text search to return the expected results, the analyzer used at indexing time and the analyzer used for the query term should produce compatible terms. - + When a `search()` query targets a dynamic field that does **not** have an explicit analyzer configuration for its generated field name, + RavenDB uses the [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) to analyze the query term by default. -* Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. -* Documents can then be queried based on those indexed values. -* For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---index-a-list-of-properties) below. + Set the [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) + configuration key to `true` to make RavenDB use the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) as the fallback analyzer for the query term instead. + + This configuration can be set at different scopes: server-wide, per database, or per index. + The example below sets it at the index scope, in the index definition. + + For a summary of how RavenDB resolves the analyzer used for the query term, + see [Which analyzer is used for the query term](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) below. + +--- + +The following example indexes localized product descriptions as dynamic fields and queries one of those generated fields using full-text search: + +* **The document**: -* **The document**: - - -{`public class Product \{ - private String id; - - // The VALUE of productType will be dynamically indexed - private String productType; - private int pricePerUnit; - - // get + set implementation ... -\} -`} - - + + ```java + public class Product { + private String id; - - -{`// Sample document content -\{ - "productType": "Electronics", - "pricePerUnit": 23 -\} -`} - - + private Map descriptions; -* **The index**: - The following index will index the **value** of document field 'productType'. + // getters and setters ... + } + ``` + + + + ```json + // Sample document content + { + "descriptions": { + "English": "North wind jacket", + "French": "Veste vent du nord" + } + } + ``` + + +* **The index**: + + The following index creates one dynamic field for each entry in the `descriptions` map. + For example, the key `English` creates the dynamic field `Description_English`. + + Each generated dynamic field is indexed for **full-text search**. + + + + ```java + public class Products_ByLocalizedDescription extends AbstractIndexCreationTask { + + private static final String USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public Products_ByLocalizedDescription() { + map = "docs.Products.Select(product => new { " + + " _ = product.descriptions.Select(x => " + + " this.CreateField( " + + " \"Description_\" + x.Key, " + + " x.Value, " + + " new CreateFieldOptions { " + + // Index each generated dynamic field for FTS + " Indexing = FieldIndexing.Search, " + + " Storage = FieldStorage.No " + + " })) " + + "})"; + + storeAllFields(FieldStorage.NO); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + getConfiguration().put(USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS, "true"); + } + } + ``` + + + ```java + public class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + + private static final String USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public Products_ByLocalizedDescription_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (product) { " + + " return { " + + " _: Object.keys(product.descriptions) " + + " .map(function (key) { " + + " return createField( " + + " 'Description_' + key, " + + " product.descriptions[key], " + + " { " + + // Index each generated dynamic field for FTS + " indexing: 'Search', " + + " storage: false " + + " }); " + + " }) " + + " }; " + + "})" + )); + + Map fields = new HashMap<>(); + + IndexFieldOptions allFieldsOptions = new IndexFieldOptions(); + allFieldsOptions.setStorage(FieldStorage.NO); + + fields.put(Constants.Documents.Indexing.Fields.ALL_FIELDS, allFieldsOptions); + setFields(fields); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + getConfiguration().put(USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS, "true"); + } + } + ``` + + + +* **Full-text search query**: + + Query the generated dynamic field by its field name. + + In this example, the query targets the generated field `Description_English`. + + Since `Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery` is set to `true`, + the searched term `"north wind"` is analyzed with the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + + + + ```java + List results = session.advanced() + .documentQuery(Product.class, Products_ByLocalizedDescription.class) + .search("Description_English", "north wind") + .toList(); + ``` + + + + ```sql + from index "Products/ByLocalizedDescription" + where search(Description_English, "north wind") + ``` + + - This value will be the dynamic-index-field name on which you can query. - e.g. Field value `Electronics` will be the dynamic-index-field. +--- - - - -{`public class Products_ByProductType extends AbstractIndexCreationTask { - public Products_ByProductType() { + + +### Configuring analyzers for dynamic fields + +The `CreateFieldOptions` (and the JavaScript `createField` options) do **not** let you specify a custom analyzer directly. +However, you can still configure which analyzer RavenDB will use for dynamic fields in the index definition by either: - // The field name will be the value of document field 'productType' - // The field terms will be derived from document field 'pricePerUnit' - map = "docs.Products.Select(p => new { " + - " _ = this.CreateField(p.productType, p.pricePerUnit) " + +* Configuring an analyzer for a specific generated dynamic field name. +* Configuring `ALL_FIELDS` as a fallback for fields that do not have their own explicit analyzer configuration. + +Use `ALL_FIELDS` with care, since it can affect any field that does not have its own explicit analyzer configuration. + +--- + +#### Configure analyzer for a specific dynamic field + + + +```java +public class Products_ByLocalizedDescription extends AbstractIndexCreationTask { + public Products_ByLocalizedDescription() { + map = "docs.Products.Select(product => new { " + + " _ = product.descriptions.Select(x => " + + " this.CreateField( " + + " \"Description_\" + x.Key, " + + " x.Value, " + + " new CreateFieldOptions { " + + " Indexing = FieldIndexing.Search, " + + " Storage = FieldStorage.No " + + " })) " + "})"; + + storeAllFields(FieldStorage.NO); + + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + analyze("Description_English", "StopAnalyzer"); } } -`} - +``` - - -* **The query**: - * To get all documents of some product type having a specific price per unit use: - - - -{`List matchingDocuments = session - .query(Product.class, Products_ByProductType.class) - .whereEquals("Electronics", 23) - .toList(); -`} - - - - -{`// 'Electronics' is the dynamic-index-field that was indexed from document field 'productType' -from index 'Products/ByProductType' where Electronics = 23 -`} - - - - -## Example - list + +```java +public class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + public Products_ByLocalizedDescription_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (product) { " + + " return { " + + " _: Object.keys(product.descriptions) " + + " .map(function (key) { " + + " return createField( " + + " 'Description_' + key, " + + " product.descriptions[key], " + + " { " + + " indexing: 'Search', " + + " storage: false " + + " }); " + + " }) " + + " }; " + + "})" + )); - + Map fields = new HashMap<>(); -* Index **values** from items in a list -* After index is deployed, any item added this list in the document will be dynamically indexed as well. + IndexFieldOptions allFieldsOptions = new IndexFieldOptions(); + allFieldsOptions.setStorage(FieldStorage.NO); + fields.put(Constants.Documents.Indexing.Fields.ALL_FIELDS, allFieldsOptions); - + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + IndexFieldOptions englishOptions = new IndexFieldOptions(); + englishOptions.setAnalyzer("StopAnalyzer"); + fields.put("Description_English", englishOptions); -* **The document**: - - -{`public class Product \{ - private String id; - private String name; - - // For each element in this list, the VALUE of property 'propName' will be dynamically indexed - // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields - private List attributes; - - // get + set implementation ... -\} - -public class Attribute \{ - private String propName; - private String propValue; - - // get + set implementation ... -\} -`} - + setFields(fields); + } +} +``` + - - -{`// Sample document content -\{ -name": "SomeName", -attributes": [ - \{ - "propName": "Color", - "propValue": "Blue" - \}, - \{ - "propName": "Width", - "propValue": "10" - \}, - \{ - "propName": "Length", - "propValue": "20" - \}, - ... - -\} -`} - - +#### Configure a fallback analyzer for all fields + + + +```java +public class Products_ByLocalizedDescription extends AbstractIndexCreationTask { + public Products_ByLocalizedDescription() { + map = "docs.Products.Select(product => new { " + + " _ = product.descriptions.Select(x => " + + " this.CreateField( " + + " \"Description_\" + x.Key, " + + " x.Value, " + + " new CreateFieldOptions { " + + " Indexing = FieldIndexing.Search, " + + " Storage = FieldStorage.No " + + " })) " + + "})"; -* **The index**: - The following index will create a dynamic-index-field per item in the document's `attributes` list. - New items added to the attributes list after index creation time will be dynamically indexed as well. - - The actual dynamic-index-field name on which you can query will be the item's propName **value**. - E.g., 'propName' value `Width` will be a dynamic-index-field. - - - - -{`public class Attributes_ByName extends AbstractIndexCreationTask { - public Attributes_ByName() { - - // For each attribute item, the field name will be the value of field 'propName' - // The field terms will be derived from field 'propValue' - // A regular-index-field (Name) is defined as well - map = - "docs.Products.Select(p => new { " + - " _ = p.attributes.Select(item => this.CreateField(item.propName, item.propValue)), " + - " Name = p.name " + - "})"; + storeAllFields(FieldStorage.NO); + + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + analyze(Constants.Documents.Indexing.Fields.ALL_FIELDS, "StopAnalyzer"); } } -`} - +``` - + +```java +public class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + public Products_ByLocalizedDescription_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (product) { " + + " return { " + + " _: Object.keys(product.descriptions) " + + " .map(function (key) { " + + " return createField( " + + " 'Description_' + key, " + + " product.descriptions[key], " + + " { " + + " indexing: 'Search', " + + " storage: false " + + " }); " + + " }) " + + " }; " + + "})" + )); -* **The query**: - * To get all documents matching a specific attribute property use: - - - -{`List matchingDocuments = session - .query(Product.class, Attributes_ByName.class) - .whereEquals("Width", 10) - .toList(); -`} - - - - -{`// 'Width' is a dynamic-index-field that was indexed from the attributes list -from index 'Attributes/ByName' where Width = 10 -`} - + Map fields = new HashMap<>(); + + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + IndexFieldOptions allFieldsOptions = new IndexFieldOptions(); + allFieldsOptions.setStorage(FieldStorage.NO); + allFieldsOptions.setAnalyzer("StopAnalyzer"); + fields.put(Constants.Documents.Indexing.Fields.ALL_FIELDS, allFieldsOptions); + + setFields(fields); + } +} +``` + + + + +### Which analyzer is used for the query term? +The following applies when a `search()` query targets a **dynamic field**: -## CreateField syntax +| Index configuration | [Configuration key](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) | Analyzer used for the query term | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| No analyzer is explicitly configured for the generated field name | `false` | The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) | +| No analyzer is explicitly configured for the generated field name | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `false` | The analyzer resolved by the normal field fallback path,
e.g. `WhitespaceAnalyzer` | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| The generated field name is explicitly configured,
e.g. `analyze("Description_English", "StopAnalyzer")` | `false` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +| The generated field name is explicitly configured,
e.g. `analyze("Description_English", "StopAnalyzer")` | `true` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | -#### Syntax for LINQ-index: +
- - -{`object CreateField(string name, object value); +
+ + + +#### Syntax for `AbstractIndexCreationTask` + + +```csharp +object CreateField(string name, object value); object CreateField(string name, object value, bool stored, bool analyzed); object CreateField(string name, object value, CreateFieldOptions options); -`} - +``` -#### Syntax for JavaScript-index: +#### Syntax for `AbstractJavaScriptIndexCreationTask` - - -{`createField(fieldName, fieldValue, options); // returns object -`} - + +```js +createField(fieldName, fieldValue, options); // returns object +``` -| Parameters | Type | Description | -|----------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **fieldName** | `string` | Name of the dynamic-index-field | -| **fieldValue** | `object` | Value of the dynamic-index-field
The field Terms are derived from this value. | -| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorate.Yes` | -| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing.Default` (default value)
`false` - `FieldIndexing.Exact`
`true` - `FieldIndexing.Search` | +| Parameters | Type | Description | +|----------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **fieldName** | `string` | Name of the dynamic-index-field | +| **fieldValue** | `object` | Value of the dynamic-index-field
The field terms are derived from this value. | +| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorage.Yes` | +| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing.Default` (default value)
`false` - `FieldIndexing.Exact`
`true` - `FieldIndexing.Search` | | **options** | `CreateFieldOptions` | Dynamic-index-field options | -| CreateFieldOptions | | | -|--------------------|--------------------|----------------------------------------------------------------------------| +| CreateFieldOptions | | | +|--------------------|--------------------|-----------------------------------------------------------------------------------| | **Storage** | `FieldStorage?` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | -| **Indexing** | `FieldIndexing?` | Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | -| **TermVector** | `FieldTermVector?` | Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | +| **Indexing** | `FieldIndexing?` | Sets the field indexing behavior.
Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | +| **TermVector** | `FieldTermVector?` | Sets the field term-vector behavior.
Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | +* The `CreateField` syntax (and the `CreateFieldOptions` shape) above describes what is available inside the server-side `map` string of an `AbstractIndexCreationTask`, + since the Java client sends the `map` source to the server for compilation. + The JavaScript `createField` function is the equivalent for `AbstractJavaScriptIndexCreationTask` map sources. + * All above examples have used the character `_` in the dynamic-index-field definition. However, using `_` is just a convention. Any other string can be used instead. * This property is Not queryable, it is only used in the index definition syntax. - The actual dynamic-index-fields that are generated are defined by the `CreateField` method. + The actual dynamic-index-fields that are generated are defined by the `CreateField` method (or the `createField` JS function). +
+ + - -## Indexed fields & terms view - -The generated dynamic-index-fields and their indexed terms can be viewed in the **Terms View**. -Below are sample index fields & their terms generated from the last example. - +The generated dynamic index fields and their indexed terms can be viewed in the **Terms View** in Studio. +Below are sample index fields & their terms generated from [this example](../../indexes/using-dynamic-fields.mdx#example---list). + ![Figure 1. Go to terms view](../assets/dynamic-index-fields-1.png) - + ![Figure 2. Indexed fields & terms](../assets/dynamic-index-fields-2.png) - - - - + + diff --git a/docs/indexes/content/_using-dynamic-fields-nodejs.mdx b/docs/indexes/content/_using-dynamic-fields-nodejs.mdx index b5df2df4a7..c14184c2b5 100644 --- a/docs/indexes/content/_using-dynamic-fields-nodejs.mdx +++ b/docs/indexes/content/_using-dynamic-fields-nodejs.mdx @@ -2,533 +2,710 @@ import Admonition from '@theme/Admonition'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; +import ContentFrame from '@site/src/components/ContentFrame'; +import Panel from '@site/src/components/Panel'; -* In RavenDB different documents can have different shapes. - Documents are schemaless - new fields can be added or removed as needed. - -* For such dynamic data, you can define indexes with **dynamic-index-fields**. - -* This allows querying the index on fields that aren't yet known at index creation time, - which is very useful when working on highly dynamic systems. - -* Any value type can be indexed, string, number, date, etc. - -* An index definition can contain both dynamic-index-fields and regular-index-fields. - -* In this page: - - * [Indexing documents fields KEYS](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-keys) - * [Example - index any field under object](../../indexes/using-dynamic-fields.mdx#example---index-any-field-under-object) - * [Example - index any field](../../indexes/using-dynamic-fields.mdx#example---index-any-field) - * [Indexing documents fields VALUES](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-values) - * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) - * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) - * [CreateField syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) - * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields--terms-view) +* In RavenDB, different documents can have different shapes. + Documents are schemaless - new fields can be added or removed as needed. + For such dynamic data, you can define indexes with **dynamic-index-fields**. + +* Dynamic-index-fields are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time, + which is very useful when working with highly dynamic systems. + +* Any value type can be indexed, string, number, date, etc. + An index definition can contain both dynamic-index-fields and regular-index-fields. + +* In this article: + * [Indexing field KEYS](../../indexes/using-dynamic-fields.mdx#indexing-field-keys) + * [Example - index every field under an object](../../indexes/using-dynamic-fields.mdx#example---index-every-field-under-an-object) + * [Example - index every field](../../indexes/using-dynamic-fields.mdx#example---index-every-field) + * [Indexing field VALUES](../../indexes/using-dynamic-fields.mdx#indexing-field-values) + * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) + * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) + * [Indexing dynamic fields for full-text search](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + * [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) + * [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + * [`createField` syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) + * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields-terms-view) -## Indexing documents fields KEYS + - -#### Example - index any field under object -The following allows you to: - -* Index any field that is under the some object from the document. -* After index is deployed, any new field added to the this object will be indexed as well. -**The document**: - - -{`class Product \{ - constructor(id, attributes) \{ - this.id = id; - - // The KEYS under the attributes object will be dynamically indexed - // Fields added to this object after index creation time will also get indexed - this.attributes = attributes; - \} -\} -`} - - + + +### Example - index every field under an object - - -{`// Sample document content -\{ - "attributes": \{ - "color": "Red", - "size": 42 - \} -\} -`} - - + +The following example allows you to: + * Index any field that is under the same object from the document. + * After index is deployed, any new field added to this object will be indexed as well. + -**The index**: +* **The document**: + + + ```js + class Product { + constructor(id, attributes) { + this.id = id; + + // The KEYS under the attributes object will be dynamically indexed + // Fields added to this object after index creation time will also get indexed + this.attributes = attributes; + } + } + ``` + + + + ```json + // Sample document content + { + "attributes": { + "color": "Red", + "size": 42 + } + } + ``` + -* The following index will index any field under the `attributes` object from the document, +* **The index**: + + The following index will index any field under the `attributes` object from the document, a dynamic-index-field will be created for each such field. - New fields added to the object after index creation time will be dynamically indexed as well. - -* The actual dynamic-index-field name on which you can query will be the attribute field **key**. - e.g. Keys `color` & `size` will become the actual dynamic-index-fields. - - - - -{`class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask { - constructor() { - super(); - - const { createField } = this.mapUtils(); - - this.map("Products", p => { - return { - // Call 'createField' to generate dynamic-index-fields from the attributes object keys - // Using '_' is just a convention. Any other string can be used instead of '_' - - // The actual field name will be the key - // The actual field terms will be derived from p.attributes[key] - _: Object.keys(p.attributes).map(key => createField(key, p.attributes[key], { - indexing: "Search", - storage: false, - termVector: null - })) - }; - }); + New fields added to the object after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the attribute field **key**. + E.g., Keys `color` & `size` will become the actual dynamic-index-fields. + + + ```js + class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { + super(); + + const { createField } = this.mapUtils(); + + this.map("Products", p => { + return { + // Call 'createField' to generate dynamic-index-fields + // from the attributes object keys. + + // Using '_' is just a convention. + // Any other string can be used instead of '_' + + // The actual field name will be the key + // The actual field terms will be derived from p.attributes[key] + _: Object.keys(p.attributes).map(key => createField(key, p.attributes[key])) + }; + }); + } } -} -`} - - - - -**The query**: - -* You can now query the generated dynamic-index fields. - Property `_` is Not queryable, it is only used in the index definition syntax. - -* To get all documents with some 'size' use: - - - - -{`const matchingDocuments = session.query({indexName: 'Products_ByAttributeKey'}) - // 'size' is a dynamic-index-field that was indexed from the attributes object - .whereEquals('size', 42) - .all(); -`} - - - - -{`// 'size' is a dynamic-index-field that was indexed from the attributes object -from index 'Products/ByAttributeKey' where size = 42 -`} - - - - - + ``` + + +* **The query**: + + You can now query the generated dynamic-index fields. + The `_` property is Not queryable but used only in the index definition syntax. + + To get all documents with a specific _size_ use: + + + + ```js + const matchingDocuments = await session + .query({ indexName: "Products/ByAttributeKey/JS" }) + // 'size' is a dynamic-index-field that was indexed from the attributes object + .whereEquals("size", 42) + .all(); + ``` + + + ```sql + // 'size' is a dynamic-index-field that was indexed from the attributes object + from index 'Products/ByAttributeKey/JS' where size = 42 + ``` + + + + + + + +### Example - index every field -#### Example - index any field -The following allows you to: - -* Define an index on a collection **without** needing any common structure between the indexed documents. -* After index is deployed, any new field added to the document will be indexed as well. - +The following example allows you to: + * Define an index on a collection **without** needing any common structure between the indexed documents. + * After the index is deployed, any new field added to any document in the collection will be indexed as well. + + - -Consider if that is a true necessity, as indexing every single field can end up costing time and disk space. - +* Use this approach with care. +* Indexing every field can increase indexing time, memory usage, and disk space, + especially for large or frequently updated documents. -**The document**: - - -{`class Product \{ - constructor(id, firstName, lastName, title) \{ - this.id = id; - - // All KEYS in the document will be dynamically indexed - // Fields added to the document after index creation time will also get indexed - this.firstName = firstName; - this.lastName = lastName; - this.title = title; - // ... - \} -\} -`} - - - - - -{`// Sample document content -\{ - "firstName": "John", - "lastName": "Doe", - "title": "Engineer", - // ... -\} -`} - - -**The index**: +* **The document**: + + + ```js + class Product { + constructor(id, firstName, lastName, title) { + this.id = id; + + // All KEYS in the document will be dynamically indexed + // Fields added to the document after index creation time will also get indexed + this.firstName = firstName; + this.lastName = lastName; + this.title = title; + // ... + } + } + ``` + + + + ```json + // Sample document content + { + "firstName": "John", + "lastName": "Doe", + "title": "Engineer", + // ... + } + ``` + -* The following index will index any field from the document, +* **The index**: + + The following index will index any field from the document, a dynamic-index-field will be created for each field. - New fields added to the document after index creation time will be dynamically indexed as well. - -* The actual dynamic-index-field name on which you can query will be the field **key**. - e.g. Keys `firstName` & `lastName` will become the actual dynamic-index-fields. + New fields added to the document after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the field **key**. + E.g., Keys `firstName` & `lastName` will become the actual dynamic-index-fields. + + + ```js + class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { + super(); + + const { createField } = this.mapUtils(); + + // This will index EVERY FIELD under the top level of the document + this.map("Products", p => { + return { + _: Object.keys(p).map(key => createField(key, p[key])) + }; + }); + } + } + ``` + + +* **The query**: + + To get all documents with a specific _lastName_ use: + + + + ```js + const matchingDocuments = await session + .query({ indexName: "Products/ByAnyField/JS" }) + // 'lastName' is a dynamic-index-field that was indexed from the document + .whereEquals("lastName", "Doe") + .all(); + ``` + + + ```sql + // 'lastName' is a dynamic-index-field that was indexed from the document + from index 'Products/ByAnyField/JS' where lastName = "Doe" + ``` + + + + + + + + + + + +### Example - basic + + +The following example shows: + * Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. + * Documents can then be queried based on those indexed values. + * For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---list) below. + - - - -{`class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask { - constructor () { - super(); +* **The document**: + + + ```js + class Product { + constructor(id, productType, pricePerUnit) { + this.id = id; + + // The VALUE of productType will be dynamically indexed + this.productType = productType; + this.pricePerUnit = pricePerUnit; + } + } + ``` + + + + ```json + // Sample document content + { + "productType": "Electronics", + "pricePerUnit": 23 + } + ``` + + +* **The index**: + + The following index will index the **value** of document field 'productType'. + + This value will be the dynamic-index-field name on which you can query. + E.g., Field value `Electronics` will be the dynamic-index-field. + + + ```js + class Products_ByProductType_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { + super(); + + const { createField } = this.mapUtils(); + + this.map("Products", p => { + return { + // The field name will be the value of document field 'productType' + // The field terms will be derived from document field 'pricePerUnit' + _: createField(p.productType, p.pricePerUnit) + }; + }); + } + } + ``` + + +* **The query**: + + To get all documents of some product type having a specific price per unit use: + + + + ```js + const matchingDocuments = await session + .query({ indexName: "Products/ByProductType/JS" }) + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + .whereEquals("Electronics", 23) + .all(); + ``` + + + ```sql + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + from index 'Products/ByProductType/JS' where Electronics = 23 + ``` + + + + + + + +### Example - list - const { createField } = this.mapUtils(); + +The following example allows you to: + * Index **values** from items in a list + * After index is deployed, any item added to this list in the document will be dynamically indexed as well. + - this.map("Products", p => { - return { - // This will index EVERY FIELD under the top level of the document - _: Object.keys(p).map(key => createField(key, p[key], { - indexing: "Search", - storage: true, - termVector: null - })) - }; - }); +* **The document**: + + + ```js + class Product { + constructor(id, name, attributes) { + this.id = id; + this.name = name; + + // For each element in this list, + // the VALUE of property 'propName' will be dynamically indexed. + // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields. + this.attributes = attributes; + } } -} -`} - - - -**The query**: + class Attribute { + constructor(propName, propValue) { + this.propName = propName; + this.propValue = propValue; + } + } + ``` + + + + ```json + // Sample document content + { + "name": "SomeName", + "attributes": [ + { + "propName": "Color", + "propValue": "Blue" + }, + { + "propName": "Width", + "propValue": "10" + }, + { + "propName": "Length", + "propValue": "20" + }, + ... + ] + } + ``` + + +* **The index**: + + The following index will create a dynamic-index-field per item in the document's `attributes` list. + New items added to the attributes list after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the item's propName **value**. + E.g., 'propName' value `Width` will be a dynamic-index-field. + + + ```js + class Attributes_ByName_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { + super(); + + const { createField } = this.mapUtils(); + + this.map("Products", p => { + return { + // Define the dynamic-index-fields by calling 'createField' + // A dynamic-index-field will be generated for each item in 'attributes' + + // For each item, the field name will be the value of field 'propName' + // The field terms will be derived from field 'propValue' + _: p.attributes.map(item => + createField(item.propName, item.propValue)), + + // A regular index field can be defined as well: + name: p.name + }; + }); + } + } + ``` + + +* **The query**: + + To get all documents matching a specific attribute property use: + + + + ```js + const matchingDocuments = await session + .query({ indexName: "Attributes/ByName/JS" }) + // 'Width' is a dynamic-index-field that was indexed from the attributes list + .whereEquals("Width", 10) + .all(); + ``` + + + ```sql + // 'Width' is a dynamic-index-field that was indexed from the attributes list + from index 'Attributes/ByName/JS' where Width = 10 + ``` + + + + + + + + + + + +Dynamic fields can be indexed for [Full-text search](../../indexes/querying/searching.mdx) by setting `indexing: "Search"` in the `createField` options. + +* **At indexing time**: + At indexing time, RavenDB analyzes the dynamic field value and creates the indexed terms. + The analyzer used at indexing time is resolved from the index definition. + You can configure an analyzer for a specific generated field name, or configure `ALL_FIELDS` as a fallback. + See [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) below. + + * If the generated dynamic field name has an explicit analyzer configuration, that analyzer is used. + * Otherwise, if `ALL_FIELDS` has an analyzer configuration, RavenDB uses it as a fallback. + * If no analyzer is configured for the field, RavenDB uses the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + +* **At querying time**: + For full-text search to return the expected results, the analyzer used at indexing time and the analyzer used for the query term should produce compatible terms. + + When a `search()` query targets a dynamic field that does **not** have an explicit analyzer configuration for its generated field name, + RavenDB uses the [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) to analyze the query term by default. + + Set the [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) + configuration key to `true` to make RavenDB use the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) as the fallback analyzer for the query term instead. + + This configuration can be set at different scopes: server-wide, per database, or per index. + The example below sets it at the index scope, in the index definition. + + For a summary of how RavenDB resolves the analyzer used for the query term, + see [Which analyzer is used for the query term](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) below. -* To get all documents with some 'lastName' use: + + +--- + +The following example indexes localized product descriptions as dynamic fields and queries one of those generated fields using full-text search: + +* **The document**: + + + ```js + class Product { + constructor(id, descriptions) { + this.id = id; + this.descriptions = descriptions; + } + } + ``` + + + + ```json + // Sample document content + { + "descriptions": { + "English": "North wind jacket", + "French": "Veste vent du nord" + } + } + ``` + + +* **The index**: + + The following index creates one dynamic field for each entry in the `descriptions` dictionary. + For example, the key `English` creates the dynamic field `Description_English`. + + Each generated dynamic field is indexed for **full-text search**. + + + ```js + class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { + super(); + + const { createField } = this.mapUtils(); + + const useSearchAnalyzerForDynamicFields = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + this.map("Products", product => { + return { + _: Object.keys(product.descriptions).map(key => + createField( + "Description_" + key, + product.descriptions[key], + { + // Index each generated dynamic field for FTS + indexing: "Search", + storage: false + })) + }; + }); + + this.storeAllFields("No"); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + this.configuration[useSearchAnalyzerForDynamicFields] = "true"; + } + } + ``` + - - - -{`const matchingDocuments = session.query({ indexName: 'Products_ByAnyField_JS' }) - // 'lastName' is a dynamic-index-field that was indexed from the document - .whereEquals('lastName', 'Doe') - .all(); -`} - - - - -{`// 'lastName' is a dynamic-index-field that was indexed from the document -from index 'Products/ByAnyField/JS' where lastName = "Doe" -`} - - - +* **Full-text search query**: -
+ Query the generated dynamic field by its field name. + In this example, the query targets the generated field `Description_English`. + Since `Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery` is set to `true`, + the searched term `"north wind"` is analyzed with the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). -## Indexing documents fields VALUES + + + ```js + const results = await session + .query({ indexName: "Products/ByLocalizedDescription/JS" }) + .search("Description_English", "north wind") + .all(); + ``` + - -#### Example - basic -This example shows: - -* Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. -* Documents can then be queried based on those indexed values. -* For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---index-a-list-of-properties) below. -**The document**: - - -{`class Product \{ - constructor(id, productType, pricePerUnit) \{ - this.id = id; - - // The VALUE of productType will be dynamically indexed - this.productType = productType; - this.pricePerUnit = pricePerUnit; - \} -\} -`} - - + + ```sql + from index "Products/ByLocalizedDescription/JS" + where search(Description_English, "north wind") + ``` + + - - -{`// Sample document content -\{ - "productType": "Electronics", - "pricePerUnit": 23 -\} -`} - - +--- -**The index**: + + +### Configuring analyzers for dynamic fields -* The following index will index the **value** of document field 'productType'. +The `createField` options object does **not** let you specify a custom analyzer directly. +However, you can still configure which analyzer RavenDB will use for dynamic fields in the index definition by either: + +* Configuring an analyzer for a specific generated dynamic field name. +* Configuring `ALL_FIELDS` as a fallback for fields that do not have their own explicit analyzer configuration. + +Use `ALL_FIELDS` with care, since it can affect any field that does not have its own explicit analyzer configuration. -* This value will be the dynamic-index-field name on which you can query. - e.g. Field value `Electronics` will be the dynamic-index-field. +--- - - - -{`class Products_ByProductType extends AbstractCsharpIndexCreationTask { - constructor () { - super(); +#### Configure analyzer for a specific dynamic field - // The field name will be the value of document field 'productType' - // The field terms will be derived from document field 'pricePerUnit' - this.map = "docs.Products.Select(p => new { " + - " _ = this.CreateField(p.productType, p.pricePerUnit) " + - "})"; - } -} -`} - - - - -{`class Products_ByProductType_JS extends AbstractJavaScriptIndexCreationTask { - constructor () { + +```js +class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { super(); const { createField } = this.mapUtils(); - this.map("Products", p => { + this.map("Products", product => { return { - _: [ - // The field name will be the value of document field 'productType' - // The field terms will be derived from document field 'pricePerUnit' - createField(p.productType, p.pricePerUnit, { - indexing: "Search", - storage: false, - termVector: null - }) - ] + _: Object.keys(product.descriptions).map(key => + createField( + "Description_" + key, + product.descriptions[key], + { + indexing: "Search", + storage: false + })) }; }); - } -} -`} - - - - -**The query**: - -* To get all documents of some product type having a specific price per unit use: - - - - -{`const matchingDocuments = session.query({ indexName: 'Products_ByProductType' }) - // 'Electronics' is the dynamic-index-field that was indexed from document field 'productType' - .whereEquals('Electronics', 23) - .all(); -`} - - - - -{`// 'Electronics' is the dynamic-index-field that was indexed from document field 'productType' -from index 'Products/ByProductType' where Electronics = 23 -`} - - - - - - -#### Example - list -The following allows you to: - -* Index **values** from items in a list -* After index is deployed, any item added this list in the document will be dynamically indexed as well. -**The document**: - - -{`class Product \{ - constructor(id, name, attributes) \{ - this.id = id; - this.name = name; - - // For each element in this list, the VALUE of property 'propName' will be dynamically indexed - // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields - this.attributes = attributes; - \} -\} - -class Attribute \{ - constructor(propName, propValue) \{ - this.propName = propName; - this.propValue = propValue; - \} -\} -`} - - - - - -{`// Sample document content -\{ - "name": "SomeName", - "attributes": [ - \{ - "propName": "Color", - "propValue": "Blue" - \}, - \{ - "propName": "Width", - "propValue": "10" - \}, - \{ - "propName": "Length", - "propValue": "20" - \}, - ... - ] -\} -`} - - - -**The index**: - -* The following index will create a dynamic-index-field per item in the document's `attributes` list. - New items added to the attributes list after index creation time will be dynamically indexed as well. - -* The actual dynamic-index-field name on which you can query will be the item's propName **value**. - e.g. 'propName' value `Width` will be a dynamic-index-field. - - - - -{`class Attributes_ByName extends AbstractCsharpIndexCreationTask -{ - constructor () { - super(); + this.storeAllFields("No"); - // For each attribute item, the field name will be the value of field 'propName' - // The field terms will be derived from field 'propValue' - // A regular-index-field (Name) is defined as well - this.map = - "docs.Products.Select(p => new { " + - " _ = p.attributes.Select(item => this.CreateField(item.propName, item.propValue)), " + - " Name = p.name " + - "})"; + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + this.analyze("Description_English", "StopAnalyzer"); } } -`} - +``` - - -{`class Attributes_ByName_JS extends AbstractJavaScriptIndexCreationTask { - constructor () { + +#### Configure a fallback analyzer for all fields + + +```js +class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { super(); const { createField } = this.mapUtils(); - this.map("Products", p => { + this.map("Products", product => { return { - // For each item, the field name will be the value of field 'propName' - // The field terms will be derived from field 'propValue' - _: p.attributes.map(item => createField(item.propName, item.propValue, { - indexing: "Search", - storage: true, - termVector: null - })), - - // A regular-index-field can be defined as well: - Name: p.name + _: Object.keys(product.descriptions).map(key => + createField( + "Description_" + key, + product.descriptions[key], + { + indexing: "Search", + storage: false + })) }; }); - } -} -`} - - - - -**The query**: -* To get all documents matching a specific attribute property use: + this.storeAllFields("No"); - - - -{`const matchingDocuments = session.query({ indexName: 'Attributes/ByName' }) - // 'Width' is a dynamic-index-field that was indexed from the attributes list - .whereEquals('Width', 10) - .all(); -`} - - - - -{`// 'Width' is a dynamic-index-field that was indexed from the attributes list -from index 'Attributes/ByName' where Width = 10 -`} - + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + this.analyze(CONSTANTS.Documents.Indexing.Fields.ALL_FIELDS, "StopAnalyzer"); + } +} +``` - - - + + + + +### Which analyzer is used for the query term? -## CreateField syntax +The following applies when a `search()` query targets a **dynamic field**: -#### Syntax for LINQ-index: +| Index configuration | [Configuration key](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) | Analyzer used for the query term | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| No analyzer is explicitly configured for the generated field name | `false` | The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) | +| No analyzer is explicitly configured for the generated field name | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `false` | The analyzer resolved by the normal field fallback path,
e.g. `WhitespaceAnalyzer` | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| The generated field name is explicitly configured,
e.g. `this.analyze("Description_English", "StopAnalyzer")` | `false` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +| The generated field name is explicitly configured,
e.g. `this.analyze("Description_English", "StopAnalyzer")` | `true` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | - - -{`object CreateField(string name, object value); +
-object CreateField(string name, object value, bool stored, bool analyzed); + -object CreateField(string name, object value, CreateFieldOptions options); -`} - - + -#### Syntax for JavaScript-index: +#### Syntax: - - -{`createField(fieldName, fieldValue, options); // returns object -`} - + +```js +createField(fieldName, fieldValue, options); // returns object +``` -| Parameters | Type | Description | -|----------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **fieldName** | `string` | Name of the dynamic-index-field | -| **fieldValue** | `object` | Value of the dynamic-index-field
The field Terms are derived from this value. | -| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorate.Yes` | -| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing.Default` (default value)
`false` - `FieldIndexing.Exact`
`true` - `FieldIndexing.Search` | -| **options** | `CreateFieldOptions` | Dynamic-index-field options | +| Parameters | Type | Description | +|----------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **fieldName** | `string` | Name of the dynamic-index-field | +| **fieldValue** | `object` | Value of the dynamic-index-field
The field terms are derived from this value. | +| **options** | `object` | Dynamic-index-field options | -| CreateFieldOptions | | | -|--------------------|--------------------|----------------------------------------------------------------------------| -| **Storage** | `FieldStorage?` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | -| **Indexing** | `FieldIndexing?` | Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | -| **TermVector** | `FieldTermVector?` | Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | +| options object | | | +|-----------------|--------------------|-----------------------------------------------------------------------------------| +| **storage** | `boolean` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | +| **indexing** | `FieldIndexing` | Sets the field indexing behavior.
Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | +| **termVector** | `FieldTermVector` | Sets the field term-vector behavior.
Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | @@ -536,21 +713,19 @@ object CreateField(string name, object value, CreateFieldOptions options); However, using `_` is just a convention. Any other string can be used instead. * This property is Not queryable, it is only used in the index definition syntax. - The actual dynamic-index-fields that are generated are defined by the `CreateField` method. + The actual dynamic-index-fields that are generated are defined by the `createField` method. +
+ + - -## Indexed fields & terms view - -The generated dynamic-index-fields and their indexed terms can be viewed in the **Terms View**. -Below are sample index fields & their terms generated from the last example. - +The generated dynamic index fields and their indexed terms can be viewed in the **Terms View** in Studio. +Below are sample index fields & their terms generated from [this example](../../indexes/using-dynamic-fields.mdx#example---list). + ![Figure 1. Go to terms view](../assets/dynamic-index-fields-1.png) - + ![Figure 2. Indexed fields & terms](../assets/dynamic-index-fields-2.png) - - - - + + diff --git a/docs/indexes/content/_using-dynamic-fields-php.mdx b/docs/indexes/content/_using-dynamic-fields-php.mdx index 09c58cc9fc..6992310715 100644 --- a/docs/indexes/content/_using-dynamic-fields-php.mdx +++ b/docs/indexes/content/_using-dynamic-fields-php.mdx @@ -2,537 +2,969 @@ import Admonition from '@theme/Admonition'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; +import ContentFrame from '@site/src/components/ContentFrame'; +import Panel from '@site/src/components/Panel'; -* In RavenDB different documents can have different shapes. - Documents are schemaless - new fields can be added or removed as needed. +* In RavenDB, different documents can have different shapes. + Documents are schemaless - new fields can be added or removed as needed. + For such dynamic data, you can define indexes with **dynamic-index-fields**. + +* Dynamic-index-fields are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time, + which is very useful when working with highly dynamic systems. + +* Any value type can be indexed, string, number, date, etc. + An index definition can contain both dynamic-index-fields and regular-index-fields. + +* In this article: + * [Indexing field KEYS](../../indexes/using-dynamic-fields.mdx#indexing-field-keys) + * [Example - index every field under an object](../../indexes/using-dynamic-fields.mdx#example---index-every-field-under-an-object) + * [Example - index every field](../../indexes/using-dynamic-fields.mdx#example---index-every-field) + * [Indexing field VALUES](../../indexes/using-dynamic-fields.mdx#indexing-field-values) + * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) + * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) + * [Indexing dynamic fields for full-text search](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + * [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) + * [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + * [`CreateField` syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) + * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields-terms-view) -* For such dynamic data, you can define indexes with **dynamic-index-fields**. - -* This allows querying the index on fields that aren't yet known at index creation time, - which is very useful when working on highly dynamic systems. - -* Any value type can be indexed, string, number, date, etc. - -* An index definition can contain both dynamic-index-fields and regular-index-fields. + -* In this page: + - * [Indexing documents fields KEYS](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-keys) - * [Example - index any field under object](../../indexes/using-dynamic-fields.mdx#example---index-any-field-under-object) - * [Example - index any field](../../indexes/using-dynamic-fields.mdx#example---index-any-field) - * [Indexing documents fields VALUES](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-values) - * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) - * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) - * [CreateField syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) - * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields--terms-view) + + +### Example - index every field under an object + +The following example allows you to: + * Index any field that is under the same object from the document. + * After index is deployed, any new field added to this object will be indexed as well. -## Indexing documents fields KEYS - -## Example - index any field under object +* **The document**: + + + ```php + use Ds\Map as DSMap; - + class Product + { + public ?string $id = null; -* Index any field that is under the some object from the document. -* After index is deployed, any new field added to the this object will be indexed as well. + // The KEYS under the attributes object will be dynamically indexed. + // Fields added to this object after index creation time will also get indexed. + public ?DSMap $attributes = null; - - -* **The document**: - - -{`use Ds\\Map as DSMap; - -class Product -\{ - private ?string $id = null; - - // The KEYS under the Attributes object will be dynamically indexed - // Fields added to this object after index creation time will also get indexed - public ?DSMap $attributes = null; -\} -`} - - - - -{`// Sample document content -\{ - "Attributes": \{ - "Color": "Red", - "Size": 42 - \} -\} -`} - - + // ... getters and setters + } + ``` + + + + ```json + // Sample document content + { + "attributes": { + "Color": "Red", + "Size": 42 + } + } + ``` + * **The index**: - The below index will index any field under the `Attributes` object from the document, + + The following index will index any field under the `attributes` object from the document, a dynamic-index-field will be created for each such field. New fields added to the object after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the attribute field **key**. - E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. + The actual dynamic-index-field name on which you can query will be the attribute field **key**. + E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. - - - -{`class Products_ByAttributeKey extends AbstractIndexCreationTask -{ - public function __construct() + + + ```php + class Products_ByAttributeKey extends AbstractIndexCreationTask { - parent::__construct(); - - $this->map = "from p in docs.Products select new {" . - "_ = p.attributes.Select(item => CreateField(item.Key, item.Value))" . - "}"; + public function __construct() + { + parent::__construct(); + + // Call 'CreateField' to generate dynamic-index-fields + // from the attributes object keys. + // + // Using '_' is just a convention. + // Any other string can be used instead of '_'. + // + // The actual field name will be 'item.Key'. + // The actual field terms will be derived from 'item.Value'. + $this->map = + "from p in docs.Products " . + "select new { " . + " _ = p.attributes.Select(item => CreateField(item.Key, item.Value)) " . + "}"; + } } -} -`} - - - - -{`class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask -{ - public function __construct() + ``` + + + ```php + class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask { - parent::__construct(); - - $this->setMaps([ - "map('Products', function (p) { " . - " return { " . - " _: Object.keys(p.attributes).map(key => createField(key, p.attributes[key], " . - " { indexing: 'Search', storage: false, termVector: null })) " . - " }; " . - "}) " - ]); + public function __construct() + { + parent::__construct(); + + $this->setMaps([ + "map('Products', function (p) { + return { + // The field name will be the key + // The field terms will be derived from the corresponding value + _: Object.keys(p.attributes) + .map(key => createField(key, p.attributes[key], null)) + }; + })" + ]); + } } -} -`} - - - - + ``` + + * **The query**: - * You can now query the generated dynamic-index fields. - * To get all documents with some 'size' use: - - - -{`$matchingDocuments = $session - ->advanced() - ->documentQuery(Product::class, Products_ByAttributeKey::class) - // 'Size' is a dynamic-index-field that was indexed from the Attributes object - ->whereEquals("Size", 42) - ->toList(); -`} - - - - -{`// 'Size' is a dynamic-index-field that was indexed from the Attributes object -from index 'Products/ByAttributeKey' where Size = 42 -`} - - - - -## Example - index any field - - + + You can now query the generated dynamic-index fields. + The `_` property is Not queryable but used only in the index definition syntax. + + To get all documents with a specific _Size_ use: + + + + ```php + /** @var array $matchingDocuments */ + $matchingDocuments = $session + ->advanced() + ->documentQuery(Product::class, Products_ByAttributeKey::class) + // 'Size' is a dynamic-index-field that was indexed from the attributes object + ->whereEquals("Size", 42) + ->toList(); + ``` + + + ```sql + // 'Size' is a dynamic-index-field that was indexed from the attributes object + from index 'Products/ByAttributeKey' where Size = 42 + ``` + + + + + Dynamic index fields are queried with _documentQuery_ or _RQL_ because the field names (e.g. `Size`, `Color`) + are generated at indexing time and are not properties on the _Product_ class. + + Use the string-based `whereEquals("Size", 42)` or RQL `where Size = 42` instead. + + + + + + +### Example - index every field + +The following example allows you to: * Define an index on a collection **without** needing any common structure between the indexed documents. - * After index is deployed, any new field added to the document will be indexed as well. - + * After the index is deployed, any new field added to any document in the collection will be indexed as well. - + -Consider whether this is really necessary, as indexing every single field can end up costing time and disk space. +* Use this approach with care. +* Indexing every field can increase indexing time, memory usage, and disk space, + especially for large or frequently updated documents. * **The document**: - - -{`class Product -\{ - private ?string $id = null; - - // All KEYS in the document will be dynamically indexed - // Fields added to the document after index creation time will also get indexed - public ?string $firstName = null; - public ?string $lastName = null; - public ?string $title = null; - - // ... getters and setters -\} -`} - - + + + ```php + class Product + { + public ?string $id = null; - - -{`// Sample document content - \{ - "FirstName": "John", - "LastName": "Doe", - "Title": "Engineer", + // All KEYS in the document will be dynamically indexed. + // Fields added to the document after index creation time will also get indexed. + public ?string $firstName = null; + public ?string $lastName = null; + public ?string $title = null; // ... -\} -`} - - + + // ... getters and setters + } + ``` + + + + ```json + // Sample document content + { + "firstName": "John", + "lastName": "Doe", + "title": "Engineer" + // ... + } + ``` + * **The index**: - The below index will index any field from the document, + + The following index will index any field from the document, a dynamic-index-field will be created for each field. New fields added to the document after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the field **key**. - E.g., Keys `FirstName` & `LastName` will become the actual dynamic-index-fields. - - - - -{`class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask -{ - public function __construct() + The actual dynamic-index-field name on which you can query will be the field **key**. + E.g., Keys `firstName` & `lastName` will become the actual dynamic-index-fields. + + The example uses a JavaScript index because it enumerates all top-level document fields at indexing time with `Object.keys(...)`. + LINQ indexes typically create dynamic fields from a known enumerable structure, such as a dictionary or collection. + + + + ```php + class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask { - parent::__construct(); - - // This will index EVERY FIELD under the top level of the document - $this->setMaps([ - "map('Products', function (p) { - return { - _: Object.keys(p).map(key => createField(key, p[key], - { indexing: 'Search', storage: true, termVector: null })) - } - })" - ]); + public function __construct() + { + parent::__construct(); + + // This will index EVERY FIELD under the top level of the document + $this->setMaps([ + "map('Products', function (p) { + return { + _: Object.keys(p).map(key => createField(key, p[key], null)) + }; + })" + ]); + } } -} -`} - - - + ``` + + * **The query**: - * To get all documents with some 'LastName' use: - - - -{`$matchingDocuments = $session - ->advanced() - ->documentQuery(Product::class, Products_ByAnyField_JS::class) - // 'LastName' is a dynamic-index-field that was indexed from the document - ->whereEquals("LastName", "Doe") - ->toList(); -`} - - - - -{`// 'LastName' is a dynamic-index-field that was indexed from the document -from index 'Products/ByAnyField/JS' where LastName = "Doe" -`} - - - + + To get all documents with a specific _lastName_ use: + + + + ```php + /** @var array $matchingDocuments */ + $matchingDocuments = $session + ->advanced() + ->documentQuery(Product::class, Products_ByAnyField_JS::class) + // 'lastName' is a dynamic-index-field that was indexed from the document + ->whereEquals("lastName", "Doe") + ->toList(); + ``` + + + ```sql + // 'lastName' is a dynamic-index-field that was indexed from the document + from index 'Products/ByAnyField/JS' where lastName = "Doe" + ``` + + + + + + + + + + + +### Example - basic + + +The following example shows: + * Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. + * Documents can then be queried based on those indexed values. + * For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---list) below. + +* **The document**: + + + ```php + class Product + { + public ?string $id = null; + // The VALUE of productType will be dynamically indexed + public ?string $productType = null; + public ?int $pricePerUnit = null; -## Indexing documents fields VALUES + // ... getters and setters + } + ``` + + + + ```json + // Sample document content + { + "productType": "Electronics", + "pricePerUnit": 23 + } + ``` + -## Example - basic +* **The index**: + + The following index will index the **value** of document field 'productType'. + + This value will be the dynamic-index-field name on which you can query. + E.g., Field value `Electronics` will be the dynamic-index-field. - + + + ```php + class Products_ByProductType extends AbstractIndexCreationTask + { + public function __construct() + { + parent::__construct(); + + // Call 'CreateField' to generate the dynamic-index-fields. + // The field name will be the value of document field 'productType'. + // The field terms will be derived from document field 'pricePerUnit'. + $this->map = + "from p in docs.Products " . + "select new { " . + " _ = CreateField(p.productType, p.pricePerUnit) " . + "}"; + } + } + ``` + + + ```php + class Products_ByProductType_JS extends AbstractJavaScriptIndexCreationTask + { + public function __construct() + { + parent::__construct(); + + $this->setMaps([ + "map('Products', function (p) { + return { + // The field name will be the value of document field 'productType' + // The field terms will be derived from document field 'pricePerUnit' + _: createField(p.productType, p.pricePerUnit, null) + }; + })" + ]); + } + } + ``` + + -* Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. -* Documents can then be queried based on those indexed values. +* **The query**: + + To get all documents of some product type having a specific price per unit use: + + + + ```php + /** @var array $matchingDocuments */ + $matchingDocuments = $session + ->advanced() + ->documentQuery(Product::class, Products_ByProductType::class) + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + ->whereEquals("Electronics", 23) + ->toList(); + ``` + + + ```sql + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + from index 'Products/ByProductType' where Electronics = 23 + ``` + + + + + + + +### Example - list + +The following example allows you to: + * Index **values** from items in a list + * After index is deployed, any item added to this list in the document will be dynamically indexed as well. * **The document**: - - -{`class Product -\{ - public ?string $id = null; - - // The VALUE of ProductType will be dynamically indexed - public ?string $productType = null; - public ?int $pricePerUnit = null; - - // ... getters and setters -\} -`} - - + + + ```php + class Product + { + public ?string $id = null; + public ?string $name = null; - - -{`// Sample document content -\{ - "ProductType": "Electronics", - "PricePerUnit": 23 -\} -`} - - + // For each element in this list, + // the VALUE of property 'propName' will be dynamically indexed. + // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields. + public ?AttributeList $attributes = null; + + // ... getters and setters + } + + class Attribute + { + public ?string $propName = null; + public ?string $propValue = null; + + // ... getters and setters + } + + class AttributeList extends TypedList + { + protected function __construct() + { + parent::__construct(Attribute::class); + } + } + ``` + + + + ```json + // Sample document content + { + "name": "SomeName", + "attributes": [ + { + "propName": "Color", + "propValue": "Blue" + }, + { + "propName": "Width", + "propValue": "10" + }, + { + "propName": "Length", + "propValue": "20" + } + // ... + ] + } + ``` + * **The index**: - The below index will index the **value** of document field 'ProductType'. + + The following index will create a dynamic-index-field per item in the document's `attributes` list. + New items added to the attributes list after index creation time will be dynamically indexed as well. - This value will be the dynamic-index-field name on which you can query. - E.g., Field value `Electronics` will be the dynamic-index-field. + The actual dynamic-index-field name on which you can query will be the item's propName **value**. + E.g., 'propName' value `Width` will be a dynamic-index-field. + + + + ```php + class Attributes_ByName extends AbstractIndexCreationTask + { + public function __construct() + { + parent::__construct(); + + // Define the dynamic-index-fields by calling 'CreateField'. + // A dynamic-index-field will be generated for each item in 'attributes'. + // + // For each item, the field name will be the value of field 'propName'. + // The field terms will be derived from field 'propValue'. + $this->map = + "from p in docs.Products " . + "select new { " . + " _ = p.attributes.Select(item => CreateField(item.propName, item.propValue)), " . + " Name = p.name " . + "}"; + } + } + ``` + + + ```php + class Attributes_ByName_JS extends AbstractJavaScriptIndexCreationTask + { + public function __construct() + { + parent::__construct(); + + $this->setMaps([ + "map('Products', function (p) { + return { + // For each item, + // the field name will be the value of field 'propName' + // The field terms will be derived from field 'propValue' + _: p.attributes.map(item => createField( + item.propName, item.propValue, null)), + + // A regular index field can be defined as well: + Name: p.name + }; + })" + ]); + } + } + ``` + + + +* **The query**: + + To get all documents matching a specific attribute property use: + + + + ```php + /** @var array $matchingDocuments */ + $matchingDocuments = $session + ->advanced() + ->documentQuery(Product::class, Attributes_ByName::class) + // 'Width' is a dynamic-index-field that was indexed from the attributes list + ->whereEquals("Width", 10) + ->toList(); + ``` + + + ```sql + // 'Width' is a dynamic-index-field that was indexed from the attributes list + from index 'Attributes/ByName' where Width = 10 + ``` + + + + + + + + + + + +Dynamic fields can be indexed for [Full-text search](../../indexes/querying/searching.mdx) by setting `FieldIndexing::search()` in `CreateFieldOptions`. + +* **At indexing time**: + At indexing time, RavenDB analyzes the dynamic field value and creates the indexed terms. + The analyzer used at indexing time is resolved from the index definition. + You can configure an analyzer for a specific generated field name, or configure `ALL_FIELDS` as a fallback. + See [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) below. + + * If the generated dynamic field name has an explicit analyzer configuration, that analyzer is used. + * Otherwise, if `ALL_FIELDS` has an analyzer configuration, RavenDB uses it as a fallback. + * If no analyzer is configured for the field, RavenDB uses the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + +* **At querying time**: + For full-text search to return the expected results, the analyzer used at indexing time and the analyzer used for the query term should produce compatible terms. + + When a `search()` query targets a dynamic field that does **not** have an explicit analyzer configuration for its generated field name, + RavenDB uses the [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) to analyze the query term by default. + + Set the [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) + configuration key to `true` to make RavenDB use the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) as the fallback analyzer for the query term instead. + + This configuration can be set at different scopes: server-wide, per database, or per index. + The example below sets it at the index scope, in the index definition. + + For a summary of how RavenDB resolves the analyzer used for the query term, + see [Which analyzer is used for the query term](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) below. + + + +--- + +The following example indexes localized product descriptions as dynamic fields and queries one of those generated fields using full-text search: + +* **The document**: + + + ```php + use Ds\Map as DSMap; + + class Product + { + public ?string $id = null; + public ?DSMap $descriptions = null; + + // ... getters and setters + } + ``` + + + + ```json + // Sample document content + { + "descriptions": { + "English": "North wind jacket", + "French": "Veste vent du nord" + } + } + ``` + + +* **The index**: + + The following index creates one dynamic field for each entry in the `descriptions` dictionary. + For example, the key `English` creates the dynamic field `Description_English`. - + Each generated dynamic field is indexed for **full-text search**. + + + + ```php + class Products_ByLocalizedDescription extends AbstractIndexCreationTask + { + private const USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public function __construct() + { + parent::__construct(); + + $this->map = + "from product in docs.Products " . + "select new { " . + " _ = product.descriptions.Select(x => " . + " CreateField(" . + " \"Description_\" + x.Key, " . + " x.Value, " . + " new CreateFieldOptions { " . + // Index each generated dynamic field for FTS + " Indexing = FieldIndexing.Search, " . + " Storage = FieldStorage.No " . + " })) " . + "}"; + + $this->storeAllFields(FieldStorage::no()); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + $this->getConfiguration()[self::USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS] = "true"; + } + } + ``` + + + ```php + class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask + { + private const USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public function __construct() + { + parent::__construct(); + + $this->setMaps([ + "map('Products', function (product) { + return { + _: Object.keys(product.descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.descriptions[key], + { + // Index each generated dynamic field for FTS + indexing: 'Search', + storage: 'No' + }); + }) + }; + })" + ]); + + // Apply a storage default to every generated field + $fields = new IndexFieldOptionsArray(); + $allFieldsOptions = new IndexFieldOptions(); + $allFieldsOptions->setStorage(FieldStorage::no()); + $fields->offsetSet(DocumentsIndexingFields::ALL_FIELDS, $allFieldsOptions); + $this->setFields($fields); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + $this->getConfiguration()[self::USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS] = "true"; + } + } + ``` + + + +* **Full-text search query**: + + Query the generated dynamic field by its field name. + + In this example, the query targets the generated field `Description_English`. + + Since `Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery` is set to `true`, + the searched term `"north wind"` is analyzed with the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + + + + ```php + /** @var array $results */ + $results = $session->advanced() + ->documentQuery(Product::class, Products_ByLocalizedDescription::class) + ->search("Description_English", "north wind") + ->toList(); + ``` + + + + ```sql + from index "Products/ByLocalizedDescription" + where search(Description_English, "north wind") + ``` + + + +--- + + + +### Configuring analyzers for dynamic fields + +`CreateFieldOptions` does **not** let you specify a custom analyzer directly. +However, you can still configure which analyzer RavenDB will use for dynamic fields in the index definition by either: + +* Configuring an analyzer for a specific generated dynamic field name. +* Configuring `ALL_FIELDS` as a fallback for fields that do not have their own explicit analyzer configuration. + +Use `ALL_FIELDS` with care, since it can affect any field that does not have its own explicit analyzer configuration. + +--- + +#### Configure analyzer for a specific dynamic field + + - -{`class Products_ByProductType extends AbstractIndexCreationTask +```php +class Products_ByLocalizedDescription extends AbstractIndexCreationTask { public function __construct() { parent::__construct(); - // Call 'CreateField' to generate the dynamic-index-fields - // The field name will be the value of document field 'ProductType' - // The field terms will be derived from document field 'PricePerUnit' - $this->map = "docs.Products.Select(p => new { " . - " _ = this.CreateField(p.productType, p.pricePerUnit) " . - "})"; + $this->map = + "from product in docs.Products " . + "select new { " . + " _ = product.descriptions.Select(x => " . + " CreateField(" . + " \"Description_\" + x.Key, " . + " x.Value, " . + " new CreateFieldOptions { " . + " Indexing = FieldIndexing.Search, " . + " Storage = FieldStorage.No " . + " })) " . + "}"; + + $this->storeAllFields(FieldStorage::no()); + + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + $this->analyze("Description_English", "StopAnalyzer"); } } -`} - +``` - - -{`class Products_ByProductType_JS extends AbstractJavaScriptIndexCreationTask + +```php +class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { public function __construct() { parent::__construct(); $this->setMaps([ - "map('Products', function (p) { + "map('Products', function (product) { return { - _: createField(p.ProductType, p.PricePerUnit, - { indexing: 'Search', storage: true, termVector: null }) + _: Object.keys(product.descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; })" ]); - } -} -`} - - - - -* **The query**: - * To get all documents of some product type having a specific price per unit use: - - - -{`$matchingDocuments = $session - ->advanced() - ->documentQuery(Product::class, Products_ByProductType::class) -// 'Electronics' is the dynamic-index-field that was indexed from document field 'ProductType' -->whereEquals("Electronics", 23) -->toList(); -`} - - - - -{`// 'Electronics' is the dynamic-index-field that was indexed from document field 'ProductType' -from index 'Products/ByProductType' where Electronics = 23 -`} - - - - -## Example - list - - -* Index **values** from items in a list -* After index is deployed, any item added this list in the document will be dynamically indexed as well. + // Apply a storage default to every generated field + + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + $fields = new IndexFieldOptionsArray(); - + $allFieldsOptions = new IndexFieldOptions(); + $allFieldsOptions->setStorage(FieldStorage::no()); + $fields->offsetSet(DocumentsIndexingFields::ALL_FIELDS, $allFieldsOptions); -* **The document**: - - -{`class Product -\{ - public ?string $id = null; - public ?string $name = null; - - // For each element in this list, the VALUE of property 'PropName' will be dynamically indexed - // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields - public ?AttributeList $attributes = null; - - // ... getters and setters -\} - -class Attribute -\{ - public ?string $propName = null; - public ?string $propValue = null; - - // ... getters and setters -\} - -class AttributeList extends TypedList -\{ - protected function __construct() - \{ - parent::__construct(Attribute::class); - \} -\} -`} - - + $englishFieldOptions = new IndexFieldOptions(); + $englishFieldOptions->setAnalyzer("StopAnalyzer"); + $fields->offsetSet("Description_English", $englishFieldOptions); - - -{`// Sample document content -\{ -Name": "SomeName", -Attributes": [ - \{ - "PropName": "Color", - "PropValue": "Blue" - \}, - \{ - "PropName": "Width", - "PropValue": "10" - \}, - \{ - "PropName": "Length", - "PropValue": "20" - \}, - ... - -\} -`} - + $this->setFields($fields); + } +} +``` + -* **The index**: - The below index will create a dynamic-index-field per item in the document's `Attributes` list. - New items added to the Attributes list after index creation time will be dynamically indexed as well. - - The actual dynamic-index-field name on which you can query will be the item's PropName **value**. - E.g., 'PropName' value `width` will be a dynamic-index-field. +#### Configure a fallback analyzer for all fields - + - -{`class Attributes_ByName extends AbstractIndexCreationTask +```php +class Products_ByLocalizedDescription extends AbstractIndexCreationTask { public function __construct() { parent::__construct(); - // Define the dynamic-index-fields by calling CreateField - // A dynamic-index-field will be generated for each item in the Attributes list + $this->map = + "from product in docs.Products " . + "select new { " . + " _ = product.descriptions.Select(x => " . + " CreateField(" . + " \"Description_\" + x.Key, " . + " x.Value, " . + " new CreateFieldOptions { " . + " Indexing = FieldIndexing.Search, " . + " Storage = FieldStorage.No " . + " })) " . + "}"; - // For each item, the field name will be the value of field 'PropName' - // The field terms will be derived from field 'PropValue' + $this->storeAllFields(FieldStorage::no()); - $this->map = - "docs.Products.Select(p => new { " . - " _ = p.attributes.Select(item => this.CreateField(item.propName, item.propValue)), " . - " Name = p.name " . - "})"; + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + $this->analyze(DocumentsIndexingFields::ALL_FIELDS, "StopAnalyzer"); } } -`} - +``` - - -{`class Attributes_ByName_JS extends AbstractJavaScriptIndexCreationTask + +```php +class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { public function __construct() { parent::__construct(); $this->setMaps([ - "map('Products', function (p) { + "map('Products', function (product) { return { - _: p.Attributes.map(item => createField(item.PropName, item.PropValue, - { indexing: 'Search', storage: true, termVector: null })), - Name: p.Name + _: Object.keys(product.descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; })" ]); + + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + $fields = new IndexFieldOptionsArray(); + $allFieldsOptions = new IndexFieldOptions(); + $allFieldsOptions->setStorage(FieldStorage::no()); + $allFieldsOptions->setAnalyzer("StopAnalyzer"); + $fields->offsetSet(DocumentsIndexingFields::ALL_FIELDS, $allFieldsOptions); + $this->setFields($fields); } } -`} - - - - -* **The query**: - To get all documents matching a specific attribute property use: - - - -{`/** @var array $matchingDocuments */ -$matchingDocuments = $session - ->advanced() - ->documentQuery(Product::class, Attributes_ByName::class) - // 'Width' is a dynamic-index-field that was indexed from the Attributes list - ->whereEquals("Width", 10) - ->toList(); -`} - - - - -{`// 'Width' is a dynamic-index-field that was indexed from the Attributes list -from index 'Attributes/ByName' where Width = 10 -`} - +``` + + + + +### Which analyzer is used for the query term? +The following applies when a `search()` query targets a **dynamic field**: -## CreateField syntax +| Index configuration | [Configuration key](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) | Analyzer used for the query term | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| No analyzer is explicitly configured for the generated field name | `false` | The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) | +| No analyzer is explicitly configured for the generated field name | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `false` | The analyzer resolved by the normal field fallback path,
e.g. `WhitespaceAnalyzer` | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| The generated field name is explicitly configured,
e.g. `$this->analyze("Description_English", "StopAnalyzer")` | `false` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +| The generated field name is explicitly configured,
e.g. `$this->analyze("Description_English", "StopAnalyzer")` | `true` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | - - -{`object CreateField(string name, object value); +
+ +
+ + + +#### Syntax for LINQ-index: + + +```php +object CreateField(string name, object value); object CreateField(string name, object value, bool stored, bool analyzed); object CreateField(string name, object value, CreateFieldOptions options); -`} - +``` -| Parameters | Type | Description | -|------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **name** | `string` | Name of the dynamic-index-field | -| **value** | `object` | Value of the dynamic-index-field
The field Terms are derived from this value. | -| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`False` - will set `FieldStorage.NO` (default value)
`True` - will set `FieldStorate.YES` | -| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`None` - `FieldIndexing.Default` (default value)
`False` - `FieldIndexing.Exact`
`True` - `FieldIndexing.Search` | -| **options** | `CreateFieldOptions` | Dynamic-index-field options | +#### Syntax for JavaScript-index: -| CreateFieldOptions | | | -|--------------------|--------------------|----------------------------------------------------------------------------| -| **Storage** | `FieldStorage` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | -| **Indexing** | `FieldIndexing` | | -| **TermVector** | `FieldTermVector` | | + +```js +createField(fieldName, fieldValue, options); // returns object +``` + + +| Parameters | Type | Description | +|----------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **fieldName** | `string` | Name of the dynamic-index-field | +| **fieldValue** | `object` | Value of the dynamic-index-field
The field terms are derived from this value. | +| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage::no()` (default value)
`true` - will set `FieldStorage::yes()` | +| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing::default()` (default value)
`false` - `FieldIndexing::exact()`
`true` - `FieldIndexing::search()` | +| **options** | `CreateFieldOptions` | Dynamic-index-field options | + +| CreateFieldOptions | | | +|--------------------|--------------------|-----------------------------------------------------------------------------------| +| **Storage** | `FieldStorage?` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | +| **Indexing** | `FieldIndexing?` | Sets the field indexing behavior.
Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | +| **TermVector** | `FieldTermVector?` | Sets the field term-vector behavior.
Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | @@ -544,17 +976,15 @@ object CreateField(string name, object value, CreateFieldOptions options); +
+ + - -## Indexed fields & terms view - -The generated dynamic-index-fields and their indexed terms can be viewed in the **Terms View**. -Below are sample index fields & their terms generated from the last example. - +The generated dynamic index fields and their indexed terms can be viewed in the **Terms View** in Studio. +Below are sample index fields & their terms generated from [this example](../../indexes/using-dynamic-fields.mdx#example---list). + ![Figure 1. Go to terms view](../assets/dynamic-index-fields-1.png) - + ![Figure 2. Indexed fields & terms](../assets/dynamic-index-fields-2.png) - - - - + + diff --git a/docs/indexes/content/_using-dynamic-fields-python.mdx b/docs/indexes/content/_using-dynamic-fields-python.mdx index 69514a29e2..6862d18067 100644 --- a/docs/indexes/content/_using-dynamic-fields-python.mdx +++ b/docs/indexes/content/_using-dynamic-fields-python.mdx @@ -1,490 +1,886 @@ import Admonition from '@theme/Admonition'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -import CodeBlock from '@theme/CodeBlock'; +import ContentFrame from '@site/src/components/ContentFrame'; +import Panel from '@site/src/components/Panel'; -* In RavenDB different documents can have different shapes. - Documents are schemaless - new fields can be added or removed as needed. - -* For such dynamic data, you can define indexes with **dynamic-index-fields**. - -* This allows querying the index on fields that aren't yet known at index creation time, - which is very useful when working on highly dynamic systems. - -* Any value type can be indexed, string, number, date, etc. - -* An index definition can contain both dynamic-index-fields and regular-index-fields. - -* In this page: - - * [Indexing documents fields KEYS](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-keys) - * [Example - index any field under object](../../indexes/using-dynamic-fields.mdx#example---index-any-field-under-object) - * [Example - index any field](../../indexes/using-dynamic-fields.mdx#example---index-any-field) - * [Indexing documents fields VALUES](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-values) - * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) - * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) - * [CreateField syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) - * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields--terms-view) +* In RavenDB, different documents can have different shapes. + Documents are schemaless - new fields can be added or removed as needed. + For such dynamic data, you can define indexes with **dynamic-index-fields**. + +* Dynamic-index-fields are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time, + which is very useful when working with highly dynamic systems. + +* Any value type can be indexed, string, number, date, etc. + An index definition can contain both dynamic-index-fields and regular-index-fields. + +* In this article: + * [Indexing field KEYS](../../indexes/using-dynamic-fields.mdx#indexing-field-keys) + * [Example - index every field under an object](../../indexes/using-dynamic-fields.mdx#example---index-every-field-under-an-object) + * [Example - index every field](../../indexes/using-dynamic-fields.mdx#example---index-every-field) + * [Indexing field VALUES](../../indexes/using-dynamic-fields.mdx#indexing-field-values) + * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) + * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) + * [Indexing dynamic fields for full-text search](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + * [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) + * [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + * [`CreateField` syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) + * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields-terms-view) -## Indexing documents fields KEYS - -## Example - index any field under object + - - -* Index any field that is under the some object from the document. -* After index is deployed, any new field added to the this object will be indexed as well. + + +### Example - index every field under an object + +The following example allows you to: + * Index any field that is under the same object from the document. + * After index is deployed, any new field added to this object will be indexed as well. * **The document**: - - -{`class Product: - def __init__(self, Id: str = None, attributes: Dict[str, object] = None): - self.Id = Id - - # The KEYS under the Attributes object will be dynamically indexed - # Fields added to this object after index creation time will also get indexed - self.attributes = attributes -`} - - - - -{`// Sample document content -\{ - "Attributes": \{ - "Color": "Red", - "Size": 42 - \} -\} -`} - - + + + ```python + class Product: + def __init__(self, Id: str = None, Attributes: Dict[str, object] = None): + self.Id = Id + + # The KEYS under the Attributes object will be dynamically indexed + # Fields added to this object after index creation time will also get indexed + self.Attributes = Attributes + ``` + + + + ```json + // Sample document content + { + "Attributes": { + "Color": "Red", + "Size": 42 + } + } + ``` + * **The index**: - The below index will index any field under the `Attributes` object from the document, + + The following index will index any field under the `Attributes` object from the document, a dynamic-index-field will be created for each such field. New fields added to the object after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the attribute field **key**. - E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. - - - - -{`class Products_ByAttributeKey(AbstractIndexCreationTask): - def __init__(self): - super().__init__() - self.map = ( - "from p in docs.Products select new {" - "_ = p.attributes.Select(item => CreateField(item.Key, item.Value))" - "}" - ) -`} - - - - -{`class Products_ByAttributeKey_JS(AbstractJavaScriptIndexCreationTask): - def __init__(self): - super().__init__() - self.maps = { - """ - map('Products', function (p) { - return { - _: Object.keys(p.attributes).map(key => createField(key, p.attributes[key], - { indexing: 'Search', storage: true, termVector: null })) - }; - }) - """ - } -`} - - - - + The actual dynamic-index-field name on which you can query will be the attribute field **key**. + E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. + + + + ```python + class Products_ByAttributeKey(AbstractIndexCreationTask): + def __init__(self): + super().__init__() + self.map = ( + "from p in docs.Products select new { " + # Call 'CreateField' to generate dynamic-index-fields + # from the Attributes object keys. + + # Using '_' is just a convention. + # Any other string can be used instead of '_' + + # The actual field name will be 'item.Key' + # The actual field terms will be derived from 'item.Value' + "_ = p.Attributes.Select(item => CreateField(item.Key, item.Value)) " + "}" + ) + ``` + + + ```python + class Products_ByAttributeKey_JS(AbstractJavaScriptIndexCreationTask): + def __init__(self): + super().__init__() + self.maps = [ + """ + map('Products', function (p) { + return { + // The field name will be the key + // The field terms will be derived from the corresponding value + _: Object.keys(p.Attributes) + .map(key => createField(key, p.Attributes[key])) + }; + }) + """ + ] + ``` + + * **The query**: - * You can now query the generated dynamic-index fields. - * To get all documents with some 'size' use: - - - -{`matching_documents = list( - session.query_index_type(Products_ByAttributeKey, Product) - # 'size' is a dynamic-index-field that was indexed from the attributes object - .where_equals("size", 42) -) -`} - - - - -{`// 'Size' is a dynamic-index-field that was indexed from the Attributes object -from index 'Products/ByAttributeKey' where Size = 42 -`} - - - - -## Example - index any field - - + + You can now query the generated dynamic-index fields. + The `_` property is Not queryable but used only in the index definition syntax. + + To get all documents with a specific _Size_ use: + + + + ```python + matching_documents = list( + session.advanced + .document_query_from_index_type(Products_ByAttributeKey, Product) + # 'Size' is a dynamic-index-field that was indexed from the Attributes object + .where_equals("Size", 42) + ) + ``` + + + ```sql + // 'Size' is a dynamic-index-field that was indexed from the Attributes object + from index 'Products/ByAttributeKey' where Size = 42 + ``` + + + + + Dynamic index fields are queried with _document_query_ or _RQL_ because the field names (e.g. `Size`, `Color`) + are generated at indexing time and are not properties on the _Product_ class. + + Use the string-based `where_equals("Size", 42)` or RQL `where Size = 42`. + + + + + + +### Example - index every field + +The following example allows you to: * Define an index on a collection **without** needing any common structure between the indexed documents. - * After index is deployed, any new field added to the document will be indexed as well. - + * After the index is deployed, any new field added to any document in the collection will be indexed as well. - + -Consider whether this is really necessary, as indexing every single field can end up costing time and disk space. +* Use this approach with care. +* Indexing every field can increase indexing time, memory usage, and disk space, + especially for large or frequently updated documents. * **The document**: - - -{`class Product: - def __init__(self, Id: str = None, first_name: str = None, last_name: str = None, title: str = None): - self.Id = Id - - # All KEYS in the document will be dynamically indexes - # Fields added to the document after index creation time wil also get indexed - self.first_name = first_name - self.last_name = last_name - self.title = title - # ... -`} - - - - - -{`// Sample document content - \{ + + + ```python + class Product: + def __init__( + self, + Id: str = None, + FirstName: str = None, + LastName: str = None, + Title: str = None, + ): + self.Id = Id + + # All KEYS in the document will be dynamically indexed + # Fields added to the document after index creation time will also get indexed + self.FirstName = FirstName + self.LastName = LastName + self.Title = Title + # ... + ``` + + + + ```json + // Sample document content + { "FirstName": "John", "LastName": "Doe", "Title": "Engineer", // ... -\} -`} - - + } + ``` + * **The index**: - The below index will index any field from the document, + + The following index will index any field from the document, a dynamic-index-field will be created for each field. New fields added to the document after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the field **key**. - E.g., Keys `FirstName` & `LastName` will become the actual dynamic-index-fields. - - - - -{`class Products_ByAnyField_JS(AbstractJavaScriptIndexCreationTask): - def __init__(self): - super().__init__() - # This will index EVERY FIELD under the top level of the document - self.maps = { - """ - map('Products', function (p) { - return { - _: Object.keys(p).map(key => createField(key, p[key], - { indexing: 'Search', storage: true, termVector: null })) - } - }) - """ - } -`} - - - + The actual dynamic-index-field name on which you can query will be the field **key**. + E.g., Keys `FirstName` & `LastName` will become the actual dynamic-index-fields. + + The example uses a JavaScript index because it enumerates all top-level document fields at indexing time with `Object.keys(...)`. + LINQ indexes typically create dynamic fields from a known enumerable structure, such as a dictionary or collection. + + + + ```python + class Products_ByAnyField_JS(AbstractJavaScriptIndexCreationTask): + def __init__(self): + super().__init__() + # This will index EVERY FIELD under the top level of the document + self.maps = [ + """ + map('Products', function (p) { + return { + _: Object.keys(p).map(key => createField(key, p[key])) + }; + }) + """ + ] + ``` + + * **The query**: - * To get all documents with some 'LastName' use: - - - -{`# 'last_name' is a dynamic-index-field that was indexed from the document -matching_documents = list( - session.query_index_type(Products_ByAnyField_JS, Product).where_equals("last_name", "Doe") -) -`} - - - - -{`// 'LastName' is a dynamic-index-field that was indexed from the document -from index 'Products/ByAnyField/JS' where LastName = "Doe" -`} - - - + + To get all documents with a specific _LastName_ use: + + + + ```python + matching_documents = list( + session.advanced + .document_query_from_index_type(Products_ByAnyField_JS, Product) + # 'LastName' is a dynamic-index-field that was indexed from the document + .where_equals("LastName", "Doe") + ) + ``` + + + ```sql + // 'LastName' is a dynamic-index-field that was indexed from the document + from index 'Products/ByAnyField/JS' where LastName = "Doe" + ``` + + + + + + + + + + + +### Example - basic + + +The following example shows: + * Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. + * Documents can then be queried based on those indexed values. + * For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---list) below. + + +* **The document**: + + + ```python + class Product: + def __init__(self, Id: str = None, ProductType: str = None, PricePerUnit: int = None): + self.Id = Id + + # The VALUE of ProductType will be dynamically indexed + self.ProductType = ProductType + self.PricePerUnit = PricePerUnit + ``` + + + + ```json + // Sample document content + { + "ProductType": "Electronics", + "PricePerUnit": 23 + } + ``` + + +* **The index**: + + The following index will index the **value** of document field 'ProductType'. + + This value will be the dynamic-index-field name on which you can query. + E.g., Field value `Electronics` will be the dynamic-index-field. + + + + ```python + class Products_ByProductType(AbstractIndexCreationTask): + def __init__(self): + super().__init__() + self.map = ( + "from p in docs.Products select new { " + # Call 'CreateField' to generate the dynamic-index-fields + # The field name will be the value of document field 'ProductType' + # The field terms will be derived from document field 'PricePerUnit' + "_ = CreateField(p.ProductType, p.PricePerUnit) " + "}" + ) + ``` + + + ```python + class Products_ByProductType_JS(AbstractJavaScriptIndexCreationTask): + def __init__(self): + super().__init__() + self.maps = [ + """ + map('Products', function (p) { + return { + // The field name will be the value of document field 'ProductType' + // The field terms will be derived from document field 'PricePerUnit' + _: createField(p.ProductType, p.PricePerUnit) + }; + }) + """ + ] + ``` + + + +* **The query**: + + To get all documents of some product type having a specific price per unit use: + + + + ```python + matching_documents = list( + session.advanced + .document_query_from_index_type(Products_ByProductType, Product) + # 'Electronics' is the dynamic-index-field that was indexed + # from document field 'ProductType' + .where_equals("Electronics", 23) + ) + ``` + + + ```sql + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'ProductType' + from index 'Products/ByProductType' where Electronics = 23 + ``` + + + + + + + +### Example - list + + +The following example allows you to: + * Index **values** from items in a list + * After index is deployed, any item added to this list in the document will be dynamically indexed as well. + + +* **The document**: + + + ```python + class Attribute: + def __init__(self, PropName: str = None, PropValue: str = None): + self.PropName = PropName + self.PropValue = PropValue + + + class Product: + def __init__(self, Id: str = None, Name: str = None, Attributes: List[Attribute] = None): + self.Id = Id + self.Name = Name + + # For each element in this list, + # the VALUE of property 'PropName' will be dynamically indexed. + # e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields. + self.Attributes = Attributes + ``` + + + + ```json + // Sample document content + { + "Name": "SomeName", + "Attributes": [ + { + "PropName": "Color", + "PropValue": "Blue" + }, + { + "PropName": "Width", + "PropValue": "10" + }, + { + "PropName": "Length", + "PropValue": "20" + }, + // ... + ] + } + ``` + + +* **The index**: + + The following index will create a dynamic-index-field per item in the document's `Attributes` list. + New items added to the Attributes list after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the item's PropName **value**. + E.g., 'PropName' value `Width` will be a dynamic-index-field. + + + + ```python + class Attributes_ByName(AbstractIndexCreationTask): + def __init__(self): + super().__init__() + self.map = ( + "from a in docs.Products select new { " + # Define the dynamic-index-fields by calling 'CreateField' + # A dynamic-index-field will be generated for each item in 'Attributes' + + # For each item, the field name will be the value of field 'PropName' + # The field terms will be derived from field 'PropValue' + "_ = a.Attributes.Select(item => CreateField(item.PropName, item.PropValue)), " + + # A regular index field can be defined as well: + "Name = a.Name " + "}" + ) + ``` + + + ```python + class Attributes_ByName_JS(AbstractJavaScriptIndexCreationTask): + def __init__(self): + super().__init__() + self.maps = [ + """ + map('Products', function (p) { + return { + // For each item, + // the field name will be the value of field 'PropName' + // The field terms will be derived from field 'PropValue' + _: p.Attributes.map(item => createField(item.PropName, item.PropValue)), + + // A regular index field can be defined as well: + Name: p.Name + }; + }) + """ + ] + ``` + + + +* **The query**: + + To get all documents matching a specific attribute property use: + + + + ```python + matching_documents = list( + session.advanced + .document_query_from_index_type(Attributes_ByName, Product) + # 'Width' is a dynamic-index-field that was indexed from the Attributes list + .where_equals("Width", 10) + ) + ``` + + + ```sql + // 'Width' is a dynamic-index-field that was indexed from the Attributes list + from index 'Attributes/ByName' where Width = 10 + ``` + + + + + + + + + + + +Dynamic fields can be indexed for [Full-text search](../../indexes/querying/searching.mdx) by setting `FieldIndexing.Search` in `CreateFieldOptions`. +* **At indexing time**: + At indexing time, RavenDB analyzes the dynamic field value and creates the indexed terms. + The analyzer used at indexing time is resolved from the index definition. + You can configure an analyzer for a specific generated field name, or configure `ALL_FIELDS` as a fallback. + See [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) below. + * If the generated dynamic field name has an explicit analyzer configuration, that analyzer is used. + * Otherwise, if `ALL_FIELDS` has an analyzer configuration, RavenDB uses it as a fallback. + * If no analyzer is configured for the field, RavenDB uses the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). -## Indexing documents fields VALUES +* **At querying time**: + For full-text search to return the expected results, the analyzer used at indexing time and the analyzer used for the query term should produce compatible terms. -## Example - basic + When a `search()` query targets a dynamic field that does **not** have an explicit analyzer configuration for its generated field name, + RavenDB uses the [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) to analyze the query term by default. - + Set the [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) + configuration key to `true` to make RavenDB use the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) as the fallback analyzer for the query term instead. -* Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. -* Documents can then be queried based on those indexed values. + This configuration can be set at different scopes: server-wide, per database, or per index. + The example below sets it at the index scope, in the index definition. + + For a summary of how RavenDB resolves the analyzer used for the query term, + see [Which analyzer is used for the query term](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) below. + +--- + +The following example indexes localized product descriptions as dynamic fields and queries one of those generated fields using full-text search: + +* **The document**: + + + ```python + class Product: + def __init__(self, Id: str = None, Descriptions: Dict[str, str] = None): + self.Id = Id + self.Descriptions = Descriptions + ``` + + + + ```json + // Sample document content + { + "Descriptions": { + "English": "North wind jacket", + "French": "Veste vent du nord" + } + } + ``` + -* **The document**: - - -{`class Product: - def __init__(self, Id: str = None, product_type: str = None, price_per_unit: float = None): - self.Id = Id - - # The VALUE of ProductType will be dynamically indexed - self.product_type = product_type - self.price_per_unit = price_per_unit -`} - - +* **The index**: - - -{`// Sample document content -\{ - "ProductType": "Electronics", - "PricePerUnit": 23 -\} -`} - - + The following index creates one dynamic field for each entry in the `Descriptions` dictionary. + For example, the key `English` creates the dynamic field `Description_English`. -* **The index**: - The below index will index the **value** of document field 'ProductType'. - - This value will be the dynamic-index-field name on which you can query. - E.g., Field value `Electronics` will be the dynamic-index-field. + Each generated dynamic field is indexed for **full-text search**. + + + + ```python + class Products_ByLocalizedDescription(AbstractIndexCreationTask): + USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = ( + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery" + ) + + def __init__(self): + super().__init__() + self.map = ( + "from product in docs.Products select new { " + " _ = product.Descriptions.Select(x => " + " CreateField(" + " \"Description_\" + x.Key, " + " x.Value, " + # Index each generated dynamic field for FTS + " new CreateFieldOptions { " + " Indexing = FieldIndexing.Search, " + " Storage = FieldStorage.No " + " })) " + "}" + ) + + self._store_all_fields(FieldStorage.NO) + + # Set this configuration key to true so that search() queries on dynamic fields + # will use the 'Default Search Analyzer' when the generated field name + # has no explicit analyzer configuration. + self.configuration = { + Products_ByLocalizedDescription.USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS: "true" + } + ``` + + + ```python + class Products_ByLocalizedDescription_JS(AbstractJavaScriptIndexCreationTask): + USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = ( + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery" + ) + + def __init__(self): + super().__init__() + self.maps = [ + """ + map('Products', function (product) { + return { + _: Object.keys(product.Descriptions).map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + // Index each generated dynamic field for FTS + indexing: 'Search', + storage: 'No' + }); + }) + }; + }) + """ + ] + + # Apply a storage default to every generated field + self.fields = { + constants.Documents.Indexing.Fields.ALL_FIELDS: + IndexFieldOptions(storage=FieldStorage.NO) + } + + # Set this configuration key to true so that search() queries on dynamic fields + # will use the 'Default Search Analyzer' when the generated field name + # has no explicit analyzer configuration. + self.configuration = { + Products_ByLocalizedDescription_JS.USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS: "true" + } + ``` + + + +* **Full-text search query**: + + Query the generated dynamic field by its field name. + + In this example, the query targets the generated field `Description_English`. + + Since `Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery` is set to `true`, + the searched term `"north wind"` is analyzed with the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + + + + ```python + results = list( + session.advanced + .document_query_from_index_type(Products_ByLocalizedDescription, Product) + .search("Description_English", "north wind") + ) + ``` + + + + ```sql + from index "Products/ByLocalizedDescription" + where search(Description_English, "north wind") + ``` + + + +--- + + + +### Configuring analyzers for dynamic fields + +`CreateFieldOptions` does **not** let you specify a custom analyzer directly. +However, you can still configure which analyzer RavenDB will use for dynamic fields in the index definition by either: + +* Configuring an analyzer for a specific generated dynamic field name. +* Configuring `ALL_FIELDS` as a fallback for fields that do not have their own explicit analyzer configuration. + +Use `ALL_FIELDS` with care, since it can affect any field that does not have its own explicit analyzer configuration. + +--- + +#### Configure analyzer for a specific dynamic field - -{`class Products_ByProductType(AbstractIndexCreationTask): +```python +class Products_ByLocalizedDescription(AbstractIndexCreationTask): def __init__(self): super().__init__() + self.map = ( + "from product in docs.Products select new { " + " _ = product.Descriptions.Select(x => " + " CreateField(" + " \"Description_\" + x.Key, " + " x.Value, " + " new CreateFieldOptions { " + " Indexing = FieldIndexing.Search, " + " Storage = FieldStorage.No " + " })) " + "}" + ) - # Call 'CreateField' to generate the dynamic-index-fields - # The field name will be the value of document field 'product_type' - # The field terms will be derived from document field 'price_per_unit' - self.map = "from p in docs.Products select new { _ = CreateField(p.product_type, p.price_per_unit)}" -`} - + self._store_all_fields(FieldStorage.NO) + + # Configure an analyzer for a known generated dynamic field name + # ============================================================== + self._analyze("Description_English", "StopAnalyzer") +``` - - -{`class Products_ByProductType_JS(AbstractJavaScriptIndexCreationTask): + +```python +class Products_ByLocalizedDescription_JS(AbstractJavaScriptIndexCreationTask): def __init__(self): super().__init__() - self.maps = { + self.maps = [ """ - map('Products', function (p) { + map('Products', function (product) { return { - _: createField(p.product_type, p.price_per_unit, - { indexing: 'Search', storage: true, termVector: null }) + _: Object.keys(product.Descriptions).map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; }) """ + ] + + # Configure an analyzer for a known generated dynamic field name + # ============================================================== + self.fields = { + constants.Documents.Indexing.Fields.ALL_FIELDS: + IndexFieldOptions(storage=FieldStorage.NO), + "Description_English": + IndexFieldOptions(analyzer="StopAnalyzer") } -`} - - - - -* **The query**: - * To get all documents of some product type having a specific price per unit use: - - - -{`# 'electronics' is the dynamic-index-field that was indexed from the document 'product_type' -matching_documents = list( - session.advanced.document_query_from_index_type(Products_ByProductType, Product).where_equals( - "electronics", 23 - ) -) -`} - - - - -{`// 'Electronics' is the dynamic-index-field that was indexed from document field 'ProductType' -from index 'Products/ByProductType' where Electronics = 23 -`} - +``` -## Example - list - - - -* Index **values** from items in a list -* After index is deployed, any item added this list in the document will be dynamically indexed as well. - - - -* **The document**: - - -{`class Attribute: - def __init__(self, prop_name: str = None, prop_value: str = None): - self.prop_name = prop_name - self.prop_value = prop_value - - -class Product: - def __init__(self, Id: str = None, name: str = None, attributes: List[Attribute] = None): - self.Id = Id - self.name = name - # For each element in this list, the VALUE of property 'prop_name' will be dynamically indexed - # e.g. color, width, length (in ex. below) will become dynamic-index-field - self.attributes = attributes -`} - - - - - -{`// Sample document content -\{ -Name": "SomeName", -Attributes": [ - \{ - "PropName": "Color", - "PropValue": "Blue" - \}, - \{ - "PropName": "Width", - "PropValue": "10" - \}, - \{ - "PropName": "Length", - "PropValue": "20" - \}, - ... - -\} -`} - - - -* **The index**: - The below index will create a dynamic-index-field per item in the document's `Attributes` list. - New items added to the Attributes list after index creation time will be dynamically indexed as well. - - The actual dynamic-index-field name on which you can query will be the item's PropName **value**. - E.g., 'PropName' value `width` will be a dynamic-index-field. +#### Configure a fallback analyzer for all fields - -{`class Attributes_ByName(AbstractIndexCreationTask): +```python +class Products_ByLocalizedDescription(AbstractIndexCreationTask): def __init__(self): super().__init__() self.map = ( - "from a in docs.Products select new " - "{ _ = a.attributes.Select( item => CreateField(item.prop_name, item.prop_value)), name = a.name " + "from product in docs.Products select new { " + " _ = product.Descriptions.Select(x => " + " CreateField(" + " \"Description_\" + x.Key, " + " x.Value, " + " new CreateFieldOptions { " + " Indexing = FieldIndexing.Search, " + " Storage = FieldStorage.No " + " })) " "}" ) -`} - + + self._store_all_fields(FieldStorage.NO) + + # Use this as a fallback for fields that do not have + # their own explicit analyzer configuration + # ================================================== + self._analyze(constants.Documents.Indexing.Fields.ALL_FIELDS, "StopAnalyzer") +``` - - -{`class Attributes_ByName_JS(AbstractJavaScriptIndexCreationTask): + +```python +class Products_ByLocalizedDescription_JS(AbstractJavaScriptIndexCreationTask): def __init__(self): super().__init__() - self.maps = { + self.maps = [ """ - map('Products', function (p) { + map('Products', function (product) { return { - _: p.Attributes.map(item => createField(item.PropName, item.PropValue, - { indexing: 'Search', storage: true, termVector: null })), - Name: p.Name + _: Object.keys(product.Descriptions).map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; }) """ + ] + + # Use this as a fallback for fields that do not have + # their own explicit analyzer configuration + # ================================================== + self.fields = { + constants.Documents.Indexing.Fields.ALL_FIELDS: + IndexFieldOptions(storage=FieldStorage.NO, analyzer="StopAnalyzer") } -`} - - - - -* **The query**: - To get all documents matching a specific attribute property use: - - - -{`matching_documents = list( - session.advanced.document_query_from_index_type(Attributes_ByName, Product).where_equals( - "width", 10 - ) -) -`} - - - - -{`// 'Width' is a dynamic-index-field that was indexed from the Attributes list -from index 'Attributes/ByName' where Width = 10 -`} - +``` + + + + +### Which analyzer is used for the query term? + +The following applies when a `search()` query targets a **dynamic field**: + +| Index configuration | [Configuration key](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) | Analyzer used for the query term | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| No analyzer is explicitly configured for the generated field name | `false` | The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) | +| No analyzer is explicitly configured for the generated field name | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `false` | The analyzer resolved by the normal field fallback path,
e.g. `WhitespaceAnalyzer` | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| The generated field name is explicitly configured,
e.g. `self._analyze("Description_English", "StopAnalyzer")` | `false` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +| The generated field name is explicitly configured,
e.g. `self._analyze("Description_English", "StopAnalyzer")` | `true` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | + +
+
-## CreateField syntax + #### Syntax for Index: - - -{`object CreateField(string name, object value); + +```python +CreateField(name, value) -object CreateField(string name, object value, bool stored, bool analyzed); +CreateField(name, value, stored, analyzed) -object CreateField(string name, object value, CreateFieldOptions options); -`} - +CreateField(name, value, CreateFieldOptions) +``` #### Syntax for JavaScript-index: - - -{`createField(fieldName, fieldValue, options); // returns object -`} - + +```js +createField(fieldName, fieldValue, options); // returns object +``` -| Parameters | Type | Description | -|------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **name** | `string` | Name of the dynamic-index-field | -| **value** | `object` | Value of the dynamic-index-field
The field Terms are derived from this value. | -| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`False` - will set `FieldStorage.NO` (default value)
`True` - will set `FieldStorate.YES` | -| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`None` - `FieldIndexing.Default` (default value)
`False` - `FieldIndexing.Exact`
`True` - `FieldIndexing.Search` | -| **options** | `CreateFieldOptions` | Dynamic-index-field options | +| Parameters | Type | Description | +|----------------|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **fieldName** | `string` | Name of the dynamic-index-field | +| **fieldValue** | `object` | Value of the dynamic-index-field
The field terms are derived from this value. | +| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorage.Yes` | +| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing.Default` (default value)
`false` - `FieldIndexing.Exact`
`true` - `FieldIndexing.Search` | +| **options** | `CreateFieldOptions` | Dynamic-index-field options | -| CreateFieldOptions | | | -|--------------------|--------------------|----------------------------------------------------------------------------| +| CreateFieldOptions | | | +|--------------------|--------------------|-----------------------------------------------------------------------------------| | **Storage** | `FieldStorage?` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | -| **Indexing** | `FieldIndexing?` | Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | -| **TermVector** | `FieldTermVector?` | Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | +| **Indexing** | `FieldIndexing?` | Sets the field indexing behavior.
Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | +| **TermVector** | `FieldTermVector?` | Sets the field term-vector behavior.
Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | @@ -496,17 +892,15 @@ object CreateField(string name, object value, CreateFieldOptions options); +
+ + - -## Indexed fields & terms view - -The generated dynamic-index-fields and their indexed terms can be viewed in the **Terms View**. -Below are sample index fields & their terms generated from the last example. +The generated dynamic index fields and their indexed terms can be viewed in the **Terms View** in Studio. +Below are sample index fields & their terms generated from [this example](../../indexes/using-dynamic-fields.mdx#example---list). ![Figure 1. Go to terms view](../assets/dynamic-index-fields-1.png) ![Figure 2. Indexed fields & terms](../assets/dynamic-index-fields-2.png) - - - - + + diff --git a/docs/indexes/querying/content/_searching-csharp.mdx b/docs/indexes/querying/content/_searching-csharp.mdx index d20ef5a65e..f0be0e6ffd 100644 --- a/docs/indexes/querying/content/_searching-csharp.mdx +++ b/docs/indexes/querying/content/_searching-csharp.mdx @@ -16,11 +16,13 @@ import CodeBlock from '@theme/CodeBlock'; See examples below. * You can configure which analyzer will be used to tokenize this field. - See [selecting an analyzer](../../../indexes/using-analyzers.mdx#selecting-an-analyzer-for-a-field). + Learn more in [Setting analyzer for index-field](../../../indexes/using-analyzers.mdx#setting-analyzer-for-index-field). + * In this article: * [Indexing single field for FTS](../../../indexes/querying/searching.mdx#indexing-single-field-for-fts) * [Indexing multiple fields for FTS](../../../indexes/querying/searching.mdx#indexing-multiple-fields-for-fts) - * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-(using-asjson)) + * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-using-asjson) + * [Indexing dynamic fields for FTS](../../../indexes/querying/searching.mdx#indexing-dynamic-fields-for-fts) * [Boosting search results](../../../indexes/querying/searching.mdx#boosting-search-results) * [Searching with wildcards](../../../indexes/querying/searching.mdx#searching-with-wildcards) * [When using RavenStandardAnalyzer or StandardAnalyzer or NGramAnalyzer](../../../indexes/querying/searching.mdx#when-usingoror) @@ -28,6 +30,7 @@ import CodeBlock from '@theme/CodeBlock'; * [When using the Exact analyzer](../../../indexes/querying/searching.mdx#when-using-the-exact-analyzer) + ## Indexing single field for FTS #### The index: @@ -262,7 +265,7 @@ where (search(EmployeeData, "Manager") or search(EmployeeData, "French Spanish", ## Indexing all fields for FTS (using AsJson) -* To search across ALL fields in a document without defining each one explicitly, +* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method in the _Map_ function to extract all property values and index them in a single searchable field. * This approach makes the index robust to changes in the document schema. @@ -377,7 +380,14 @@ where search(AllValues, "tofu") +## Indexing dynamic fields for FTS + +* [Dynamic index fields](../../../indexes/using-dynamic-fields.mdx) are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time. +* Dynamic index fields can be configured for full-text search just like regular index fields. + Learn more in [Indexing dynamic fields for full-text search](../../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search). ## Boosting search results diff --git a/docs/indexes/querying/content/_searching-java.mdx b/docs/indexes/querying/content/_searching-java.mdx index 7d85246ced..4accf3f512 100644 --- a/docs/indexes/querying/content/_searching-java.mdx +++ b/docs/indexes/querying/content/_searching-java.mdx @@ -388,4 +388,11 @@ where search(allValues, "tofu") +## Indexing dynamic fields for FTS +* [Dynamic index fields](../../../indexes/using-dynamic-fields.mdx) are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time. + +* Dynamic index fields can be configured for full-text search just like regular index fields. + Learn more in [Indexing dynamic fields for full-text search](../../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search). \ No newline at end of file diff --git a/docs/indexes/querying/content/_searching-nodejs.mdx b/docs/indexes/querying/content/_searching-nodejs.mdx index 4ca66b5877..cb73bfd7c0 100644 --- a/docs/indexes/querying/content/_searching-nodejs.mdx +++ b/docs/indexes/querying/content/_searching-nodejs.mdx @@ -5,7 +5,7 @@ import CodeBlock from '@theme/CodeBlock'; -* Prior to reading this article, please refer to [full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) +* Prior to reading this article, please refer to [Full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) to learn about the `search` method. * **All capabilities** provided by `search` with a dynamic query can also be used when querying a static-index. @@ -17,11 +17,13 @@ import CodeBlock from '@theme/CodeBlock'; See examples below. * You can configure which analyzer will be used to tokenize this field. - See [selecting an analyzer](../../../indexes/using-analyzers.mdx#selecting-an-analyzer-for-a-field). + Learn more in [Setting analyzer for index-field](../../../indexes/using-analyzers.mdx#setting-analyzer-for-index-field). + * In this article: * [Indexing single field for FTS](../../../indexes/querying/searching.mdx#indexing-single-field-for-fts) * [Indexing multiple fields for FTS](../../../indexes/querying/searching.mdx#indexing-multiple-fields-for-fts) - * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-(using-asjson)) + * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-using-asjson) + * [Indexing dynamic fields for FTS](../../../indexes/querying/searching.mdx#indexing-dynamic-fields-for-fts) * [Boosting search results](../../../indexes/querying/searching.mdx#boosting-search-results) * [Searching with wildcards](../../../indexes/querying/searching.mdx#searching-with-wildcards) * [When using RavenStandardAnalyzer or StandardAnalyzer or NGramAnalyzer](../../../indexes/querying/searching.mdx#when-usingoror) @@ -29,6 +31,7 @@ import CodeBlock from '@theme/CodeBlock'; * [When using the Exact analyzer](../../../indexes/querying/searching.mdx#when-using-the-exact-analyzer) + ## Indexing single field for FTS #### The index: @@ -163,7 +166,7 @@ where (search(employeeData, "Manager") or search(employeeData, "French Spanish", ## Indexing all fields for FTS (using AsJson) -* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, +* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, which is available when using **a C# LINQ string** that is assigned to the `map` property in the Node.js index class, as shown in the example below. @@ -229,6 +232,14 @@ where search(AllValues, "tofu") +## Indexing dynamic fields for FTS + +* [Dynamic index fields](../../../indexes/using-dynamic-fields.mdx) are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time. + +* Dynamic index fields can be configured for full-text search just like regular index fields. + Learn more in [Indexing dynamic fields for full-text search](../../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search). ## Boosting search results diff --git a/docs/indexes/querying/content/_searching-php.mdx b/docs/indexes/querying/content/_searching-php.mdx index 55261ce19f..510a6f8752 100644 --- a/docs/indexes/querying/content/_searching-php.mdx +++ b/docs/indexes/querying/content/_searching-php.mdx @@ -5,7 +5,7 @@ import CodeBlock from '@theme/CodeBlock'; -* Prior to reading this article, please refer to [full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) +* Prior to reading this article, please refer to [Full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) to learn about the `search` method. * **All capabilities** provided by `search` with a dynamic query can also be used when querying a static-index. @@ -17,14 +17,17 @@ import CodeBlock from '@theme/CodeBlock'; See examples below. * You can configure which analyzer will be used to tokenize this field. - See [selecting an analyzer](../../../indexes/using-analyzers.mdx#selecting-an-analyzer-for-a-field). + Learn more in [Setting analyzer for index-field](../../../indexes/using-analyzers.mdx#setting-analyzer-for-index-field). + * In this article: * [Indexing single field for FTS](../../../indexes/querying/searching.mdx#indexing-single-field-for-fts) * [Indexing multiple fields for FTS](../../../indexes/querying/searching.mdx#indexing-multiple-fields-for-fts) - * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-(using-asjson)) + * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-using-asjson) + * [Indexing dynamic fields for FTS](../../../indexes/querying/searching.mdx#indexing-dynamic-fields-for-fts) * [Boosting search results](../../../indexes/querying/searching.mdx#boosting-search-results) + ## Indexing single field for FTS #### The index: @@ -253,7 +256,7 @@ where (search(EmployeeData, "Manager") or search(EmployeeData, "French Spanish", ## Indexing all fields for FTS (using AsJson) -* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, +* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, which is available in the **C# LINQ string** that is assigned to the `map` property in the PHP index class, as shown in the example below. @@ -332,6 +335,15 @@ where search(allValues, "tofu") +## Indexing dynamic fields for FTS + +* [Dynamic index fields](../../../indexes/using-dynamic-fields.mdx) are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time. + +* Dynamic index fields can be configured for full-text search just like regular index fields. + Learn more in [Indexing dynamic fields for full-text search](../../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search). + ## Boosting search results diff --git a/docs/indexes/querying/content/_searching-python.mdx b/docs/indexes/querying/content/_searching-python.mdx index f06c29d94f..88c6aa452f 100644 --- a/docs/indexes/querying/content/_searching-python.mdx +++ b/docs/indexes/querying/content/_searching-python.mdx @@ -5,7 +5,7 @@ import CodeBlock from '@theme/CodeBlock'; -* Prior to reading this article, please refer to [full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) +* Prior to reading this article, please refer to [Full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) to learn about the `search` method. * **All capabilities** provided by `search` with a dynamic query can also be used when querying a static-index. @@ -17,14 +17,17 @@ import CodeBlock from '@theme/CodeBlock'; See examples below. * You can configure which analyzer will be used to tokenize this field. - See [selecting an analyzer](../../../indexes/using-analyzers.mdx#selecting-an-analyzer-for-a-field). + Learn more in [Setting analyzer for index-field](../../../indexes/using-analyzers.mdx#setting-analyzer-for-index-field). + * In this article: * [Indexing single field for FTS](../../../indexes/querying/searching.mdx#indexing-single-field-for-fts) * [Indexing multiple fields for FTS](../../../indexes/querying/searching.mdx#indexing-multiple-fields-for-fts) - * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-(using-asjson)) + * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-using-asjson) + * [Indexing dynamic fields for FTS](../../../indexes/querying/searching.mdx#indexing-dynamic-fields-for-fts) * [Boosting search results](../../../indexes/querying/searching.mdx#boosting-search-results) + ## Indexing single field for FTS #### The index: @@ -167,7 +170,7 @@ where (search(EmployeeData, "Manager") or search(EmployeeData, "French Spanish", ## Indexing all fields for FTS (using AsJson) -* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, +* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, which is available in the **C# LINQ string** that is assigned to the `map` property in the Python index class, as shown in the example below. @@ -238,6 +241,14 @@ where search(all_values, "tofu") +## Indexing dynamic fields for FTS + +* [Dynamic index fields](../../../indexes/using-dynamic-fields.mdx) are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time. + +* Dynamic index fields can be configured for full-text search just like regular index fields. + Learn more in [Indexing dynamic fields for full-text search](../../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search). ## Boosting search results diff --git a/docs/indexes/using-dynamic-fields.mdx b/docs/indexes/using-dynamic-fields.mdx index 19b5239557..e465ba5431 100644 --- a/docs/indexes/using-dynamic-fields.mdx +++ b/docs/indexes/using-dynamic-fields.mdx @@ -1,6 +1,6 @@ --- -title: "Indexes: Dynamic Index Fields" -sidebar_label: Dynamic Fields +title: "Dynamic Index Fields" +sidebar_label: "Dynamic Index Fields" description: "Index document fields dynamically in RavenDB when field names are unknown at index definition time, using the CreateField method." sidebar_position: 27 supported_languages: ["csharp", "java", "python", "php", "nodejs"] @@ -32,7 +32,6 @@ import UsingDynamicFieldsPython from './content/_using-dynamic-fields-python.mdx import UsingDynamicFieldsPhp from './content/_using-dynamic-fields-php.mdx'; import UsingDynamicFieldsNodejs from './content/_using-dynamic-fields-nodejs.mdx'; - diff --git a/docs/server/configuration/indexing-configuration.mdx b/docs/server/configuration/indexing-configuration.mdx index 132bf987f5..0abdfc4f72 100644 --- a/docs/server/configuration/indexing-configuration.mdx +++ b/docs/server/configuration/indexing-configuration.mdx @@ -104,6 +104,7 @@ import LanguageContent from "@site/src/components/LanguageContent"; [Indexing.OrderByTicksAutomaticallyWhenDatesAreInvolved](../../server/configuration/indexing-configuration.mdx#indexingorderbyticksautomaticallywhendatesareinvolved) [Indexing.QueryClauseCache.Disabled](../../server/configuration/indexing-configuration.mdx#indexingqueryclausecachedisabled) [Indexing.QueryClauseCache.RepeatedQueriesTimeFrameInSec](../../server/configuration/indexing-configuration.mdx#indexingqueryclausecacherepeatedqueriestimeframeinsec) + [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) [Indexing.ScratchSpaceLimitInMb](../../server/configuration/indexing-configuration.mdx#indexingscratchspacelimitinmb) [Indexing.Static.SearchEngineType](../../server/configuration/indexing-configuration.mdx#indexingstaticsearchenginetype) [Indexing.Throttling.TimeIntervalInMs](../../server/configuration/indexing-configuration.mdx#indexingthrottlingtimeintervalinms) @@ -1009,6 +1010,33 @@ Queries that repeat within this time frame will be considered worth caching. +## Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery + +* This setting controls which analyzer is used for the **query term** when a **`search()` query** targets a [Dynamic index field](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + that was indexed for full-text search, and no analyzer is explicitly configured for that field in the index definition. + + * `false` (default): + The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) is used to analyze the **query term**. + This analyzer can be configured via the [Indexing.Analyzers.Default](../../server/configuration/indexing-configuration.mdx#indexinganalyzersdefault) configuration key. + + * `true`: + The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) is used to analyze the **query term**. + This analyzer can be configured via the [Indexing.Analyzers.Search.Default](../../server/configuration/indexing-configuration.mdx#indexinganalyzerssearchdefault) configuration key. + +* See the table in [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + for the complete analyzer-selection rules based on this configuration key and the analyzer configured for the dynamic index field. + +* This configuration applies to both the Lucene and Corax search engines. + The default is `false` to preserve backward compatibility. + +--- + +- **Type**: `bool` +- **Default**: `false` +- **Scope**: Server-wide, or per database, or per index + + + ## Indexing.ScratchSpaceLimitInMb * Amount of scratch space in megabytes that we allow to use for the index storage. From 452c39ebf56ec2ed93ff62358d771cb2318c07a4 Mon Sep 17 00:00:00 2001 From: Danielle9897 Date: Sun, 17 May 2026 13:49:55 +0300 Subject: [PATCH 2/2] RDoc-3826 Dynamic fields (v7.1, v7.0, v6.2) --- .../indexes/assets/dynamic-index-fields-1.png | Bin 26390 -> 51136 bytes .../indexes/assets/dynamic-index-fields-2.png | Bin 43570 -> 67121 bytes .../content/_using-analyzers-csharp.mdx | 2 +- .../content/_using-analyzers-nodejs.mdx | 2 +- .../content/_using-dynamic-fields-csharp.mdx | 1210 ++++++++++------ .../content/_using-dynamic-fields-java.mdx | 1191 ++++++++++----- .../content/_using-dynamic-fields-nodejs.mdx | 1113 ++++++++------ .../content/_using-dynamic-fields-php.mdx | 1276 +++++++++++------ .../content/_using-dynamic-fields-python.mdx | 1180 ++++++++++----- .../querying/content/_searching-csharp.mdx | 16 +- .../querying/content/_searching-java.mdx | 7 + .../querying/content/_searching-nodejs.mdx | 19 +- .../querying/content/_searching-php.mdx | 20 +- .../querying/content/_searching-python.mdx | 19 +- .../indexes/using-dynamic-fields.mdx | 8 +- .../configuration/indexing-configuration.mdx | 28 + .../indexes/assets/dynamic-index-fields-1.png | Bin 26390 -> 51136 bytes .../indexes/assets/dynamic-index-fields-2.png | Bin 43570 -> 67121 bytes .../content/_using-analyzers-csharp.mdx | 2 +- .../content/_using-analyzers-nodejs.mdx | 2 +- .../content/_using-dynamic-fields-csharp.mdx | 1210 ++++++++++------ .../content/_using-dynamic-fields-java.mdx | 1191 ++++++++++----- .../content/_using-dynamic-fields-nodejs.mdx | 1113 ++++++++------ .../content/_using-dynamic-fields-php.mdx | 1276 +++++++++++------ .../content/_using-dynamic-fields-python.mdx | 1180 ++++++++++----- .../querying/content/_searching-csharp.mdx | 16 +- .../querying/content/_searching-java.mdx | 7 + .../querying/content/_searching-nodejs.mdx | 19 +- .../querying/content/_searching-php.mdx | 20 +- .../querying/content/_searching-python.mdx | 19 +- .../indexes/using-dynamic-fields.mdx | 8 +- .../configuration/indexing-configuration.mdx | 28 + .../indexes/assets/dynamic-index-fields-1.png | Bin 26390 -> 51136 bytes .../indexes/assets/dynamic-index-fields-2.png | Bin 43570 -> 67121 bytes .../content/_using-analyzers-csharp.mdx | 2 +- .../content/_using-analyzers-nodejs.mdx | 2 +- .../content/_using-dynamic-fields-csharp.mdx | 1210 ++++++++++------ .../content/_using-dynamic-fields-java.mdx | 1191 ++++++++++----- .../content/_using-dynamic-fields-nodejs.mdx | 1113 ++++++++------ .../content/_using-dynamic-fields-php.mdx | 1276 +++++++++++------ .../content/_using-dynamic-fields-python.mdx | 1180 ++++++++++----- .../querying/content/_searching-csharp.mdx | 16 +- .../querying/content/_searching-java.mdx | 7 + .../querying/content/_searching-nodejs.mdx | 19 +- .../querying/content/_searching-php.mdx | 20 +- .../querying/content/_searching-python.mdx | 19 +- .../indexes/using-dynamic-fields.mdx | 6 +- .../configuration/indexing-configuration.mdx | 28 + 48 files changed, 12002 insertions(+), 6269 deletions(-) diff --git a/versioned_docs/version-6.2/indexes/assets/dynamic-index-fields-1.png b/versioned_docs/version-6.2/indexes/assets/dynamic-index-fields-1.png index b13d9076f89e3363e97e2a4a6c01aecc4d101b67..a20adda0ece521b8d27172708c0a11aeea13f887 100644 GIT binary patch literal 51136 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 literal 26390 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 diff --git a/versioned_docs/version-6.2/indexes/assets/dynamic-index-fields-2.png b/versioned_docs/version-6.2/indexes/assets/dynamic-index-fields-2.png index dd2b4082eaa04bd406f9fac609adccde69e21b42..c5b1bc14bca3b86dd281347ae4c56e6545ff1bba 100644 GIT binary patch literal 67121 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%0Dmpj<&g2>Q& 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 X>}(5)fwv}4d^z}m?)_4g$D#iV5#_1q literal 43570 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

MYQ6EJKjSRtlshL!QySY5<Da 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 -* In RavenDB different documents can have different shapes. - Documents are schemaless - new fields can be added or removed as needed. - -* For such dynamic data, you can define indexes with **dynamic-index-fields**. - -* This allows querying the index on fields that aren't yet known at index creation time, - which is very useful when working on highly dynamic systems. - -* Any value type can be indexed, string, number, date, etc. - -* An index definition can contain both dynamic-index-fields and regular-index-fields. - -* In this page: - - * [Indexing documents fields KEYS](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-keys) - * [Example - index any field under object](../../indexes/using-dynamic-fields.mdx#example---index-any-field-under-object) - * [Example - index any field](../../indexes/using-dynamic-fields.mdx#example---index-any-field) - * [Indexing documents fields VALUES](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-values) - * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) - * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) - * [CreateField syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) - * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields--terms-view) +* In RavenDB, different documents can have different shapes. + Documents are schemaless - new fields can be added or removed as needed. + For such dynamic data, you can define indexes with **dynamic-index-fields**. + +* Dynamic-index-fields are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time, + which is very useful when working with highly dynamic systems. + +* Any value type can be indexed, string, number, date, etc. + An index definition can contain both dynamic-index-fields and regular-index-fields. + +* In this article: + * [Indexing field KEYS](../../indexes/using-dynamic-fields.mdx#indexing-field-keys) + * [Example - index every field under an object](../../indexes/using-dynamic-fields.mdx#example---index-every-field-under-an-object) + * [Example - index every field](../../indexes/using-dynamic-fields.mdx#example---index-every-field) + * [Indexing field VALUES](../../indexes/using-dynamic-fields.mdx#indexing-field-values) + * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) + * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) + * [Indexing dynamic fields for full-text search](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + * [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) + * [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + * [`CreateField` syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) + * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields-terms-view) -## Indexing documents fields KEYS - -## Example - index any field under object - - + -* Index any field that is under the some object from the document. -* After index is deployed, any new field added to the this object will be indexed as well. + + +### Example - index every field under an object + +The following example allows you to: + * Index any field that is under the same object from the document. + * After index is deployed, any new field added to this object will be indexed as well. * **The document**: - - -{`public class Product -\{ - public string Id \{ get; set; \} - - // The KEYS under the Attributes object will be dynamically indexed - // Fields added to this object after index creation time will also get indexed - public Dictionary Attributes \{ get; set; \} -\} -`} - - - - -{`// Sample document content -\{ - "Attributes": \{ - "Color": "Red", - "Size": 42 - \} -\} -`} - - + + + ```csharp + public class Product + { + public string Id { get; set; } + + // The KEYS under the Attributes object will be dynamically indexed + // Fields added to this object after index creation time will also get indexed + public Dictionary Attributes { get; set; } + } + ``` + + + + ```json + // Sample document content + { + "Attributes": { + "Color": "Red", + "Size": 42 + } + } + ``` + * **The index**: - The below index will index any field under the `Attributes` object from the document, + + The following index will index any field under the `Attributes` object from the document, a dynamic-index-field will be created for each such field. New fields added to the object after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the attribute field **key**. - E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. + The actual dynamic-index-field name on which you can query will be the attribute field **key**. + E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. - - - -{`public class Products_ByAttributeKey : AbstractIndexCreationTask -{ - public Products_ByAttributeKey() + + + ```csharp + public class Products_ByAttributeKey : AbstractIndexCreationTask { - Map = products => from p in products - select new - { - // Call 'CreateField' to generate dynamic-index-fields from the Attributes object keys - // Using '_' is just a convention. Any other string can be used instead of '_' - - // The actual field name will be item.Key - // The actual field terms will be derived from item.Value - _ = p.Attributes.Select(item => CreateField(item.Key, item.Value)) - }; + public Products_ByAttributeKey() + { + Map = products => from p in products + select new + { + // Call 'CreateField' to generate dynamic-index-fields + // from the Attributes object keys. + + // Using '_' is just a convention. + // Any other string can be used instead of '_' + + // The actual field name will be 'item.Key' + // The actual field terms will be derived from 'item.Value' + _ = p.Attributes.Select(item => CreateField(item.Key, item.Value)) + }; + } } -} -`} - - - - -{`public class Products_ByAttributeKey_JS : AbstractJavaScriptIndexCreationTask -{ - public Products_ByAttributeKey_JS() + ``` + + + ```csharp + public class Products_ByAttributeKey_JS : AbstractJavaScriptIndexCreationTask { - Maps = new HashSet + public Products_ByAttributeKey_JS() { - @"map('Products', function (p) { - return { - _: Object.keys(p.Attributes).map(key => createField(key, p.Attributes[key], - { indexing: 'Search', storage: true, termVector: null })) - }; - })" - }; + Maps = new HashSet + { + @"map('Products', function (p) { + return { + // The field name will be the key + // The field terms will be derived from the corresponding value + _: Object.keys(p.Attributes) + .map(key => createField(key, p.Attributes[key])) + }; + })" + }; + } } -} -`} - - - - + ``` + + * **The query**: - * You can now query the generated dynamic-index fields. - The `_` property is Not queryable but used only in the index definition syntax. - * To get all documents with some 'Size' use: - - - -{`IList matchingDocuments = session - .Advanced - .DocumentQuery() - // 'Size' is a dynamic-index-field that was indexed from the Attributes object - .WhereEquals("Size", 42) - .ToList(); -`} - - - - -{`// 'Size' is a dynamic-index-field that was indexed from the Attributes object -from index 'Products/ByAttributeKey' where Size = 42 -`} - - - + + You can now query the generated dynamic-index fields. + The `_` property is Not queryable but used only in the index definition syntax. + + To get all documents with a specific _Size_ use: + + + + ```csharp + IList matchingDocuments = session + .Advanced + .DocumentQuery() + // 'Size' is a dynamic-index-field that was indexed from the Attributes object + .WhereEquals("Size", 42) + .ToList(); + ``` + + + ```sql + // 'Size' is a dynamic-index-field that was indexed from the Attributes object + from index 'Products/ByAttributeKey' where Size = 42 + ``` + + + + + Dynamic index fields are queried with _DocumentQuery_ or _RQL_ because the field names (e.g. `Size`, `Color`) + are generated at indexing time and are not properties on the _Product_ class. + + A strongly typed LINQ query such as `Query().Where(p => p.Size == 42)` would not compile. + Use the string-based `WhereEquals("Size", 42)` or RQL `where Size = 42` instead. + -## Example - index any field + - + + +### Example - index every field + +The following example allows you to: * Define an index on a collection **without** needing any common structure between the indexed documents. - * After index is deployed, any new field added to the document will be indexed as well. - + * After the index is deployed, any new field added to any document in the collection will be indexed as well. - + -Consider whether this is really necessary, as indexing every single field can end up costing time and disk space. +* Use this approach with care. +* Indexing every field can increase indexing time, memory usage, and disk space, + especially for large or frequently updated documents. * **The document**: - - -{`public class Product -\{ - public string Id \{ get; set; \} - - // All KEYS in the document will be dynamically indexed - // Fields added to the document after index creation time will also get indexed - public string FirstName \{ get; set; \} - public string LastName \{ get; set; \} - public string Title \{ get; set; \} - // ... -\} -`} - - - - - -{`// Sample document content - \{ - "FirstName": "John", - "LastName": "Doe", - "Title": "Engineer", + + + ```csharp + public class Product + { + public string Id { get; set; } + + // All KEYS in the document will be dynamically indexed + // Fields added to the document after index creation time will also get indexed + public string FirstName { get; set; } + public string LastName { get; set; } + public string Title { get; set; } // ... -\} -`} - - + } + ``` + + + + ```json + // Sample document content + { + "FirstName": "John", + "LastName": "Doe", + "Title": "Engineer", + // ... + } + ``` + * **The index**: - The below index will index any field from the document, + + The following index will index any field from the document, a dynamic-index-field will be created for each field. New fields added to the document after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the field **key**. - E.g., Keys `FirstName` & `LastName` will become the actual dynamic-index-fields. + The actual dynamic-index-field name on which you can query will be the field **key**. + E.g., Keys `FirstName` & `LastName` will become the actual dynamic-index-fields. + + The example uses a JavaScript index because it enumerates all top-level document fields at indexing time with `Object.keys(...)`. + LINQ indexes typically create dynamic fields from a known enumerable structure, such as a dictionary or collection. - - - -{`public class Products_ByAnyField_JS : AbstractJavaScriptIndexCreationTask -{ - public Products_ByAnyField_JS() + + + ```csharp + public class Products_ByAnyField_JS : AbstractJavaScriptIndexCreationTask { - // This will index EVERY FIELD under the top level of the document - Maps = new HashSet + public Products_ByAnyField_JS() { - @"map('Products', function (p) { - return { - _: Object.keys(p).map(key => createField(key, p[key], - { indexing: 'Search', storage: true, termVector: null })) - } - })" - }; + // This will index EVERY FIELD under the top level of the document + Maps = new HashSet + { + @"map('Products', function (p) { + return { + _: Object.keys(p).map(key => createField(key, p[key])) + }; + })" + }; + } } -} -`} - - - + ``` + + * **The query**: - * To get all documents with some 'LastName' use: - - - -{`IList matchingDocuments = session - .Advanced - .DocumentQuery() - // 'LastName' is a dynamic-index-field that was indexed from the document - .WhereEquals("LastName", "Doe") - .ToList(); -`} - - - - -{`// 'LastName' is a dynamic-index-field that was indexed from the document -from index 'Products/ByAnyField/JS' where LastName = "Doe" -`} - - - + + To get all documents with a specific _LastName_ use: + + + + ```csharp + IList matchingDocuments = session + .Advanced + .DocumentQuery() + // 'LastName' is a dynamic-index-field that was indexed from the document + .WhereEquals("LastName", "Doe") + .ToList(); + ``` + + + ```sql + // 'LastName' is a dynamic-index-field that was indexed from the document + from index 'Products/ByAnyField/JS' where LastName = "Doe" + ``` + + + + + + + + + +### Example - basic -## Indexing documents fields VALUES + +The following example shows: + * Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. + * Documents can then be queried based on those indexed values. + * For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---list) below. + -## Example - basic +* **The document**: + + + ```csharp + public class Product + { + public string Id { get; set; } + + // The VALUE of ProductType will be dynamically indexed + public string ProductType { get; set; } + public int PricePerUnit { get; set; } + } + ``` + + + + ```json + // Sample document content + { + "ProductType": "Electronics", + "PricePerUnit": 23 + } + ``` + - +* **The index**: + + The following index will index the **value** of document field 'ProductType'. + + This value will be the dynamic-index-field name on which you can query. + E.g., Field value `Electronics` will be the dynamic-index-field. + + + + ```csharp + public class Products_ByProductType : AbstractIndexCreationTask + { + public Products_ByProductType() + { + Map = products => from p in products + select new + { + // Call 'CreateField' to generate the dynamic-index-fields + // The field name will be the value of document field 'ProductType' + // The field terms will be derived from document field 'PricePerUnit' + _ = CreateField(p.ProductType, p.PricePerUnit) + }; + } + } + ``` + + + ```csharp + public class Products_ByProductType_JS : AbstractJavaScriptIndexCreationTask + { + public Products_ByProductType_JS() + { + Maps = new HashSet + { + @"map('Products', function (p) { + return { + // The field name will be the value of document field 'ProductType' + // The field terms will be derived from document field 'PricePerUnit' + _: createField(p.ProductType, p.PricePerUnit) + }; + })" + }; + } + } + ``` + + + +* **The query**: + + To get all documents of some product type having a specific price per unit use: + + + + ```csharp + IList matchingDocuments = session + .Advanced + .DocumentQuery() + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'ProductType' + .WhereEquals("Electronics", 23) + .ToList(); + ``` + + + ```sql + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'ProductType' + from index 'Products/ByProductType' where Electronics = 23 + ``` + + + + + + -* Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. -* Documents can then be queried based on those indexed values. -* For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---index-a-list-of-properties) below. +### Example - list + +The following example allows you to: + * Index **values** from items in a list + * After index is deployed, any item added to this list in the document will be dynamically indexed as well. * **The document**: - - -{`public class Product -\{ - public string Id \{ get; set; \} - - // The VALUE of ProductType will be dynamically indexed - public string ProductType \{ get; set; \} - public int PricePerUnit \{ get; set; \} -\} -`} - - - - - -{`// Sample document content -\{ - "ProductType": "Electronics", - "PricePerUnit": 23 -\} -`} - - + + + ```csharp + public class Product + { + public string Id { get; set; } + public string Name { get; set; } + + // For each element in this list, + // the VALUE of property 'PropName' will be dynamically indexed. + // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields. + public List Attributes { get; set; } + } + + public class Attribute + { + public string PropName { get; set; } + public string PropValue { get; set; } + } + ``` + + + + ```json + // Sample document content + { + "Name": "SomeName", + "Attributes": [ + { + "PropName": "Color", + "PropValue": "Blue" + }, + { + "PropName": "Width", + "PropValue": "10" + }, + { + "PropName": "Length", + "PropValue": "20" + }, + ... + ] + } + ``` + * **The index**: - The below index will index the **value** of document field 'ProductType'. + + The following index will create a dynamic-index-field per item in the document's `Attributes` list. + New items added to the Attributes list after index creation time will be dynamically indexed as well. - This value will be the dynamic-index-field name on which you can query. - E.g., Field value `Electronics` will be the dynamic-index-field. + The actual dynamic-index-field name on which you can query will be the item's PropName **value**. + E.g., 'PropName' value `Width` will be a dynamic-index-field. + + + + ```csharp + public class Attributes_ByName : AbstractIndexCreationTask + { + public Attributes_ByName() + { + Map = products => from a in products + select new + { + // Define the dynamic-index-fields by calling 'CreateField' + // A dynamic-index-field will be generated for each item in 'Attributes' + + // For each item, the field name will be the value of field 'PropName' + // The field terms will be derived from field 'PropValue' + _ = a.Attributes.Select(item => + CreateField(item.PropName, item.PropValue)), + + // A regular index field can be defined as well: + Name = a.Name + }; + } + } + ``` + + + ```csharp + public class Attributes_ByName_JS : AbstractJavaScriptIndexCreationTask + { + public Attributes_ByName_JS() + { + Maps = new HashSet + { + @"map('Products', function (p) { + return { + // For each item, + // the field name will be the value of field 'PropName' + // The field terms will be derived from field 'PropValue' + _: p.Attributes.map(item => createField( + item.PropName, item.PropValue)), + + // A regular index field can be defined as well: + Name: p.Name + }; + })" + }; + } + } + ``` + + + +* **The query**: + + To get all documents matching a specific attribute property use: + + + + ```csharp + IList matchingDocuments = session + .Advanced + .DocumentQuery() + // 'Width' is a dynamic-index-field that was indexed from the Attributes list + .WhereEquals("Width", 10) + .ToList(); + ``` + + + ```sql + // 'Width' is a dynamic-index-field that was indexed from the Attributes list + from index 'Attributes/ByName' where Width = 10 + ``` + + + + + + + + + + + +Dynamic fields can be indexed for [Full-text search](../../indexes/querying/searching.mdx) by setting `FieldIndexing.Search` in `CreateFieldOptions`. + +* **At indexing time**: + At indexing time, RavenDB analyzes the dynamic field value and creates the indexed terms. + The analyzer used at indexing time is resolved from the index definition. + You can configure an analyzer for a specific generated field name, or configure `AllFields` as a fallback. + See [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) below. + + * If the generated dynamic field name has an explicit analyzer configuration, that analyzer is used. + * Otherwise, if `AllFields` has an analyzer configuration, RavenDB uses it as a fallback. + * If no analyzer is configured for the field, RavenDB uses the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + +* **At querying time**: + For full-text search to return the expected results, the analyzer used at indexing time and the analyzer used for the query term should produce compatible terms. + + When a `search()` query targets a dynamic field that does **not** have an explicit analyzer configuration for its generated field name, + RavenDB uses the [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) to analyze the query term by default. + + Set the [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) + configuration key to `true` to make RavenDB use the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) as the fallback analyzer for the query term instead. + + This configuration can be set at different scopes: server-wide, per database, or per index. + The example below sets it at the index scope, in the index definition. + + For a summary of how RavenDB resolves the analyzer used for the query term, + see [Which analyzer is used for the query term](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) below. + + + +--- + +The following example indexes localized product descriptions as dynamic fields and queries one of those generated fields using full-text search: + +* **The document**: + + + ```csharp + public class Product + { + public string Id { get; set; } + + public Dictionary Descriptions { get; set; } + } + ``` + + + + ```json + // Sample document content + { + "Descriptions": { + "English": "North wind jacket", + "French": "Veste vent du nord" + } + } + ``` + + +* **The index**: + + The following index creates one dynamic field for each entry in the `Descriptions` dictionary. + For example, the key `English` creates the dynamic field `Description_English`. + + Each generated dynamic field is indexed for **full-text search**. + + + + ```csharp + public class Products_ByLocalizedDescription : AbstractIndexCreationTask + { + private const string UseSearchAnalyzerForDynamicFields = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public Products_ByLocalizedDescription() + { + Map = products => from product in products + select new + { + _ = product.Descriptions.Select(x => + CreateField( + "Description_" + x.Key, + x.Value, + new CreateFieldOptions + { + // Index each generated dynamic field for FTS + Indexing = FieldIndexing.Search, + Storage = FieldStorage.No + })) + }; + + StoreAllFields(FieldStorage.No); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + Configuration[UseSearchAnalyzerForDynamicFields] = "true"; + } + } + ``` + + + ```csharp + public class Products_ByLocalizedDescription_JS : AbstractJavaScriptIndexCreationTask + { + private const string UseSearchAnalyzerForDynamicFields = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public Products_ByLocalizedDescription_JS() + { + Maps = new HashSet + { + @"map('Products', function (product) { + return { + _: Object.keys(product.Descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + // Index each generated dynamic field for FTS + indexing: 'Search', + storage: 'No' + }); + }) + }; + })" + }; + + Fields[Constants.Documents.Indexing.Fields.AllFields] = new IndexFieldOptions + { + Storage = FieldStorage.No + }; + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + Configuration[UseSearchAnalyzerForDynamicFields] = "true"; + } + } + ``` + + + +* **Full-text search query**: + + Query the generated dynamic field by its field name. + + In this example, the query targets the generated field `Description_English`. + + Since `Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery` is set to `true`, + the searched term `"north wind"` is analyzed with the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + + + + ```csharp + List results = session.Advanced + .DocumentQuery() + .Search("Description_English", "north wind") + .ToList(); + ``` + + + + ```sql + from index "Products/ByLocalizedDescription" + where search(Description_English, "north wind") + ``` + + + +--- + + + +### Configuring analyzers for dynamic fields - - - -{`public class Products_ByProductType : AbstractIndexCreationTask +`CreateFieldOptions` does **not** let you specify a custom analyzer directly. +However, you can still configure which analyzer RavenDB will use for dynamic fields in the index definition by either: + +* Configuring an analyzer for a specific generated dynamic field name. +* Configuring `AllFields` as a fallback for fields that do not have their own explicit analyzer configuration. + +Use `AllFields` with care, since it can affect any field that does not have its own explicit analyzer configuration. + +--- + +#### Configure analyzer for a specific dynamic field + + + +```csharp +public class Products_ByLocalizedDescription : AbstractIndexCreationTask { - public Products_ByProductType() + public Products_ByLocalizedDescription() { - Map = products => from p in products + Map = products => from product in products select new { - // Call 'CreateField' to generate the dynamic-index-fields - // The field name will be the value of document field 'ProductType' - // The field terms will be derived from document field 'PricePerUnit' - _ = CreateField(p.ProductType, p.PricePerUnit) + _ = product.Descriptions.Select(x => + CreateField( + "Description_" + x.Key, + x.Value, + new CreateFieldOptions + { + Indexing = FieldIndexing.Search, + Storage = FieldStorage.No + })) }; + + StoreAllFields(FieldStorage.No); + + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + Analyze("Description_English", "StopAnalyzer"); } } -`} - +``` - - -{`public class Products_ByProductType_JS : AbstractJavaScriptIndexCreationTask + +```csharp +public class Products_ByLocalizedDescription_JS : AbstractJavaScriptIndexCreationTask { - public Products_ByProductType_JS() + public Products_ByLocalizedDescription_JS() { Maps = new HashSet { - @"map('Products', function (p) { + @"map('Products', function (product) { return { - _: createField(p.ProductType, p.PricePerUnit, - { indexing: 'Search', storage: true, termVector: null }) + _: Object.keys(product.Descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; })" }; + + Fields[Constants.Documents.Indexing.Fields.AllFields] = new IndexFieldOptions + { + Storage = FieldStorage.No + }; + + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + Fields["Description_English"] = new IndexFieldOptions + { + Analyzer = "StopAnalyzer" + }; } } -`} - - - - -* **The query**: - * To get all documents of some product type having a specific price per unit use: - - - -{`IList matchingDocuments = session - .Advanced - .DocumentQuery() - // 'Electronics' is the dynamic-index-field that was indexed from document field 'ProductType' - .WhereEquals("Electronics", 23) - .ToList(); -`} - - - - -{`// 'Electronics' is the dynamic-index-field that was indexed from document field 'ProductType' -from index 'Products/ByProductType' where Electronics = 23 -`} - +``` -## Example - list - - +#### Configure a fallback analyzer for all fields -* Index **values** from items in a list -* After index is deployed, any item added this list in the document will be dynamically indexed as well. - - - -* **The document**: - - -{`public class Product -\{ - public string Id \{ get; set; \} - public string Name \{ get; set; \} - - // For each element in this list, the VALUE of property 'PropName' will be dynamically indexed - // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields - public List Attributes \{ get; set; \} -\} - -public class Attribute -\{ - public string PropName \{ get; set; \} - public string PropValue \{ get; set; \} -\} -`} - - - - - -{`// Sample document content -\{ -Name": "SomeName", -Attributes": [ - \{ - "PropName": "Color", - "PropValue": "Blue" - \}, - \{ - "PropName": "Width", - "PropValue": "10" - \}, - \{ - "PropName": "Length", - "PropValue": "20" - \}, - ... - -\} -`} - - - -* **The index**: - The below index will create a dynamic-index-field per item in the document's `Attributes` list. - New items added to the Attributes list after index creation time will be dynamically indexed as well. - - The actual dynamic-index-field name on which you can query will be the item's PropName **value**. - E.g., 'PropName' value `Width` will be a dynamic-index-field. - - - - -{`public class Attributes_ByName : AbstractIndexCreationTask + + +```csharp +public class Products_ByLocalizedDescription : AbstractIndexCreationTask { - public Attributes_ByName() + public Products_ByLocalizedDescription() { - Map = products => from a in products + Map = products => from product in products select new { - // Define the dynamic-index-fields by calling CreateField - // A dynamic-index-field will be generated for each item in the Attributes list - - // For each item, the field name will be the value of field 'PropName' - // The field terms will be derived from field 'PropValue' - _ = a.Attributes.Select(item => CreateField(item.PropName, item.PropValue)), - - // A regular index field can be defined as well: - Name = a.Name + _ = product.Descriptions.Select(x => + CreateField( + "Description_" + x.Key, + x.Value, + new CreateFieldOptions + { + Indexing = FieldIndexing.Search, + Storage = FieldStorage.No + })) }; + + StoreAllFields(FieldStorage.No); + + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + Analyze(Constants.Documents.Indexing.Fields.AllFields, "StopAnalyzer"); } } -`} - +``` - - -{`public class Attributes_ByName_JS : AbstractJavaScriptIndexCreationTask + +```csharp +public class Products_ByLocalizedDescription_JS : AbstractJavaScriptIndexCreationTask { - public Attributes_ByName_JS() + public Products_ByLocalizedDescription_JS() { Maps = new HashSet { - @"map('Products', function (p) { + @"map('Products', function (product) { return { - _: p.Attributes.map(item => createField(item.PropName, item.PropValue, - { indexing: 'Search', storage: true, termVector: null })), - Name: p.Name + _: Object.keys(product.Descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; })" }; + + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + Fields[Constants.Documents.Indexing.Fields.AllFields] = new IndexFieldOptions + { + Storage = FieldStorage.No, + Analyzer = "StopAnalyzer" + }; } } -`} - - - - -* **The query**: - * To get all documents matching a specific attribute property use: - - - -{`IList matchingDocuments = session - .Advanced - .DocumentQuery() - // 'Width' is a dynamic-index-field that was indexed from the Attributes list - .WhereEquals("Width", 10) - .ToList(); -`} - - - - -{`// 'Width' is a dynamic-index-field that was indexed from the Attributes list -from index 'Attributes/ByName' where Width = 10 -`} - +``` + + + + +### Which analyzer is used for the query term? + +The following applies when a `search()` query targets a **dynamic field**: + +| Index configuration | [Configuration key](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) | Analyzer used for the query term | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| No analyzer is explicitly configured for the generated field name | `false` | The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) | +| No analyzer is explicitly configured for the generated field name | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| `AllFields` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `false` | The analyzer resolved by the normal field fallback path,
e.g. `WhitespaceAnalyzer` | +| `AllFields` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| The generated field name is explicitly configured,
e.g. `Analyze("Description_English", "StopAnalyzer")` | `false` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +| The generated field name is explicitly configured,
e.g. `Analyze("Description_English", "StopAnalyzer")` | `true` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +
-## CreateField syntax +
+ + #### Syntax for LINQ-index: - - -{`object CreateField(string name, object value); + +```csharp +object CreateField(string name, object value); object CreateField(string name, object value, bool stored, bool analyzed); object CreateField(string name, object value, CreateFieldOptions options); -`} - +``` #### Syntax for JavaScript-index: - - -{`createField(fieldName, fieldValue, options); // returns object -`} - + +```js +createField(fieldName, fieldValue, options); // returns object +``` | Parameters | Type | Description | |----------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| | **fieldName** | `string` | Name of the dynamic-index-field | -| **fieldValue** | `object` | Value of the dynamic-index-field
The field Terms are derived from this value. | -| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorate.Yes` | +| **fieldValue** | `object` | Value of the dynamic-index-field
The field terms are derived from this value. | +| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorage.Yes` | | **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing.Default` (default value)
`false` - `FieldIndexing.Exact`
`true` - `FieldIndexing.Search` | | **options** | `CreateFieldOptions` | Dynamic-index-field options | -| CreateFieldOptions | | | -|--------------------|--------------------|----------------------------------------------------------------------------| +| CreateFieldOptions | | | +|--------------------|--------------------|-----------------------------------------------------------------------------------| | **Storage** | `FieldStorage?` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | -| **Indexing** | `FieldIndexing?` | Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | -| **TermVector** | `FieldTermVector?` | Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | +| **Indexing** | `FieldIndexing?` | Sets the field indexing behavior.
Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | +| **TermVector** | `FieldTermVector?` | Sets the field term-vector behavior.
Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | @@ -540,11 +930,15 @@ object CreateField(string name, object value, CreateFieldOptions options); -## Indexed fields & terms view - -The generated dynamic-index-fields and their indexed terms can be viewed in the **Terms View**. -Below are sample index fields & their terms generated from the last example. +
+ + +The generated dynamic index fields and their indexed terms can be viewed in the **Terms View** in Studio. +Below are sample index fields & their terms generated from [this example](../../indexes/using-dynamic-fields.mdx#example---list). + ![Figure 1. Go to terms view](../assets/dynamic-index-fields-1.png) - + ![Figure 2. Indexed fields & terms](../assets/dynamic-index-fields-2.png) + + \ No newline at end of file diff --git a/versioned_docs/version-6.2/indexes/content/_using-dynamic-fields-java.mdx b/versioned_docs/version-6.2/indexes/content/_using-dynamic-fields-java.mdx index 17f3cbe360..d54ef6bea8 100644 --- a/versioned_docs/version-6.2/indexes/content/_using-dynamic-fields-java.mdx +++ b/versioned_docs/version-6.2/indexes/content/_using-dynamic-fields-java.mdx @@ -2,479 +2,918 @@ import Admonition from '@theme/Admonition'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; +import ContentFrame from '@site/src/components/ContentFrame'; +import Panel from '@site/src/components/Panel'; -* In RavenDB different documents can have different shapes. - Documents are schemaless - new fields can be added or removed as needed. +* In RavenDB, different documents can have different shapes. + Documents are schemaless - new fields can be added or removed as needed. + For such dynamic data, you can define indexes with **dynamic-index-fields**. + +* Dynamic-index-fields are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time, + which is very useful when working with highly dynamic systems. + +* Any value type can be indexed, string, number, date, etc. + An index definition can contain both dynamic-index-fields and regular-index-fields. + +* In this article: + * [Indexing field KEYS](../../indexes/using-dynamic-fields.mdx#indexing-field-keys) + * [Example - index every field under an object](../../indexes/using-dynamic-fields.mdx#example---index-every-field-under-an-object) + * [Example - index every field](../../indexes/using-dynamic-fields.mdx#example---index-every-field) + * [Indexing field VALUES](../../indexes/using-dynamic-fields.mdx#indexing-field-values) + * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) + * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) + * [Indexing dynamic fields for full-text search](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + * [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) + * [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + * [`CreateField` syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) + * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields-terms-view) -* For such dynamic data, you can define indexes with **dynamic-index-fields**. + -* This allows querying the index on fields that aren't yet known at index creation time, - which is very useful when working on highly dynamic systems. + -* Any value type can be indexed, string, number, date, etc. + + +### Example - index every field under an object -* An index definition can contain both dynamic-index-fields and regular-index-fields. + +The following example allows you to: + * Index any field that is under the same object from the document. + * After index is deployed, any new field added to this object will be indexed as well. + -* In this page: +* **The document**: + + + ```java + public class Product { + private String id; - * [Indexing documents fields KEYS](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-keys) - * [Example - index any field under object](../../indexes/using-dynamic-fields.mdx#example---index-any-field-under-object) - * [Example - index any field](../../indexes/using-dynamic-fields.mdx#example---index-any-field) - * [Indexing documents fields VALUES](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-values) - * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) - * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) - * [CreateField syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) - * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields--terms-view) + // The KEYS under the attributes object will be dynamically indexed + // Fields added to this object after index creation time will also get indexed + private Map attributes; -
+ // getters and setters ... + } + ``` + + + + ```json + // Sample document content + { + "attributes": { + "color": "Red", + "size": 42 + } + } + ``` + -## Indexing documents fields KEYS +* **The index**: + + The following index will index any field under the `attributes` object from the document, + a dynamic-index-field will be created for each such field. + New fields added to the object after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the attribute field **key**. + E.g., Keys `color` & `size` will become the actual dynamic-index-fields. + + + + ```java + public class Products_ByAttributeKey extends AbstractIndexCreationTask { + public Products_ByAttributeKey() { + + // Call 'CreateField' to generate dynamic-index-fields + // from the attributes object keys. + + // Using '_' is just a convention. + // Any other string can be used instead of '_'. + + // The actual field name will be 'item.Key' + // The actual field terms will be derived from 'item.Value' + map = "docs.Products.Select(p => new { " + + " _ = p.attributes.Select(item => " + + " this.CreateField(item.Key, item.Value)) " + + "})"; + } + } + ``` + + + ```java + public class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask { + public Products_ByAttributeKey_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (p) { " + + " return { " + + // The field name will be the key + // The field terms will be derived from the corresponding value + " _: Object.keys(p.attributes) " + + " .map(key => createField(key, p.attributes[key])) " + + " }; " + + "})" + )); + } + } + ``` + + -## Example - index any field under object +* **The query**: + + You can now query the generated dynamic-index fields. + The `_` property is Not queryable but used only in the index definition syntax. + + To get all documents with a specific _size_ use: + + + + ```java + List matchingDocuments = session + .advanced() + .documentQuery(Product.class, Products_ByAttributeKey.class) + // 'size' is a dynamic-index-field that was indexed from the attributes object + .whereEquals("size", 42) + .toList(); + ``` + + + ```sql + // 'size' is a dynamic-index-field that was indexed from the attributes object + from index 'Products/ByAttributeKey' where size = 42 + ``` + + + + + Dynamic index fields are queried with _DocumentQuery_ or _RQL_ because the field names (e.g. `size`, `color`) + are generated at indexing time and are not properties on the _Product_ class. + + Use the string-based `whereEquals("size", 42)` or RQL `where size = 42` instead. + - + -* Index any field that is under the some object from the document. -* After index is deployed, any new field added to the this object will be indexed as well. + + +### Example - index every field + +The following example allows you to: + * Define an index on a collection **without** needing any common structure between the indexed documents. + * After the index is deployed, any new field added to any document in the collection will be indexed as well. + + + +* Use this approach with care. +* Indexing every field can increase indexing time, memory usage, and disk space, + especially for large or frequently updated documents. * **The document**: - - -{`public class Product \{ - private String id; - - // The KEYS under the attributes object will be dynamically indexed - // Fields added to this object after index creation time will also get indexed - private Dictionary attributes; - - // get + set implementation ... -\} -`} - - - - -{`// Sample document content -\{ - "attributes": \{ - "color": "Red", - "size": 42 - \} -\} -`} - - + + + ```java + public class Product { + private String id; + + // All KEYS in the document will be dynamically indexed + // Fields added to the document after index creation time will also get indexed + private String firstName; + private String lastName; + private String title; + // ... + + // getters and setters ... + } + ``` + + + + ```json + // Sample document content + { + "firstName": "John", + "lastName": "Doe", + "title": "Engineer" + // ... + } + ``` + * **The index**: - The following index will index any field under the `attributes` object from the document, - a dynamic-index-field will be created for each such field. - New fields added to the object after index creation time will be dynamically indexed as well. + + The following index will index any field from the document, + a dynamic-index-field will be created for each field. + New fields added to the document after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the field **key**. + E.g., Keys `firstName` & `lastName` will become the actual dynamic-index-fields. + + The example uses an `AbstractJavaScriptIndexCreationTask` because it enumerates all top-level document fields at indexing time with `Object.keys(...)`. + LINQ indexes typically create dynamic fields from a known enumerable structure, such as a map or collection. + + + + ```java + public class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask { + public Products_ByAnyField_JS() { + + // This will index EVERY FIELD under the top level of the document + setMaps(Sets.newHashSet( + "map('Products', function (p) { " + + " return { " + + " _: Object.keys(p).map(key => createField(key, p[key])) " + + " }; " + + "})" + )); + } + } + ``` + + - The actual dynamic-index-field name on which you can query will be the attribute field **key**. - e.g. Keys `color` & `size` will become the actual dynamic-index-fields. +* **The query**: + + To get all documents with a specific _lastName_ use: + + + + ```java + List matchingDocuments = session + .advanced() + .documentQuery(Product.class, Products_ByAnyField_JS.class) + // 'lastName' is a dynamic-index-field that was indexed from the document + .whereEquals("lastName", "Doe") + .toList(); + ``` + + + ```sql + // 'lastName' is a dynamic-index-field that was indexed from the document + from index 'Products/ByAnyField/JS' where lastName = "Doe" + ``` + + + + + + - - - -{`public class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask { - public Products_ByAttributeKey_JS() { - // Call 'createField' to generate dynamic-index-fields from the attributes object keys - // Using '_' is just a convention. Any other string can be used instead of '_' + - // The actual field name will be the key - // The actual field terms will be derived from p.attributes[key] - setMaps(Sets.newHashSet( - "map('Products', function (p) { " + - " return { " + - " _: Object.keys(p.attributes).map(key => createField(key, p.attributes[key], " + - " { indexing: 'Search', storage: false, termVector: null })) " + - " }; " + - "}) " - )); - } -} -`} - - - + + +### Example - basic -* **The query**: - * You can now query the generated dynamic-index fields. - Property `_` is Not queryable, it is only used in the index definition syntax. - * To get all documents with some 'size' use: - - - -{`List matchingDocuments = session - .query(Product.class, Products_ByAttributeKey_JS.class) - .whereEquals("size", 42) - .toList(); -`} - - - - -{`// 'size' is a dynamic-index-field that was indexed from the Attributes object -from index 'Products/ByAttributeKey/JS' where size = 42 -`} - - - + +The following example shows: + * Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. + * Documents can then be queried based on those indexed values. + * For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---list) below. + -## Example - index any field +* **The document**: + + + ```java + public class Product { + private String id; - + // The VALUE of productType will be dynamically indexed + private String productType; + private int pricePerUnit; -* Define an index on a collection **without** needing any common structure between the indexed documents. -* After index is deployed, any new field added to the document will be indexed as well. + // getters and setters ... + } + ``` + + + + ```json + // Sample document content + { + "productType": "Electronics", + "pricePerUnit": 23 + } + ``` + - +* **The index**: + + The following index will index the **value** of document field 'productType'. + + This value will be the dynamic-index-field name on which you can query. + E.g., Field value `Electronics` will be the dynamic-index-field. + + + + ```java + public class Products_ByProductType extends AbstractIndexCreationTask { + public Products_ByProductType() { + + // Call 'CreateField' to generate the dynamic-index-field. + // The field name will be the value of document field 'productType'. + // The field terms will be derived from document field 'pricePerUnit'. + map = "docs.Products.Select(p => new { " + + " _ = this.CreateField(p.productType, p.pricePerUnit) " + + "})"; + } + } + ``` + + + ```java + public class Products_ByProductType_JS extends AbstractJavaScriptIndexCreationTask { + public Products_ByProductType_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (p) { " + + " return { " + + // The field name will be the value of document field 'productType' + // The field terms will be derived from document field 'pricePerUnit' + " _: createField(p.productType, p.pricePerUnit) " + + " }; " + + "})" + )); + } + } + ``` + + - -Consider if that is a true necessity, as indexing every single field can end up costing time and disk space. +* **The query**: + + To get all documents of some product type having a specific price per unit use: + + + + ```java + List matchingDocuments = session + .advanced() + .documentQuery(Product.class, Products_ByProductType.class) + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + .whereEquals("Electronics", 23) + .toList(); + ``` + + + ```sql + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + from index 'Products/ByProductType' where Electronics = 23 + ``` + + + + + + + +### Example - list + + +The following example allows you to: + * Index **values** from items in a list + * After index is deployed, any item added to this list in the document will be dynamically indexed as well. * **The document**: - - -{`public class Product \{ - private String id; - - // All KEYS in the document will be dynamically indexed - // Fields added to the document after index creation time will also get indexed - private String firstName; - private String lastName; - private String title; - // ... - - // get + set implementation ... -\} -`} - - - - - -{`// Sample document content -\{ - "firstName": "John", - "lastName": "Doe", - "title": "Engineer", - // ... -\} -`} - - + + + ```java + public class Product { + private String id; + private String name; + + // For each element in this list, + // the VALUE of property 'propName' will be dynamically indexed. + // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields. + private List attributes; + + // getters and setters ... + } -* **The index**: - The following index will index any field from the document, - a dynamic-index-field will be created for each field. - New fields added to the document after index creation time will be dynamically indexed as well. + public class Attribute { + private String propName; + private String propValue; - The actual dynamic-index-field name on which you can query will be the field **key**. - E.g., Keys `firstName` & `lastName` will become the actual dynamic-index-fields. + // getters and setters ... + } + ``` + + + + ```json + // Sample document content + { + "name": "SomeName", + "attributes": [ + { + "propName": "Color", + "propValue": "Blue" + }, + { + "propName": "Width", + "propValue": "10" + }, + { + "propName": "Length", + "propValue": "20" + } + // ... + ] + } + ``` + - - - -{`public class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask { - public Products_ByAnyField_JS() { +* **The index**: - // This will index EVERY FIELD under the top level of the document - setMaps(Sets.newHashSet( - "map('Products', function (p) { " + - " return { " + - " _: Object.keys(p).map(key => createField(key, p[key], " + - " { indexing: 'Search', storage: true, termVector: null })) " + - " }; " + - "}) " - )); + The following index will create a dynamic-index-field per item in the document's `attributes` list. + New items added to the attributes list after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the item's propName **value**. + E.g., 'propName' value `Width` will be a dynamic-index-field. + + + + ```java + public class Attributes_ByName extends AbstractIndexCreationTask { + public Attributes_ByName() { + + // Define the dynamic-index-fields by calling 'CreateField'. + // A dynamic-index-field will be generated for each item in 'attributes'. + + // For each item, the field name will be the value of field 'propName' + // The field terms will be derived from field 'propValue'. + // A regular index field (Name) is defined as well. + map = "docs.Products.Select(p => new { " + + " _ = p.attributes.Select(item => " + + " this.CreateField(item.propName, item.propValue)), " + + " Name = p.name " + + "})"; + } } -} -`} - - - + ``` + + + ```java + public class Attributes_ByName_JS extends AbstractJavaScriptIndexCreationTask { + public Attributes_ByName_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (p) { " + + " return { " + + // For each item, + // the field name will be the value of field 'propName'. + // The field terms will be derived from field 'propValue'. + " _: p.attributes.map(item => " + + " createField(item.propName, item.propValue)), " + + // A regular index field can be defined as well: + " Name: p.name " + + " }; " + + "})" + )); + } + } + ``` + + * **The query**: - * To get all documents with some 'lastName' use: - - - -{`List matchingDocuments = session - .query(Product.class, Products_ByAnyField_JS.class) - .whereEquals("lastName", "Doe") - .toList(); -`} - - - - -{`// 'lastName' is a dynamic-index-field that was indexed from the document -from index 'Products/ByAnyField/JS' where lastName = "Doe" -`} - - - + + To get all documents matching a specific attribute property use: + + + + ```java + List matchingDocuments = session + .advanced() + .documentQuery(Product.class, Attributes_ByName.class) + // 'Width' is a dynamic-index-field that was indexed from the attributes list + .whereEquals("Width", 10) + .toList(); + ``` + + + ```sql + // 'Width' is a dynamic-index-field that was indexed from the attributes list + from index 'Attributes/ByName' where Width = 10 + ``` + + + + + + + + + + +Dynamic fields can be indexed for [Full-text search](../../indexes/querying/searching.mdx) by setting `indexing: 'Search'` in the `createField` options object (`AbstractJavaScriptIndexCreationTask`) +or `FieldIndexing.Search` in the `CreateFieldOptions` (`AbstractIndexCreationTask`). +* **At indexing time**: + At indexing time, RavenDB analyzes the dynamic field value and creates the indexed terms. + The analyzer used at indexing time is resolved from the index definition. + You can configure an analyzer for a specific generated field name, or configure `ALL_FIELDS` as a fallback. + See [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) below. -## Indexing documents fields VALUES + * If the generated dynamic field name has an explicit analyzer configuration, that analyzer is used. + * Otherwise, if `ALL_FIELDS` has an analyzer configuration, RavenDB uses it as a fallback. + * If no analyzer is configured for the field, RavenDB uses the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). -## Example - basic +* **At querying time**: + For full-text search to return the expected results, the analyzer used at indexing time and the analyzer used for the query term should produce compatible terms. - + When a `search()` query targets a dynamic field that does **not** have an explicit analyzer configuration for its generated field name, + RavenDB uses the [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) to analyze the query term by default. -* Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. -* Documents can then be queried based on those indexed values. -* For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---index-a-list-of-properties) below. + Set the [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) + configuration key to `true` to make RavenDB use the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) as the fallback analyzer for the query term instead. + + This configuration can be set at different scopes: server-wide, per database, or per index. + The example below sets it at the index scope, in the index definition. + + For a summary of how RavenDB resolves the analyzer used for the query term, + see [Which analyzer is used for the query term](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) below. + +--- + +The following example indexes localized product descriptions as dynamic fields and queries one of those generated fields using full-text search: + +* **The document**: -* **The document**: - - -{`public class Product \{ - private String id; - - // The VALUE of productType will be dynamically indexed - private String productType; - private int pricePerUnit; - - // get + set implementation ... -\} -`} - - + + ```java + public class Product { + private String id; - - -{`// Sample document content -\{ - "productType": "Electronics", - "pricePerUnit": 23 -\} -`} - - + private Map descriptions; -* **The index**: - The following index will index the **value** of document field 'productType'. + // getters and setters ... + } + ``` + + + + ```json + // Sample document content + { + "descriptions": { + "English": "North wind jacket", + "French": "Veste vent du nord" + } + } + ``` + + +* **The index**: + + The following index creates one dynamic field for each entry in the `descriptions` map. + For example, the key `English` creates the dynamic field `Description_English`. + + Each generated dynamic field is indexed for **full-text search**. + + + + ```java + public class Products_ByLocalizedDescription extends AbstractIndexCreationTask { + + private static final String USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public Products_ByLocalizedDescription() { + map = "docs.Products.Select(product => new { " + + " _ = product.descriptions.Select(x => " + + " this.CreateField( " + + " \"Description_\" + x.Key, " + + " x.Value, " + + " new CreateFieldOptions { " + + // Index each generated dynamic field for FTS + " Indexing = FieldIndexing.Search, " + + " Storage = FieldStorage.No " + + " })) " + + "})"; + + storeAllFields(FieldStorage.NO); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + getConfiguration().put(USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS, "true"); + } + } + ``` + + + ```java + public class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + + private static final String USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public Products_ByLocalizedDescription_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (product) { " + + " return { " + + " _: Object.keys(product.descriptions) " + + " .map(function (key) { " + + " return createField( " + + " 'Description_' + key, " + + " product.descriptions[key], " + + " { " + + // Index each generated dynamic field for FTS + " indexing: 'Search', " + + " storage: false " + + " }); " + + " }) " + + " }; " + + "})" + )); + + Map fields = new HashMap<>(); + + IndexFieldOptions allFieldsOptions = new IndexFieldOptions(); + allFieldsOptions.setStorage(FieldStorage.NO); + + fields.put(Constants.Documents.Indexing.Fields.ALL_FIELDS, allFieldsOptions); + setFields(fields); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + getConfiguration().put(USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS, "true"); + } + } + ``` + + + +* **Full-text search query**: + + Query the generated dynamic field by its field name. + + In this example, the query targets the generated field `Description_English`. + + Since `Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery` is set to `true`, + the searched term `"north wind"` is analyzed with the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + + + + ```java + List results = session.advanced() + .documentQuery(Product.class, Products_ByLocalizedDescription.class) + .search("Description_English", "north wind") + .toList(); + ``` + + + + ```sql + from index "Products/ByLocalizedDescription" + where search(Description_English, "north wind") + ``` + + - This value will be the dynamic-index-field name on which you can query. - e.g. Field value `Electronics` will be the dynamic-index-field. +--- - - - -{`public class Products_ByProductType extends AbstractIndexCreationTask { - public Products_ByProductType() { + + +### Configuring analyzers for dynamic fields + +The `CreateFieldOptions` (and the JavaScript `createField` options) do **not** let you specify a custom analyzer directly. +However, you can still configure which analyzer RavenDB will use for dynamic fields in the index definition by either: - // The field name will be the value of document field 'productType' - // The field terms will be derived from document field 'pricePerUnit' - map = "docs.Products.Select(p => new { " + - " _ = this.CreateField(p.productType, p.pricePerUnit) " + +* Configuring an analyzer for a specific generated dynamic field name. +* Configuring `ALL_FIELDS` as a fallback for fields that do not have their own explicit analyzer configuration. + +Use `ALL_FIELDS` with care, since it can affect any field that does not have its own explicit analyzer configuration. + +--- + +#### Configure analyzer for a specific dynamic field + + + +```java +public class Products_ByLocalizedDescription extends AbstractIndexCreationTask { + public Products_ByLocalizedDescription() { + map = "docs.Products.Select(product => new { " + + " _ = product.descriptions.Select(x => " + + " this.CreateField( " + + " \"Description_\" + x.Key, " + + " x.Value, " + + " new CreateFieldOptions { " + + " Indexing = FieldIndexing.Search, " + + " Storage = FieldStorage.No " + + " })) " + "})"; + + storeAllFields(FieldStorage.NO); + + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + analyze("Description_English", "StopAnalyzer"); } } -`} - +``` - - -* **The query**: - * To get all documents of some product type having a specific price per unit use: - - - -{`List matchingDocuments = session - .query(Product.class, Products_ByProductType.class) - .whereEquals("Electronics", 23) - .toList(); -`} - - - - -{`// 'Electronics' is the dynamic-index-field that was indexed from document field 'productType' -from index 'Products/ByProductType' where Electronics = 23 -`} - - - - -## Example - list + +```java +public class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + public Products_ByLocalizedDescription_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (product) { " + + " return { " + + " _: Object.keys(product.descriptions) " + + " .map(function (key) { " + + " return createField( " + + " 'Description_' + key, " + + " product.descriptions[key], " + + " { " + + " indexing: 'Search', " + + " storage: false " + + " }); " + + " }) " + + " }; " + + "})" + )); - + Map fields = new HashMap<>(); -* Index **values** from items in a list -* After index is deployed, any item added this list in the document will be dynamically indexed as well. + IndexFieldOptions allFieldsOptions = new IndexFieldOptions(); + allFieldsOptions.setStorage(FieldStorage.NO); + fields.put(Constants.Documents.Indexing.Fields.ALL_FIELDS, allFieldsOptions); - + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + IndexFieldOptions englishOptions = new IndexFieldOptions(); + englishOptions.setAnalyzer("StopAnalyzer"); + fields.put("Description_English", englishOptions); -* **The document**: - - -{`public class Product \{ - private String id; - private String name; - - // For each element in this list, the VALUE of property 'propName' will be dynamically indexed - // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields - private List attributes; - - // get + set implementation ... -\} - -public class Attribute \{ - private String propName; - private String propValue; - - // get + set implementation ... -\} -`} - + setFields(fields); + } +} +``` + - - -{`// Sample document content -\{ -name": "SomeName", -attributes": [ - \{ - "propName": "Color", - "propValue": "Blue" - \}, - \{ - "propName": "Width", - "propValue": "10" - \}, - \{ - "propName": "Length", - "propValue": "20" - \}, - ... - -\} -`} - - +#### Configure a fallback analyzer for all fields + + + +```java +public class Products_ByLocalizedDescription extends AbstractIndexCreationTask { + public Products_ByLocalizedDescription() { + map = "docs.Products.Select(product => new { " + + " _ = product.descriptions.Select(x => " + + " this.CreateField( " + + " \"Description_\" + x.Key, " + + " x.Value, " + + " new CreateFieldOptions { " + + " Indexing = FieldIndexing.Search, " + + " Storage = FieldStorage.No " + + " })) " + + "})"; -* **The index**: - The following index will create a dynamic-index-field per item in the document's `attributes` list. - New items added to the attributes list after index creation time will be dynamically indexed as well. - - The actual dynamic-index-field name on which you can query will be the item's propName **value**. - E.g., 'propName' value `Width` will be a dynamic-index-field. - - - - -{`public class Attributes_ByName extends AbstractIndexCreationTask { - public Attributes_ByName() { - - // For each attribute item, the field name will be the value of field 'propName' - // The field terms will be derived from field 'propValue' - // A regular-index-field (Name) is defined as well - map = - "docs.Products.Select(p => new { " + - " _ = p.attributes.Select(item => this.CreateField(item.propName, item.propValue)), " + - " Name = p.name " + - "})"; + storeAllFields(FieldStorage.NO); + + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + analyze(Constants.Documents.Indexing.Fields.ALL_FIELDS, "StopAnalyzer"); } } -`} - +``` - + +```java +public class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + public Products_ByLocalizedDescription_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (product) { " + + " return { " + + " _: Object.keys(product.descriptions) " + + " .map(function (key) { " + + " return createField( " + + " 'Description_' + key, " + + " product.descriptions[key], " + + " { " + + " indexing: 'Search', " + + " storage: false " + + " }); " + + " }) " + + " }; " + + "})" + )); -* **The query**: - * To get all documents matching a specific attribute property use: - - - -{`List matchingDocuments = session - .query(Product.class, Attributes_ByName.class) - .whereEquals("Width", 10) - .toList(); -`} - - - - -{`// 'Width' is a dynamic-index-field that was indexed from the attributes list -from index 'Attributes/ByName' where Width = 10 -`} - + Map fields = new HashMap<>(); + + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + IndexFieldOptions allFieldsOptions = new IndexFieldOptions(); + allFieldsOptions.setStorage(FieldStorage.NO); + allFieldsOptions.setAnalyzer("StopAnalyzer"); + fields.put(Constants.Documents.Indexing.Fields.ALL_FIELDS, allFieldsOptions); + + setFields(fields); + } +} +``` + + + + +### Which analyzer is used for the query term? +The following applies when a `search()` query targets a **dynamic field**: -## CreateField syntax +| Index configuration | [Configuration key](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) | Analyzer used for the query term | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| No analyzer is explicitly configured for the generated field name | `false` | The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) | +| No analyzer is explicitly configured for the generated field name | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `false` | The analyzer resolved by the normal field fallback path,
e.g. `WhitespaceAnalyzer` | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| The generated field name is explicitly configured,
e.g. `analyze("Description_English", "StopAnalyzer")` | `false` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +| The generated field name is explicitly configured,
e.g. `analyze("Description_English", "StopAnalyzer")` | `true` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | -#### Syntax for LINQ-index: +
- - -{`object CreateField(string name, object value); +
+ + + +#### Syntax for `AbstractIndexCreationTask` + + +```csharp +object CreateField(string name, object value); object CreateField(string name, object value, bool stored, bool analyzed); object CreateField(string name, object value, CreateFieldOptions options); -`} - +``` -#### Syntax for JavaScript-index: +#### Syntax for `AbstractJavaScriptIndexCreationTask` - - -{`createField(fieldName, fieldValue, options); // returns object -`} - + +```js +createField(fieldName, fieldValue, options); // returns object +``` -| Parameters | Type | Description | -|----------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **fieldName** | `string` | Name of the dynamic-index-field | -| **fieldValue** | `object` | Value of the dynamic-index-field
The field Terms are derived from this value. | -| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorate.Yes` | -| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing.Default` (default value)
`false` - `FieldIndexing.Exact`
`true` - `FieldIndexing.Search` | +| Parameters | Type | Description | +|----------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **fieldName** | `string` | Name of the dynamic-index-field | +| **fieldValue** | `object` | Value of the dynamic-index-field
The field terms are derived from this value. | +| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorage.Yes` | +| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing.Default` (default value)
`false` - `FieldIndexing.Exact`
`true` - `FieldIndexing.Search` | | **options** | `CreateFieldOptions` | Dynamic-index-field options | -| CreateFieldOptions | | | -|--------------------|--------------------|----------------------------------------------------------------------------| +| CreateFieldOptions | | | +|--------------------|--------------------|-----------------------------------------------------------------------------------| | **Storage** | `FieldStorage?` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | -| **Indexing** | `FieldIndexing?` | Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | -| **TermVector** | `FieldTermVector?` | Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | +| **Indexing** | `FieldIndexing?` | Sets the field indexing behavior.
Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | +| **TermVector** | `FieldTermVector?` | Sets the field term-vector behavior.
Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | +* The `CreateField` syntax (and the `CreateFieldOptions` shape) above describes what is available inside the server-side `map` string of an `AbstractIndexCreationTask`, + since the Java client sends the `map` source to the server for compilation. + The JavaScript `createField` function is the equivalent for `AbstractJavaScriptIndexCreationTask` map sources. + * All above examples have used the character `_` in the dynamic-index-field definition. However, using `_` is just a convention. Any other string can be used instead. * This property is Not queryable, it is only used in the index definition syntax. - The actual dynamic-index-fields that are generated are defined by the `CreateField` method. + The actual dynamic-index-fields that are generated are defined by the `CreateField` method (or the `createField` JS function). +
+ + - -## Indexed fields & terms view - -The generated dynamic-index-fields and their indexed terms can be viewed in the **Terms View**. -Below are sample index fields & their terms generated from the last example. - +The generated dynamic index fields and their indexed terms can be viewed in the **Terms View** in Studio. +Below are sample index fields & their terms generated from [this example](../../indexes/using-dynamic-fields.mdx#example---list). + ![Figure 1. Go to terms view](../assets/dynamic-index-fields-1.png) - + ![Figure 2. Indexed fields & terms](../assets/dynamic-index-fields-2.png) - - - - + + diff --git a/versioned_docs/version-6.2/indexes/content/_using-dynamic-fields-nodejs.mdx b/versioned_docs/version-6.2/indexes/content/_using-dynamic-fields-nodejs.mdx index b5df2df4a7..c14184c2b5 100644 --- a/versioned_docs/version-6.2/indexes/content/_using-dynamic-fields-nodejs.mdx +++ b/versioned_docs/version-6.2/indexes/content/_using-dynamic-fields-nodejs.mdx @@ -2,533 +2,710 @@ import Admonition from '@theme/Admonition'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; +import ContentFrame from '@site/src/components/ContentFrame'; +import Panel from '@site/src/components/Panel'; -* In RavenDB different documents can have different shapes. - Documents are schemaless - new fields can be added or removed as needed. - -* For such dynamic data, you can define indexes with **dynamic-index-fields**. - -* This allows querying the index on fields that aren't yet known at index creation time, - which is very useful when working on highly dynamic systems. - -* Any value type can be indexed, string, number, date, etc. - -* An index definition can contain both dynamic-index-fields and regular-index-fields. - -* In this page: - - * [Indexing documents fields KEYS](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-keys) - * [Example - index any field under object](../../indexes/using-dynamic-fields.mdx#example---index-any-field-under-object) - * [Example - index any field](../../indexes/using-dynamic-fields.mdx#example---index-any-field) - * [Indexing documents fields VALUES](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-values) - * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) - * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) - * [CreateField syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) - * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields--terms-view) +* In RavenDB, different documents can have different shapes. + Documents are schemaless - new fields can be added or removed as needed. + For such dynamic data, you can define indexes with **dynamic-index-fields**. + +* Dynamic-index-fields are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time, + which is very useful when working with highly dynamic systems. + +* Any value type can be indexed, string, number, date, etc. + An index definition can contain both dynamic-index-fields and regular-index-fields. + +* In this article: + * [Indexing field KEYS](../../indexes/using-dynamic-fields.mdx#indexing-field-keys) + * [Example - index every field under an object](../../indexes/using-dynamic-fields.mdx#example---index-every-field-under-an-object) + * [Example - index every field](../../indexes/using-dynamic-fields.mdx#example---index-every-field) + * [Indexing field VALUES](../../indexes/using-dynamic-fields.mdx#indexing-field-values) + * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) + * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) + * [Indexing dynamic fields for full-text search](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + * [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) + * [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + * [`createField` syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) + * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields-terms-view) -## Indexing documents fields KEYS + - -#### Example - index any field under object -The following allows you to: - -* Index any field that is under the some object from the document. -* After index is deployed, any new field added to the this object will be indexed as well. -**The document**: - - -{`class Product \{ - constructor(id, attributes) \{ - this.id = id; - - // The KEYS under the attributes object will be dynamically indexed - // Fields added to this object after index creation time will also get indexed - this.attributes = attributes; - \} -\} -`} - - + + +### Example - index every field under an object - - -{`// Sample document content -\{ - "attributes": \{ - "color": "Red", - "size": 42 - \} -\} -`} - - + +The following example allows you to: + * Index any field that is under the same object from the document. + * After index is deployed, any new field added to this object will be indexed as well. + -**The index**: +* **The document**: + + + ```js + class Product { + constructor(id, attributes) { + this.id = id; + + // The KEYS under the attributes object will be dynamically indexed + // Fields added to this object after index creation time will also get indexed + this.attributes = attributes; + } + } + ``` + + + + ```json + // Sample document content + { + "attributes": { + "color": "Red", + "size": 42 + } + } + ``` + -* The following index will index any field under the `attributes` object from the document, +* **The index**: + + The following index will index any field under the `attributes` object from the document, a dynamic-index-field will be created for each such field. - New fields added to the object after index creation time will be dynamically indexed as well. - -* The actual dynamic-index-field name on which you can query will be the attribute field **key**. - e.g. Keys `color` & `size` will become the actual dynamic-index-fields. - - - - -{`class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask { - constructor() { - super(); - - const { createField } = this.mapUtils(); - - this.map("Products", p => { - return { - // Call 'createField' to generate dynamic-index-fields from the attributes object keys - // Using '_' is just a convention. Any other string can be used instead of '_' - - // The actual field name will be the key - // The actual field terms will be derived from p.attributes[key] - _: Object.keys(p.attributes).map(key => createField(key, p.attributes[key], { - indexing: "Search", - storage: false, - termVector: null - })) - }; - }); + New fields added to the object after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the attribute field **key**. + E.g., Keys `color` & `size` will become the actual dynamic-index-fields. + + + ```js + class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { + super(); + + const { createField } = this.mapUtils(); + + this.map("Products", p => { + return { + // Call 'createField' to generate dynamic-index-fields + // from the attributes object keys. + + // Using '_' is just a convention. + // Any other string can be used instead of '_' + + // The actual field name will be the key + // The actual field terms will be derived from p.attributes[key] + _: Object.keys(p.attributes).map(key => createField(key, p.attributes[key])) + }; + }); + } } -} -`} - - - - -**The query**: - -* You can now query the generated dynamic-index fields. - Property `_` is Not queryable, it is only used in the index definition syntax. - -* To get all documents with some 'size' use: - - - - -{`const matchingDocuments = session.query({indexName: 'Products_ByAttributeKey'}) - // 'size' is a dynamic-index-field that was indexed from the attributes object - .whereEquals('size', 42) - .all(); -`} - - - - -{`// 'size' is a dynamic-index-field that was indexed from the attributes object -from index 'Products/ByAttributeKey' where size = 42 -`} - - - - - + ``` + + +* **The query**: + + You can now query the generated dynamic-index fields. + The `_` property is Not queryable but used only in the index definition syntax. + + To get all documents with a specific _size_ use: + + + + ```js + const matchingDocuments = await session + .query({ indexName: "Products/ByAttributeKey/JS" }) + // 'size' is a dynamic-index-field that was indexed from the attributes object + .whereEquals("size", 42) + .all(); + ``` + + + ```sql + // 'size' is a dynamic-index-field that was indexed from the attributes object + from index 'Products/ByAttributeKey/JS' where size = 42 + ``` + + + + + + + +### Example - index every field -#### Example - index any field -The following allows you to: - -* Define an index on a collection **without** needing any common structure between the indexed documents. -* After index is deployed, any new field added to the document will be indexed as well. - +The following example allows you to: + * Define an index on a collection **without** needing any common structure between the indexed documents. + * After the index is deployed, any new field added to any document in the collection will be indexed as well. + + - -Consider if that is a true necessity, as indexing every single field can end up costing time and disk space. - +* Use this approach with care. +* Indexing every field can increase indexing time, memory usage, and disk space, + especially for large or frequently updated documents. -**The document**: - - -{`class Product \{ - constructor(id, firstName, lastName, title) \{ - this.id = id; - - // All KEYS in the document will be dynamically indexed - // Fields added to the document after index creation time will also get indexed - this.firstName = firstName; - this.lastName = lastName; - this.title = title; - // ... - \} -\} -`} - - - - - -{`// Sample document content -\{ - "firstName": "John", - "lastName": "Doe", - "title": "Engineer", - // ... -\} -`} - - -**The index**: +* **The document**: + + + ```js + class Product { + constructor(id, firstName, lastName, title) { + this.id = id; + + // All KEYS in the document will be dynamically indexed + // Fields added to the document after index creation time will also get indexed + this.firstName = firstName; + this.lastName = lastName; + this.title = title; + // ... + } + } + ``` + + + + ```json + // Sample document content + { + "firstName": "John", + "lastName": "Doe", + "title": "Engineer", + // ... + } + ``` + -* The following index will index any field from the document, +* **The index**: + + The following index will index any field from the document, a dynamic-index-field will be created for each field. - New fields added to the document after index creation time will be dynamically indexed as well. - -* The actual dynamic-index-field name on which you can query will be the field **key**. - e.g. Keys `firstName` & `lastName` will become the actual dynamic-index-fields. + New fields added to the document after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the field **key**. + E.g., Keys `firstName` & `lastName` will become the actual dynamic-index-fields. + + + ```js + class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { + super(); + + const { createField } = this.mapUtils(); + + // This will index EVERY FIELD under the top level of the document + this.map("Products", p => { + return { + _: Object.keys(p).map(key => createField(key, p[key])) + }; + }); + } + } + ``` + + +* **The query**: + + To get all documents with a specific _lastName_ use: + + + + ```js + const matchingDocuments = await session + .query({ indexName: "Products/ByAnyField/JS" }) + // 'lastName' is a dynamic-index-field that was indexed from the document + .whereEquals("lastName", "Doe") + .all(); + ``` + + + ```sql + // 'lastName' is a dynamic-index-field that was indexed from the document + from index 'Products/ByAnyField/JS' where lastName = "Doe" + ``` + + + + + + + + + + + +### Example - basic + + +The following example shows: + * Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. + * Documents can then be queried based on those indexed values. + * For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---list) below. + - - - -{`class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask { - constructor () { - super(); +* **The document**: + + + ```js + class Product { + constructor(id, productType, pricePerUnit) { + this.id = id; + + // The VALUE of productType will be dynamically indexed + this.productType = productType; + this.pricePerUnit = pricePerUnit; + } + } + ``` + + + + ```json + // Sample document content + { + "productType": "Electronics", + "pricePerUnit": 23 + } + ``` + + +* **The index**: + + The following index will index the **value** of document field 'productType'. + + This value will be the dynamic-index-field name on which you can query. + E.g., Field value `Electronics` will be the dynamic-index-field. + + + ```js + class Products_ByProductType_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { + super(); + + const { createField } = this.mapUtils(); + + this.map("Products", p => { + return { + // The field name will be the value of document field 'productType' + // The field terms will be derived from document field 'pricePerUnit' + _: createField(p.productType, p.pricePerUnit) + }; + }); + } + } + ``` + + +* **The query**: + + To get all documents of some product type having a specific price per unit use: + + + + ```js + const matchingDocuments = await session + .query({ indexName: "Products/ByProductType/JS" }) + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + .whereEquals("Electronics", 23) + .all(); + ``` + + + ```sql + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + from index 'Products/ByProductType/JS' where Electronics = 23 + ``` + + + + + + + +### Example - list - const { createField } = this.mapUtils(); + +The following example allows you to: + * Index **values** from items in a list + * After index is deployed, any item added to this list in the document will be dynamically indexed as well. + - this.map("Products", p => { - return { - // This will index EVERY FIELD under the top level of the document - _: Object.keys(p).map(key => createField(key, p[key], { - indexing: "Search", - storage: true, - termVector: null - })) - }; - }); +* **The document**: + + + ```js + class Product { + constructor(id, name, attributes) { + this.id = id; + this.name = name; + + // For each element in this list, + // the VALUE of property 'propName' will be dynamically indexed. + // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields. + this.attributes = attributes; + } } -} -`} - - - -**The query**: + class Attribute { + constructor(propName, propValue) { + this.propName = propName; + this.propValue = propValue; + } + } + ``` + + + + ```json + // Sample document content + { + "name": "SomeName", + "attributes": [ + { + "propName": "Color", + "propValue": "Blue" + }, + { + "propName": "Width", + "propValue": "10" + }, + { + "propName": "Length", + "propValue": "20" + }, + ... + ] + } + ``` + + +* **The index**: + + The following index will create a dynamic-index-field per item in the document's `attributes` list. + New items added to the attributes list after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the item's propName **value**. + E.g., 'propName' value `Width` will be a dynamic-index-field. + + + ```js + class Attributes_ByName_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { + super(); + + const { createField } = this.mapUtils(); + + this.map("Products", p => { + return { + // Define the dynamic-index-fields by calling 'createField' + // A dynamic-index-field will be generated for each item in 'attributes' + + // For each item, the field name will be the value of field 'propName' + // The field terms will be derived from field 'propValue' + _: p.attributes.map(item => + createField(item.propName, item.propValue)), + + // A regular index field can be defined as well: + name: p.name + }; + }); + } + } + ``` + + +* **The query**: + + To get all documents matching a specific attribute property use: + + + + ```js + const matchingDocuments = await session + .query({ indexName: "Attributes/ByName/JS" }) + // 'Width' is a dynamic-index-field that was indexed from the attributes list + .whereEquals("Width", 10) + .all(); + ``` + + + ```sql + // 'Width' is a dynamic-index-field that was indexed from the attributes list + from index 'Attributes/ByName/JS' where Width = 10 + ``` + + + + + + + + + + + +Dynamic fields can be indexed for [Full-text search](../../indexes/querying/searching.mdx) by setting `indexing: "Search"` in the `createField` options. + +* **At indexing time**: + At indexing time, RavenDB analyzes the dynamic field value and creates the indexed terms. + The analyzer used at indexing time is resolved from the index definition. + You can configure an analyzer for a specific generated field name, or configure `ALL_FIELDS` as a fallback. + See [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) below. + + * If the generated dynamic field name has an explicit analyzer configuration, that analyzer is used. + * Otherwise, if `ALL_FIELDS` has an analyzer configuration, RavenDB uses it as a fallback. + * If no analyzer is configured for the field, RavenDB uses the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + +* **At querying time**: + For full-text search to return the expected results, the analyzer used at indexing time and the analyzer used for the query term should produce compatible terms. + + When a `search()` query targets a dynamic field that does **not** have an explicit analyzer configuration for its generated field name, + RavenDB uses the [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) to analyze the query term by default. + + Set the [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) + configuration key to `true` to make RavenDB use the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) as the fallback analyzer for the query term instead. + + This configuration can be set at different scopes: server-wide, per database, or per index. + The example below sets it at the index scope, in the index definition. + + For a summary of how RavenDB resolves the analyzer used for the query term, + see [Which analyzer is used for the query term](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) below. -* To get all documents with some 'lastName' use: + + +--- + +The following example indexes localized product descriptions as dynamic fields and queries one of those generated fields using full-text search: + +* **The document**: + + + ```js + class Product { + constructor(id, descriptions) { + this.id = id; + this.descriptions = descriptions; + } + } + ``` + + + + ```json + // Sample document content + { + "descriptions": { + "English": "North wind jacket", + "French": "Veste vent du nord" + } + } + ``` + + +* **The index**: + + The following index creates one dynamic field for each entry in the `descriptions` dictionary. + For example, the key `English` creates the dynamic field `Description_English`. + + Each generated dynamic field is indexed for **full-text search**. + + + ```js + class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { + super(); + + const { createField } = this.mapUtils(); + + const useSearchAnalyzerForDynamicFields = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + this.map("Products", product => { + return { + _: Object.keys(product.descriptions).map(key => + createField( + "Description_" + key, + product.descriptions[key], + { + // Index each generated dynamic field for FTS + indexing: "Search", + storage: false + })) + }; + }); + + this.storeAllFields("No"); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + this.configuration[useSearchAnalyzerForDynamicFields] = "true"; + } + } + ``` + - - - -{`const matchingDocuments = session.query({ indexName: 'Products_ByAnyField_JS' }) - // 'lastName' is a dynamic-index-field that was indexed from the document - .whereEquals('lastName', 'Doe') - .all(); -`} - - - - -{`// 'lastName' is a dynamic-index-field that was indexed from the document -from index 'Products/ByAnyField/JS' where lastName = "Doe" -`} - - - +* **Full-text search query**: -
+ Query the generated dynamic field by its field name. + In this example, the query targets the generated field `Description_English`. + Since `Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery` is set to `true`, + the searched term `"north wind"` is analyzed with the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). -## Indexing documents fields VALUES + + + ```js + const results = await session + .query({ indexName: "Products/ByLocalizedDescription/JS" }) + .search("Description_English", "north wind") + .all(); + ``` + - -#### Example - basic -This example shows: - -* Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. -* Documents can then be queried based on those indexed values. -* For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---index-a-list-of-properties) below. -**The document**: - - -{`class Product \{ - constructor(id, productType, pricePerUnit) \{ - this.id = id; - - // The VALUE of productType will be dynamically indexed - this.productType = productType; - this.pricePerUnit = pricePerUnit; - \} -\} -`} - - + + ```sql + from index "Products/ByLocalizedDescription/JS" + where search(Description_English, "north wind") + ``` + + - - -{`// Sample document content -\{ - "productType": "Electronics", - "pricePerUnit": 23 -\} -`} - - +--- -**The index**: + + +### Configuring analyzers for dynamic fields -* The following index will index the **value** of document field 'productType'. +The `createField` options object does **not** let you specify a custom analyzer directly. +However, you can still configure which analyzer RavenDB will use for dynamic fields in the index definition by either: + +* Configuring an analyzer for a specific generated dynamic field name. +* Configuring `ALL_FIELDS` as a fallback for fields that do not have their own explicit analyzer configuration. + +Use `ALL_FIELDS` with care, since it can affect any field that does not have its own explicit analyzer configuration. -* This value will be the dynamic-index-field name on which you can query. - e.g. Field value `Electronics` will be the dynamic-index-field. +--- - - - -{`class Products_ByProductType extends AbstractCsharpIndexCreationTask { - constructor () { - super(); +#### Configure analyzer for a specific dynamic field - // The field name will be the value of document field 'productType' - // The field terms will be derived from document field 'pricePerUnit' - this.map = "docs.Products.Select(p => new { " + - " _ = this.CreateField(p.productType, p.pricePerUnit) " + - "})"; - } -} -`} - - - - -{`class Products_ByProductType_JS extends AbstractJavaScriptIndexCreationTask { - constructor () { + +```js +class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { super(); const { createField } = this.mapUtils(); - this.map("Products", p => { + this.map("Products", product => { return { - _: [ - // The field name will be the value of document field 'productType' - // The field terms will be derived from document field 'pricePerUnit' - createField(p.productType, p.pricePerUnit, { - indexing: "Search", - storage: false, - termVector: null - }) - ] + _: Object.keys(product.descriptions).map(key => + createField( + "Description_" + key, + product.descriptions[key], + { + indexing: "Search", + storage: false + })) }; }); - } -} -`} - - - - -**The query**: - -* To get all documents of some product type having a specific price per unit use: - - - - -{`const matchingDocuments = session.query({ indexName: 'Products_ByProductType' }) - // 'Electronics' is the dynamic-index-field that was indexed from document field 'productType' - .whereEquals('Electronics', 23) - .all(); -`} - - - - -{`// 'Electronics' is the dynamic-index-field that was indexed from document field 'productType' -from index 'Products/ByProductType' where Electronics = 23 -`} - - - - - - -#### Example - list -The following allows you to: - -* Index **values** from items in a list -* After index is deployed, any item added this list in the document will be dynamically indexed as well. -**The document**: - - -{`class Product \{ - constructor(id, name, attributes) \{ - this.id = id; - this.name = name; - - // For each element in this list, the VALUE of property 'propName' will be dynamically indexed - // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields - this.attributes = attributes; - \} -\} - -class Attribute \{ - constructor(propName, propValue) \{ - this.propName = propName; - this.propValue = propValue; - \} -\} -`} - - - - - -{`// Sample document content -\{ - "name": "SomeName", - "attributes": [ - \{ - "propName": "Color", - "propValue": "Blue" - \}, - \{ - "propName": "Width", - "propValue": "10" - \}, - \{ - "propName": "Length", - "propValue": "20" - \}, - ... - ] -\} -`} - - - -**The index**: - -* The following index will create a dynamic-index-field per item in the document's `attributes` list. - New items added to the attributes list after index creation time will be dynamically indexed as well. - -* The actual dynamic-index-field name on which you can query will be the item's propName **value**. - e.g. 'propName' value `Width` will be a dynamic-index-field. - - - - -{`class Attributes_ByName extends AbstractCsharpIndexCreationTask -{ - constructor () { - super(); + this.storeAllFields("No"); - // For each attribute item, the field name will be the value of field 'propName' - // The field terms will be derived from field 'propValue' - // A regular-index-field (Name) is defined as well - this.map = - "docs.Products.Select(p => new { " + - " _ = p.attributes.Select(item => this.CreateField(item.propName, item.propValue)), " + - " Name = p.name " + - "})"; + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + this.analyze("Description_English", "StopAnalyzer"); } } -`} - +``` - - -{`class Attributes_ByName_JS extends AbstractJavaScriptIndexCreationTask { - constructor () { + +#### Configure a fallback analyzer for all fields + + +```js +class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { super(); const { createField } = this.mapUtils(); - this.map("Products", p => { + this.map("Products", product => { return { - // For each item, the field name will be the value of field 'propName' - // The field terms will be derived from field 'propValue' - _: p.attributes.map(item => createField(item.propName, item.propValue, { - indexing: "Search", - storage: true, - termVector: null - })), - - // A regular-index-field can be defined as well: - Name: p.name + _: Object.keys(product.descriptions).map(key => + createField( + "Description_" + key, + product.descriptions[key], + { + indexing: "Search", + storage: false + })) }; }); - } -} -`} - - - - -**The query**: -* To get all documents matching a specific attribute property use: + this.storeAllFields("No"); - - - -{`const matchingDocuments = session.query({ indexName: 'Attributes/ByName' }) - // 'Width' is a dynamic-index-field that was indexed from the attributes list - .whereEquals('Width', 10) - .all(); -`} - - - - -{`// 'Width' is a dynamic-index-field that was indexed from the attributes list -from index 'Attributes/ByName' where Width = 10 -`} - + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + this.analyze(CONSTANTS.Documents.Indexing.Fields.ALL_FIELDS, "StopAnalyzer"); + } +} +``` - - - + + + + +### Which analyzer is used for the query term? -## CreateField syntax +The following applies when a `search()` query targets a **dynamic field**: -#### Syntax for LINQ-index: +| Index configuration | [Configuration key](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) | Analyzer used for the query term | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| No analyzer is explicitly configured for the generated field name | `false` | The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) | +| No analyzer is explicitly configured for the generated field name | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `false` | The analyzer resolved by the normal field fallback path,
e.g. `WhitespaceAnalyzer` | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| The generated field name is explicitly configured,
e.g. `this.analyze("Description_English", "StopAnalyzer")` | `false` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +| The generated field name is explicitly configured,
e.g. `this.analyze("Description_English", "StopAnalyzer")` | `true` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | - - -{`object CreateField(string name, object value); +
-object CreateField(string name, object value, bool stored, bool analyzed); + -object CreateField(string name, object value, CreateFieldOptions options); -`} - - + -#### Syntax for JavaScript-index: +#### Syntax: - - -{`createField(fieldName, fieldValue, options); // returns object -`} - + +```js +createField(fieldName, fieldValue, options); // returns object +``` -| Parameters | Type | Description | -|----------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **fieldName** | `string` | Name of the dynamic-index-field | -| **fieldValue** | `object` | Value of the dynamic-index-field
The field Terms are derived from this value. | -| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorate.Yes` | -| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing.Default` (default value)
`false` - `FieldIndexing.Exact`
`true` - `FieldIndexing.Search` | -| **options** | `CreateFieldOptions` | Dynamic-index-field options | +| Parameters | Type | Description | +|----------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **fieldName** | `string` | Name of the dynamic-index-field | +| **fieldValue** | `object` | Value of the dynamic-index-field
The field terms are derived from this value. | +| **options** | `object` | Dynamic-index-field options | -| CreateFieldOptions | | | -|--------------------|--------------------|----------------------------------------------------------------------------| -| **Storage** | `FieldStorage?` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | -| **Indexing** | `FieldIndexing?` | Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | -| **TermVector** | `FieldTermVector?` | Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | +| options object | | | +|-----------------|--------------------|-----------------------------------------------------------------------------------| +| **storage** | `boolean` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | +| **indexing** | `FieldIndexing` | Sets the field indexing behavior.
Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | +| **termVector** | `FieldTermVector` | Sets the field term-vector behavior.
Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | @@ -536,21 +713,19 @@ object CreateField(string name, object value, CreateFieldOptions options); However, using `_` is just a convention. Any other string can be used instead. * This property is Not queryable, it is only used in the index definition syntax. - The actual dynamic-index-fields that are generated are defined by the `CreateField` method. + The actual dynamic-index-fields that are generated are defined by the `createField` method. +
+ + - -## Indexed fields & terms view - -The generated dynamic-index-fields and their indexed terms can be viewed in the **Terms View**. -Below are sample index fields & their terms generated from the last example. - +The generated dynamic index fields and their indexed terms can be viewed in the **Terms View** in Studio. +Below are sample index fields & their terms generated from [this example](../../indexes/using-dynamic-fields.mdx#example---list). + ![Figure 1. Go to terms view](../assets/dynamic-index-fields-1.png) - + ![Figure 2. Indexed fields & terms](../assets/dynamic-index-fields-2.png) - - - - + + diff --git a/versioned_docs/version-6.2/indexes/content/_using-dynamic-fields-php.mdx b/versioned_docs/version-6.2/indexes/content/_using-dynamic-fields-php.mdx index 09c58cc9fc..6992310715 100644 --- a/versioned_docs/version-6.2/indexes/content/_using-dynamic-fields-php.mdx +++ b/versioned_docs/version-6.2/indexes/content/_using-dynamic-fields-php.mdx @@ -2,537 +2,969 @@ import Admonition from '@theme/Admonition'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; +import ContentFrame from '@site/src/components/ContentFrame'; +import Panel from '@site/src/components/Panel'; -* In RavenDB different documents can have different shapes. - Documents are schemaless - new fields can be added or removed as needed. +* In RavenDB, different documents can have different shapes. + Documents are schemaless - new fields can be added or removed as needed. + For such dynamic data, you can define indexes with **dynamic-index-fields**. + +* Dynamic-index-fields are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time, + which is very useful when working with highly dynamic systems. + +* Any value type can be indexed, string, number, date, etc. + An index definition can contain both dynamic-index-fields and regular-index-fields. + +* In this article: + * [Indexing field KEYS](../../indexes/using-dynamic-fields.mdx#indexing-field-keys) + * [Example - index every field under an object](../../indexes/using-dynamic-fields.mdx#example---index-every-field-under-an-object) + * [Example - index every field](../../indexes/using-dynamic-fields.mdx#example---index-every-field) + * [Indexing field VALUES](../../indexes/using-dynamic-fields.mdx#indexing-field-values) + * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) + * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) + * [Indexing dynamic fields for full-text search](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + * [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) + * [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + * [`CreateField` syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) + * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields-terms-view) -* For such dynamic data, you can define indexes with **dynamic-index-fields**. - -* This allows querying the index on fields that aren't yet known at index creation time, - which is very useful when working on highly dynamic systems. - -* Any value type can be indexed, string, number, date, etc. - -* An index definition can contain both dynamic-index-fields and regular-index-fields. + -* In this page: + - * [Indexing documents fields KEYS](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-keys) - * [Example - index any field under object](../../indexes/using-dynamic-fields.mdx#example---index-any-field-under-object) - * [Example - index any field](../../indexes/using-dynamic-fields.mdx#example---index-any-field) - * [Indexing documents fields VALUES](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-values) - * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) - * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) - * [CreateField syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) - * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields--terms-view) + + +### Example - index every field under an object + +The following example allows you to: + * Index any field that is under the same object from the document. + * After index is deployed, any new field added to this object will be indexed as well. -## Indexing documents fields KEYS - -## Example - index any field under object +* **The document**: + + + ```php + use Ds\Map as DSMap; - + class Product + { + public ?string $id = null; -* Index any field that is under the some object from the document. -* After index is deployed, any new field added to the this object will be indexed as well. + // The KEYS under the attributes object will be dynamically indexed. + // Fields added to this object after index creation time will also get indexed. + public ?DSMap $attributes = null; - - -* **The document**: - - -{`use Ds\\Map as DSMap; - -class Product -\{ - private ?string $id = null; - - // The KEYS under the Attributes object will be dynamically indexed - // Fields added to this object after index creation time will also get indexed - public ?DSMap $attributes = null; -\} -`} - - - - -{`// Sample document content -\{ - "Attributes": \{ - "Color": "Red", - "Size": 42 - \} -\} -`} - - + // ... getters and setters + } + ``` + + + + ```json + // Sample document content + { + "attributes": { + "Color": "Red", + "Size": 42 + } + } + ``` + * **The index**: - The below index will index any field under the `Attributes` object from the document, + + The following index will index any field under the `attributes` object from the document, a dynamic-index-field will be created for each such field. New fields added to the object after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the attribute field **key**. - E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. + The actual dynamic-index-field name on which you can query will be the attribute field **key**. + E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. - - - -{`class Products_ByAttributeKey extends AbstractIndexCreationTask -{ - public function __construct() + + + ```php + class Products_ByAttributeKey extends AbstractIndexCreationTask { - parent::__construct(); - - $this->map = "from p in docs.Products select new {" . - "_ = p.attributes.Select(item => CreateField(item.Key, item.Value))" . - "}"; + public function __construct() + { + parent::__construct(); + + // Call 'CreateField' to generate dynamic-index-fields + // from the attributes object keys. + // + // Using '_' is just a convention. + // Any other string can be used instead of '_'. + // + // The actual field name will be 'item.Key'. + // The actual field terms will be derived from 'item.Value'. + $this->map = + "from p in docs.Products " . + "select new { " . + " _ = p.attributes.Select(item => CreateField(item.Key, item.Value)) " . + "}"; + } } -} -`} - - - - -{`class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask -{ - public function __construct() + ``` + + + ```php + class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask { - parent::__construct(); - - $this->setMaps([ - "map('Products', function (p) { " . - " return { " . - " _: Object.keys(p.attributes).map(key => createField(key, p.attributes[key], " . - " { indexing: 'Search', storage: false, termVector: null })) " . - " }; " . - "}) " - ]); + public function __construct() + { + parent::__construct(); + + $this->setMaps([ + "map('Products', function (p) { + return { + // The field name will be the key + // The field terms will be derived from the corresponding value + _: Object.keys(p.attributes) + .map(key => createField(key, p.attributes[key], null)) + }; + })" + ]); + } } -} -`} - - - - + ``` + + * **The query**: - * You can now query the generated dynamic-index fields. - * To get all documents with some 'size' use: - - - -{`$matchingDocuments = $session - ->advanced() - ->documentQuery(Product::class, Products_ByAttributeKey::class) - // 'Size' is a dynamic-index-field that was indexed from the Attributes object - ->whereEquals("Size", 42) - ->toList(); -`} - - - - -{`// 'Size' is a dynamic-index-field that was indexed from the Attributes object -from index 'Products/ByAttributeKey' where Size = 42 -`} - - - - -## Example - index any field - - + + You can now query the generated dynamic-index fields. + The `_` property is Not queryable but used only in the index definition syntax. + + To get all documents with a specific _Size_ use: + + + + ```php + /** @var array $matchingDocuments */ + $matchingDocuments = $session + ->advanced() + ->documentQuery(Product::class, Products_ByAttributeKey::class) + // 'Size' is a dynamic-index-field that was indexed from the attributes object + ->whereEquals("Size", 42) + ->toList(); + ``` + + + ```sql + // 'Size' is a dynamic-index-field that was indexed from the attributes object + from index 'Products/ByAttributeKey' where Size = 42 + ``` + + + + + Dynamic index fields are queried with _documentQuery_ or _RQL_ because the field names (e.g. `Size`, `Color`) + are generated at indexing time and are not properties on the _Product_ class. + + Use the string-based `whereEquals("Size", 42)` or RQL `where Size = 42` instead. + + + + + + +### Example - index every field + +The following example allows you to: * Define an index on a collection **without** needing any common structure between the indexed documents. - * After index is deployed, any new field added to the document will be indexed as well. - + * After the index is deployed, any new field added to any document in the collection will be indexed as well. - + -Consider whether this is really necessary, as indexing every single field can end up costing time and disk space. +* Use this approach with care. +* Indexing every field can increase indexing time, memory usage, and disk space, + especially for large or frequently updated documents. * **The document**: - - -{`class Product -\{ - private ?string $id = null; - - // All KEYS in the document will be dynamically indexed - // Fields added to the document after index creation time will also get indexed - public ?string $firstName = null; - public ?string $lastName = null; - public ?string $title = null; - - // ... getters and setters -\} -`} - - + + + ```php + class Product + { + public ?string $id = null; - - -{`// Sample document content - \{ - "FirstName": "John", - "LastName": "Doe", - "Title": "Engineer", + // All KEYS in the document will be dynamically indexed. + // Fields added to the document after index creation time will also get indexed. + public ?string $firstName = null; + public ?string $lastName = null; + public ?string $title = null; // ... -\} -`} - - + + // ... getters and setters + } + ``` + + + + ```json + // Sample document content + { + "firstName": "John", + "lastName": "Doe", + "title": "Engineer" + // ... + } + ``` + * **The index**: - The below index will index any field from the document, + + The following index will index any field from the document, a dynamic-index-field will be created for each field. New fields added to the document after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the field **key**. - E.g., Keys `FirstName` & `LastName` will become the actual dynamic-index-fields. - - - - -{`class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask -{ - public function __construct() + The actual dynamic-index-field name on which you can query will be the field **key**. + E.g., Keys `firstName` & `lastName` will become the actual dynamic-index-fields. + + The example uses a JavaScript index because it enumerates all top-level document fields at indexing time with `Object.keys(...)`. + LINQ indexes typically create dynamic fields from a known enumerable structure, such as a dictionary or collection. + + + + ```php + class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask { - parent::__construct(); - - // This will index EVERY FIELD under the top level of the document - $this->setMaps([ - "map('Products', function (p) { - return { - _: Object.keys(p).map(key => createField(key, p[key], - { indexing: 'Search', storage: true, termVector: null })) - } - })" - ]); + public function __construct() + { + parent::__construct(); + + // This will index EVERY FIELD under the top level of the document + $this->setMaps([ + "map('Products', function (p) { + return { + _: Object.keys(p).map(key => createField(key, p[key], null)) + }; + })" + ]); + } } -} -`} - - - + ``` + + * **The query**: - * To get all documents with some 'LastName' use: - - - -{`$matchingDocuments = $session - ->advanced() - ->documentQuery(Product::class, Products_ByAnyField_JS::class) - // 'LastName' is a dynamic-index-field that was indexed from the document - ->whereEquals("LastName", "Doe") - ->toList(); -`} - - - - -{`// 'LastName' is a dynamic-index-field that was indexed from the document -from index 'Products/ByAnyField/JS' where LastName = "Doe" -`} - - - + + To get all documents with a specific _lastName_ use: + + + + ```php + /** @var array $matchingDocuments */ + $matchingDocuments = $session + ->advanced() + ->documentQuery(Product::class, Products_ByAnyField_JS::class) + // 'lastName' is a dynamic-index-field that was indexed from the document + ->whereEquals("lastName", "Doe") + ->toList(); + ``` + + + ```sql + // 'lastName' is a dynamic-index-field that was indexed from the document + from index 'Products/ByAnyField/JS' where lastName = "Doe" + ``` + + + + + + + + + + + +### Example - basic + + +The following example shows: + * Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. + * Documents can then be queried based on those indexed values. + * For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---list) below. + +* **The document**: + + + ```php + class Product + { + public ?string $id = null; + // The VALUE of productType will be dynamically indexed + public ?string $productType = null; + public ?int $pricePerUnit = null; -## Indexing documents fields VALUES + // ... getters and setters + } + ``` + + + + ```json + // Sample document content + { + "productType": "Electronics", + "pricePerUnit": 23 + } + ``` + -## Example - basic +* **The index**: + + The following index will index the **value** of document field 'productType'. + + This value will be the dynamic-index-field name on which you can query. + E.g., Field value `Electronics` will be the dynamic-index-field. - + + + ```php + class Products_ByProductType extends AbstractIndexCreationTask + { + public function __construct() + { + parent::__construct(); + + // Call 'CreateField' to generate the dynamic-index-fields. + // The field name will be the value of document field 'productType'. + // The field terms will be derived from document field 'pricePerUnit'. + $this->map = + "from p in docs.Products " . + "select new { " . + " _ = CreateField(p.productType, p.pricePerUnit) " . + "}"; + } + } + ``` + + + ```php + class Products_ByProductType_JS extends AbstractJavaScriptIndexCreationTask + { + public function __construct() + { + parent::__construct(); + + $this->setMaps([ + "map('Products', function (p) { + return { + // The field name will be the value of document field 'productType' + // The field terms will be derived from document field 'pricePerUnit' + _: createField(p.productType, p.pricePerUnit, null) + }; + })" + ]); + } + } + ``` + + -* Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. -* Documents can then be queried based on those indexed values. +* **The query**: + + To get all documents of some product type having a specific price per unit use: + + + + ```php + /** @var array $matchingDocuments */ + $matchingDocuments = $session + ->advanced() + ->documentQuery(Product::class, Products_ByProductType::class) + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + ->whereEquals("Electronics", 23) + ->toList(); + ``` + + + ```sql + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + from index 'Products/ByProductType' where Electronics = 23 + ``` + + + + + + + +### Example - list + +The following example allows you to: + * Index **values** from items in a list + * After index is deployed, any item added to this list in the document will be dynamically indexed as well. * **The document**: - - -{`class Product -\{ - public ?string $id = null; - - // The VALUE of ProductType will be dynamically indexed - public ?string $productType = null; - public ?int $pricePerUnit = null; - - // ... getters and setters -\} -`} - - + + + ```php + class Product + { + public ?string $id = null; + public ?string $name = null; - - -{`// Sample document content -\{ - "ProductType": "Electronics", - "PricePerUnit": 23 -\} -`} - - + // For each element in this list, + // the VALUE of property 'propName' will be dynamically indexed. + // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields. + public ?AttributeList $attributes = null; + + // ... getters and setters + } + + class Attribute + { + public ?string $propName = null; + public ?string $propValue = null; + + // ... getters and setters + } + + class AttributeList extends TypedList + { + protected function __construct() + { + parent::__construct(Attribute::class); + } + } + ``` + + + + ```json + // Sample document content + { + "name": "SomeName", + "attributes": [ + { + "propName": "Color", + "propValue": "Blue" + }, + { + "propName": "Width", + "propValue": "10" + }, + { + "propName": "Length", + "propValue": "20" + } + // ... + ] + } + ``` + * **The index**: - The below index will index the **value** of document field 'ProductType'. + + The following index will create a dynamic-index-field per item in the document's `attributes` list. + New items added to the attributes list after index creation time will be dynamically indexed as well. - This value will be the dynamic-index-field name on which you can query. - E.g., Field value `Electronics` will be the dynamic-index-field. + The actual dynamic-index-field name on which you can query will be the item's propName **value**. + E.g., 'propName' value `Width` will be a dynamic-index-field. + + + + ```php + class Attributes_ByName extends AbstractIndexCreationTask + { + public function __construct() + { + parent::__construct(); + + // Define the dynamic-index-fields by calling 'CreateField'. + // A dynamic-index-field will be generated for each item in 'attributes'. + // + // For each item, the field name will be the value of field 'propName'. + // The field terms will be derived from field 'propValue'. + $this->map = + "from p in docs.Products " . + "select new { " . + " _ = p.attributes.Select(item => CreateField(item.propName, item.propValue)), " . + " Name = p.name " . + "}"; + } + } + ``` + + + ```php + class Attributes_ByName_JS extends AbstractJavaScriptIndexCreationTask + { + public function __construct() + { + parent::__construct(); + + $this->setMaps([ + "map('Products', function (p) { + return { + // For each item, + // the field name will be the value of field 'propName' + // The field terms will be derived from field 'propValue' + _: p.attributes.map(item => createField( + item.propName, item.propValue, null)), + + // A regular index field can be defined as well: + Name: p.name + }; + })" + ]); + } + } + ``` + + + +* **The query**: + + To get all documents matching a specific attribute property use: + + + + ```php + /** @var array $matchingDocuments */ + $matchingDocuments = $session + ->advanced() + ->documentQuery(Product::class, Attributes_ByName::class) + // 'Width' is a dynamic-index-field that was indexed from the attributes list + ->whereEquals("Width", 10) + ->toList(); + ``` + + + ```sql + // 'Width' is a dynamic-index-field that was indexed from the attributes list + from index 'Attributes/ByName' where Width = 10 + ``` + + + + + + + + + + + +Dynamic fields can be indexed for [Full-text search](../../indexes/querying/searching.mdx) by setting `FieldIndexing::search()` in `CreateFieldOptions`. + +* **At indexing time**: + At indexing time, RavenDB analyzes the dynamic field value and creates the indexed terms. + The analyzer used at indexing time is resolved from the index definition. + You can configure an analyzer for a specific generated field name, or configure `ALL_FIELDS` as a fallback. + See [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) below. + + * If the generated dynamic field name has an explicit analyzer configuration, that analyzer is used. + * Otherwise, if `ALL_FIELDS` has an analyzer configuration, RavenDB uses it as a fallback. + * If no analyzer is configured for the field, RavenDB uses the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + +* **At querying time**: + For full-text search to return the expected results, the analyzer used at indexing time and the analyzer used for the query term should produce compatible terms. + + When a `search()` query targets a dynamic field that does **not** have an explicit analyzer configuration for its generated field name, + RavenDB uses the [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) to analyze the query term by default. + + Set the [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) + configuration key to `true` to make RavenDB use the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) as the fallback analyzer for the query term instead. + + This configuration can be set at different scopes: server-wide, per database, or per index. + The example below sets it at the index scope, in the index definition. + + For a summary of how RavenDB resolves the analyzer used for the query term, + see [Which analyzer is used for the query term](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) below. + + + +--- + +The following example indexes localized product descriptions as dynamic fields and queries one of those generated fields using full-text search: + +* **The document**: + + + ```php + use Ds\Map as DSMap; + + class Product + { + public ?string $id = null; + public ?DSMap $descriptions = null; + + // ... getters and setters + } + ``` + + + + ```json + // Sample document content + { + "descriptions": { + "English": "North wind jacket", + "French": "Veste vent du nord" + } + } + ``` + + +* **The index**: + + The following index creates one dynamic field for each entry in the `descriptions` dictionary. + For example, the key `English` creates the dynamic field `Description_English`. - + Each generated dynamic field is indexed for **full-text search**. + + + + ```php + class Products_ByLocalizedDescription extends AbstractIndexCreationTask + { + private const USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public function __construct() + { + parent::__construct(); + + $this->map = + "from product in docs.Products " . + "select new { " . + " _ = product.descriptions.Select(x => " . + " CreateField(" . + " \"Description_\" + x.Key, " . + " x.Value, " . + " new CreateFieldOptions { " . + // Index each generated dynamic field for FTS + " Indexing = FieldIndexing.Search, " . + " Storage = FieldStorage.No " . + " })) " . + "}"; + + $this->storeAllFields(FieldStorage::no()); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + $this->getConfiguration()[self::USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS] = "true"; + } + } + ``` + + + ```php + class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask + { + private const USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public function __construct() + { + parent::__construct(); + + $this->setMaps([ + "map('Products', function (product) { + return { + _: Object.keys(product.descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.descriptions[key], + { + // Index each generated dynamic field for FTS + indexing: 'Search', + storage: 'No' + }); + }) + }; + })" + ]); + + // Apply a storage default to every generated field + $fields = new IndexFieldOptionsArray(); + $allFieldsOptions = new IndexFieldOptions(); + $allFieldsOptions->setStorage(FieldStorage::no()); + $fields->offsetSet(DocumentsIndexingFields::ALL_FIELDS, $allFieldsOptions); + $this->setFields($fields); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + $this->getConfiguration()[self::USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS] = "true"; + } + } + ``` + + + +* **Full-text search query**: + + Query the generated dynamic field by its field name. + + In this example, the query targets the generated field `Description_English`. + + Since `Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery` is set to `true`, + the searched term `"north wind"` is analyzed with the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + + + + ```php + /** @var array $results */ + $results = $session->advanced() + ->documentQuery(Product::class, Products_ByLocalizedDescription::class) + ->search("Description_English", "north wind") + ->toList(); + ``` + + + + ```sql + from index "Products/ByLocalizedDescription" + where search(Description_English, "north wind") + ``` + + + +--- + + + +### Configuring analyzers for dynamic fields + +`CreateFieldOptions` does **not** let you specify a custom analyzer directly. +However, you can still configure which analyzer RavenDB will use for dynamic fields in the index definition by either: + +* Configuring an analyzer for a specific generated dynamic field name. +* Configuring `ALL_FIELDS` as a fallback for fields that do not have their own explicit analyzer configuration. + +Use `ALL_FIELDS` with care, since it can affect any field that does not have its own explicit analyzer configuration. + +--- + +#### Configure analyzer for a specific dynamic field + + - -{`class Products_ByProductType extends AbstractIndexCreationTask +```php +class Products_ByLocalizedDescription extends AbstractIndexCreationTask { public function __construct() { parent::__construct(); - // Call 'CreateField' to generate the dynamic-index-fields - // The field name will be the value of document field 'ProductType' - // The field terms will be derived from document field 'PricePerUnit' - $this->map = "docs.Products.Select(p => new { " . - " _ = this.CreateField(p.productType, p.pricePerUnit) " . - "})"; + $this->map = + "from product in docs.Products " . + "select new { " . + " _ = product.descriptions.Select(x => " . + " CreateField(" . + " \"Description_\" + x.Key, " . + " x.Value, " . + " new CreateFieldOptions { " . + " Indexing = FieldIndexing.Search, " . + " Storage = FieldStorage.No " . + " })) " . + "}"; + + $this->storeAllFields(FieldStorage::no()); + + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + $this->analyze("Description_English", "StopAnalyzer"); } } -`} - +``` - - -{`class Products_ByProductType_JS extends AbstractJavaScriptIndexCreationTask + +```php +class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { public function __construct() { parent::__construct(); $this->setMaps([ - "map('Products', function (p) { + "map('Products', function (product) { return { - _: createField(p.ProductType, p.PricePerUnit, - { indexing: 'Search', storage: true, termVector: null }) + _: Object.keys(product.descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; })" ]); - } -} -`} - - - - -* **The query**: - * To get all documents of some product type having a specific price per unit use: - - - -{`$matchingDocuments = $session - ->advanced() - ->documentQuery(Product::class, Products_ByProductType::class) -// 'Electronics' is the dynamic-index-field that was indexed from document field 'ProductType' -->whereEquals("Electronics", 23) -->toList(); -`} - - - - -{`// 'Electronics' is the dynamic-index-field that was indexed from document field 'ProductType' -from index 'Products/ByProductType' where Electronics = 23 -`} - - - - -## Example - list - - -* Index **values** from items in a list -* After index is deployed, any item added this list in the document will be dynamically indexed as well. + // Apply a storage default to every generated field + + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + $fields = new IndexFieldOptionsArray(); - + $allFieldsOptions = new IndexFieldOptions(); + $allFieldsOptions->setStorage(FieldStorage::no()); + $fields->offsetSet(DocumentsIndexingFields::ALL_FIELDS, $allFieldsOptions); -* **The document**: - - -{`class Product -\{ - public ?string $id = null; - public ?string $name = null; - - // For each element in this list, the VALUE of property 'PropName' will be dynamically indexed - // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields - public ?AttributeList $attributes = null; - - // ... getters and setters -\} - -class Attribute -\{ - public ?string $propName = null; - public ?string $propValue = null; - - // ... getters and setters -\} - -class AttributeList extends TypedList -\{ - protected function __construct() - \{ - parent::__construct(Attribute::class); - \} -\} -`} - - + $englishFieldOptions = new IndexFieldOptions(); + $englishFieldOptions->setAnalyzer("StopAnalyzer"); + $fields->offsetSet("Description_English", $englishFieldOptions); - - -{`// Sample document content -\{ -Name": "SomeName", -Attributes": [ - \{ - "PropName": "Color", - "PropValue": "Blue" - \}, - \{ - "PropName": "Width", - "PropValue": "10" - \}, - \{ - "PropName": "Length", - "PropValue": "20" - \}, - ... - -\} -`} - + $this->setFields($fields); + } +} +``` + -* **The index**: - The below index will create a dynamic-index-field per item in the document's `Attributes` list. - New items added to the Attributes list after index creation time will be dynamically indexed as well. - - The actual dynamic-index-field name on which you can query will be the item's PropName **value**. - E.g., 'PropName' value `width` will be a dynamic-index-field. +#### Configure a fallback analyzer for all fields - + - -{`class Attributes_ByName extends AbstractIndexCreationTask +```php +class Products_ByLocalizedDescription extends AbstractIndexCreationTask { public function __construct() { parent::__construct(); - // Define the dynamic-index-fields by calling CreateField - // A dynamic-index-field will be generated for each item in the Attributes list + $this->map = + "from product in docs.Products " . + "select new { " . + " _ = product.descriptions.Select(x => " . + " CreateField(" . + " \"Description_\" + x.Key, " . + " x.Value, " . + " new CreateFieldOptions { " . + " Indexing = FieldIndexing.Search, " . + " Storage = FieldStorage.No " . + " })) " . + "}"; - // For each item, the field name will be the value of field 'PropName' - // The field terms will be derived from field 'PropValue' + $this->storeAllFields(FieldStorage::no()); - $this->map = - "docs.Products.Select(p => new { " . - " _ = p.attributes.Select(item => this.CreateField(item.propName, item.propValue)), " . - " Name = p.name " . - "})"; + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + $this->analyze(DocumentsIndexingFields::ALL_FIELDS, "StopAnalyzer"); } } -`} - +``` - - -{`class Attributes_ByName_JS extends AbstractJavaScriptIndexCreationTask + +```php +class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { public function __construct() { parent::__construct(); $this->setMaps([ - "map('Products', function (p) { + "map('Products', function (product) { return { - _: p.Attributes.map(item => createField(item.PropName, item.PropValue, - { indexing: 'Search', storage: true, termVector: null })), - Name: p.Name + _: Object.keys(product.descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; })" ]); + + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + $fields = new IndexFieldOptionsArray(); + $allFieldsOptions = new IndexFieldOptions(); + $allFieldsOptions->setStorage(FieldStorage::no()); + $allFieldsOptions->setAnalyzer("StopAnalyzer"); + $fields->offsetSet(DocumentsIndexingFields::ALL_FIELDS, $allFieldsOptions); + $this->setFields($fields); } } -`} - - - - -* **The query**: - To get all documents matching a specific attribute property use: - - - -{`/** @var array $matchingDocuments */ -$matchingDocuments = $session - ->advanced() - ->documentQuery(Product::class, Attributes_ByName::class) - // 'Width' is a dynamic-index-field that was indexed from the Attributes list - ->whereEquals("Width", 10) - ->toList(); -`} - - - - -{`// 'Width' is a dynamic-index-field that was indexed from the Attributes list -from index 'Attributes/ByName' where Width = 10 -`} - +``` + + + + +### Which analyzer is used for the query term? +The following applies when a `search()` query targets a **dynamic field**: -## CreateField syntax +| Index configuration | [Configuration key](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) | Analyzer used for the query term | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| No analyzer is explicitly configured for the generated field name | `false` | The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) | +| No analyzer is explicitly configured for the generated field name | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `false` | The analyzer resolved by the normal field fallback path,
e.g. `WhitespaceAnalyzer` | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| The generated field name is explicitly configured,
e.g. `$this->analyze("Description_English", "StopAnalyzer")` | `false` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +| The generated field name is explicitly configured,
e.g. `$this->analyze("Description_English", "StopAnalyzer")` | `true` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | - - -{`object CreateField(string name, object value); +
+ +
+ + + +#### Syntax for LINQ-index: + + +```php +object CreateField(string name, object value); object CreateField(string name, object value, bool stored, bool analyzed); object CreateField(string name, object value, CreateFieldOptions options); -`} - +``` -| Parameters | Type | Description | -|------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **name** | `string` | Name of the dynamic-index-field | -| **value** | `object` | Value of the dynamic-index-field
The field Terms are derived from this value. | -| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`False` - will set `FieldStorage.NO` (default value)
`True` - will set `FieldStorate.YES` | -| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`None` - `FieldIndexing.Default` (default value)
`False` - `FieldIndexing.Exact`
`True` - `FieldIndexing.Search` | -| **options** | `CreateFieldOptions` | Dynamic-index-field options | +#### Syntax for JavaScript-index: -| CreateFieldOptions | | | -|--------------------|--------------------|----------------------------------------------------------------------------| -| **Storage** | `FieldStorage` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | -| **Indexing** | `FieldIndexing` | | -| **TermVector** | `FieldTermVector` | | + +```js +createField(fieldName, fieldValue, options); // returns object +``` + + +| Parameters | Type | Description | +|----------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **fieldName** | `string` | Name of the dynamic-index-field | +| **fieldValue** | `object` | Value of the dynamic-index-field
The field terms are derived from this value. | +| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage::no()` (default value)
`true` - will set `FieldStorage::yes()` | +| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing::default()` (default value)
`false` - `FieldIndexing::exact()`
`true` - `FieldIndexing::search()` | +| **options** | `CreateFieldOptions` | Dynamic-index-field options | + +| CreateFieldOptions | | | +|--------------------|--------------------|-----------------------------------------------------------------------------------| +| **Storage** | `FieldStorage?` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | +| **Indexing** | `FieldIndexing?` | Sets the field indexing behavior.
Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | +| **TermVector** | `FieldTermVector?` | Sets the field term-vector behavior.
Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | @@ -544,17 +976,15 @@ object CreateField(string name, object value, CreateFieldOptions options); +
+ + - -## Indexed fields & terms view - -The generated dynamic-index-fields and their indexed terms can be viewed in the **Terms View**. -Below are sample index fields & their terms generated from the last example. - +The generated dynamic index fields and their indexed terms can be viewed in the **Terms View** in Studio. +Below are sample index fields & their terms generated from [this example](../../indexes/using-dynamic-fields.mdx#example---list). + ![Figure 1. Go to terms view](../assets/dynamic-index-fields-1.png) - + ![Figure 2. Indexed fields & terms](../assets/dynamic-index-fields-2.png) - - - - + + diff --git a/versioned_docs/version-6.2/indexes/content/_using-dynamic-fields-python.mdx b/versioned_docs/version-6.2/indexes/content/_using-dynamic-fields-python.mdx index 69514a29e2..6862d18067 100644 --- a/versioned_docs/version-6.2/indexes/content/_using-dynamic-fields-python.mdx +++ b/versioned_docs/version-6.2/indexes/content/_using-dynamic-fields-python.mdx @@ -1,490 +1,886 @@ import Admonition from '@theme/Admonition'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -import CodeBlock from '@theme/CodeBlock'; +import ContentFrame from '@site/src/components/ContentFrame'; +import Panel from '@site/src/components/Panel'; -* In RavenDB different documents can have different shapes. - Documents are schemaless - new fields can be added or removed as needed. - -* For such dynamic data, you can define indexes with **dynamic-index-fields**. - -* This allows querying the index on fields that aren't yet known at index creation time, - which is very useful when working on highly dynamic systems. - -* Any value type can be indexed, string, number, date, etc. - -* An index definition can contain both dynamic-index-fields and regular-index-fields. - -* In this page: - - * [Indexing documents fields KEYS](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-keys) - * [Example - index any field under object](../../indexes/using-dynamic-fields.mdx#example---index-any-field-under-object) - * [Example - index any field](../../indexes/using-dynamic-fields.mdx#example---index-any-field) - * [Indexing documents fields VALUES](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-values) - * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) - * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) - * [CreateField syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) - * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields--terms-view) +* In RavenDB, different documents can have different shapes. + Documents are schemaless - new fields can be added or removed as needed. + For such dynamic data, you can define indexes with **dynamic-index-fields**. + +* Dynamic-index-fields are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time, + which is very useful when working with highly dynamic systems. + +* Any value type can be indexed, string, number, date, etc. + An index definition can contain both dynamic-index-fields and regular-index-fields. + +* In this article: + * [Indexing field KEYS](../../indexes/using-dynamic-fields.mdx#indexing-field-keys) + * [Example - index every field under an object](../../indexes/using-dynamic-fields.mdx#example---index-every-field-under-an-object) + * [Example - index every field](../../indexes/using-dynamic-fields.mdx#example---index-every-field) + * [Indexing field VALUES](../../indexes/using-dynamic-fields.mdx#indexing-field-values) + * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) + * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) + * [Indexing dynamic fields for full-text search](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + * [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) + * [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + * [`CreateField` syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) + * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields-terms-view) -## Indexing documents fields KEYS - -## Example - index any field under object + - - -* Index any field that is under the some object from the document. -* After index is deployed, any new field added to the this object will be indexed as well. + + +### Example - index every field under an object + +The following example allows you to: + * Index any field that is under the same object from the document. + * After index is deployed, any new field added to this object will be indexed as well. * **The document**: - - -{`class Product: - def __init__(self, Id: str = None, attributes: Dict[str, object] = None): - self.Id = Id - - # The KEYS under the Attributes object will be dynamically indexed - # Fields added to this object after index creation time will also get indexed - self.attributes = attributes -`} - - - - -{`// Sample document content -\{ - "Attributes": \{ - "Color": "Red", - "Size": 42 - \} -\} -`} - - + + + ```python + class Product: + def __init__(self, Id: str = None, Attributes: Dict[str, object] = None): + self.Id = Id + + # The KEYS under the Attributes object will be dynamically indexed + # Fields added to this object after index creation time will also get indexed + self.Attributes = Attributes + ``` + + + + ```json + // Sample document content + { + "Attributes": { + "Color": "Red", + "Size": 42 + } + } + ``` + * **The index**: - The below index will index any field under the `Attributes` object from the document, + + The following index will index any field under the `Attributes` object from the document, a dynamic-index-field will be created for each such field. New fields added to the object after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the attribute field **key**. - E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. - - - - -{`class Products_ByAttributeKey(AbstractIndexCreationTask): - def __init__(self): - super().__init__() - self.map = ( - "from p in docs.Products select new {" - "_ = p.attributes.Select(item => CreateField(item.Key, item.Value))" - "}" - ) -`} - - - - -{`class Products_ByAttributeKey_JS(AbstractJavaScriptIndexCreationTask): - def __init__(self): - super().__init__() - self.maps = { - """ - map('Products', function (p) { - return { - _: Object.keys(p.attributes).map(key => createField(key, p.attributes[key], - { indexing: 'Search', storage: true, termVector: null })) - }; - }) - """ - } -`} - - - - + The actual dynamic-index-field name on which you can query will be the attribute field **key**. + E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. + + + + ```python + class Products_ByAttributeKey(AbstractIndexCreationTask): + def __init__(self): + super().__init__() + self.map = ( + "from p in docs.Products select new { " + # Call 'CreateField' to generate dynamic-index-fields + # from the Attributes object keys. + + # Using '_' is just a convention. + # Any other string can be used instead of '_' + + # The actual field name will be 'item.Key' + # The actual field terms will be derived from 'item.Value' + "_ = p.Attributes.Select(item => CreateField(item.Key, item.Value)) " + "}" + ) + ``` + + + ```python + class Products_ByAttributeKey_JS(AbstractJavaScriptIndexCreationTask): + def __init__(self): + super().__init__() + self.maps = [ + """ + map('Products', function (p) { + return { + // The field name will be the key + // The field terms will be derived from the corresponding value + _: Object.keys(p.Attributes) + .map(key => createField(key, p.Attributes[key])) + }; + }) + """ + ] + ``` + + * **The query**: - * You can now query the generated dynamic-index fields. - * To get all documents with some 'size' use: - - - -{`matching_documents = list( - session.query_index_type(Products_ByAttributeKey, Product) - # 'size' is a dynamic-index-field that was indexed from the attributes object - .where_equals("size", 42) -) -`} - - - - -{`// 'Size' is a dynamic-index-field that was indexed from the Attributes object -from index 'Products/ByAttributeKey' where Size = 42 -`} - - - - -## Example - index any field - - + + You can now query the generated dynamic-index fields. + The `_` property is Not queryable but used only in the index definition syntax. + + To get all documents with a specific _Size_ use: + + + + ```python + matching_documents = list( + session.advanced + .document_query_from_index_type(Products_ByAttributeKey, Product) + # 'Size' is a dynamic-index-field that was indexed from the Attributes object + .where_equals("Size", 42) + ) + ``` + + + ```sql + // 'Size' is a dynamic-index-field that was indexed from the Attributes object + from index 'Products/ByAttributeKey' where Size = 42 + ``` + + + + + Dynamic index fields are queried with _document_query_ or _RQL_ because the field names (e.g. `Size`, `Color`) + are generated at indexing time and are not properties on the _Product_ class. + + Use the string-based `where_equals("Size", 42)` or RQL `where Size = 42`. + + + + + + +### Example - index every field + +The following example allows you to: * Define an index on a collection **without** needing any common structure between the indexed documents. - * After index is deployed, any new field added to the document will be indexed as well. - + * After the index is deployed, any new field added to any document in the collection will be indexed as well. - + -Consider whether this is really necessary, as indexing every single field can end up costing time and disk space. +* Use this approach with care. +* Indexing every field can increase indexing time, memory usage, and disk space, + especially for large or frequently updated documents. * **The document**: - - -{`class Product: - def __init__(self, Id: str = None, first_name: str = None, last_name: str = None, title: str = None): - self.Id = Id - - # All KEYS in the document will be dynamically indexes - # Fields added to the document after index creation time wil also get indexed - self.first_name = first_name - self.last_name = last_name - self.title = title - # ... -`} - - - - - -{`// Sample document content - \{ + + + ```python + class Product: + def __init__( + self, + Id: str = None, + FirstName: str = None, + LastName: str = None, + Title: str = None, + ): + self.Id = Id + + # All KEYS in the document will be dynamically indexed + # Fields added to the document after index creation time will also get indexed + self.FirstName = FirstName + self.LastName = LastName + self.Title = Title + # ... + ``` + + + + ```json + // Sample document content + { "FirstName": "John", "LastName": "Doe", "Title": "Engineer", // ... -\} -`} - - + } + ``` + * **The index**: - The below index will index any field from the document, + + The following index will index any field from the document, a dynamic-index-field will be created for each field. New fields added to the document after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the field **key**. - E.g., Keys `FirstName` & `LastName` will become the actual dynamic-index-fields. - - - - -{`class Products_ByAnyField_JS(AbstractJavaScriptIndexCreationTask): - def __init__(self): - super().__init__() - # This will index EVERY FIELD under the top level of the document - self.maps = { - """ - map('Products', function (p) { - return { - _: Object.keys(p).map(key => createField(key, p[key], - { indexing: 'Search', storage: true, termVector: null })) - } - }) - """ - } -`} - - - + The actual dynamic-index-field name on which you can query will be the field **key**. + E.g., Keys `FirstName` & `LastName` will become the actual dynamic-index-fields. + + The example uses a JavaScript index because it enumerates all top-level document fields at indexing time with `Object.keys(...)`. + LINQ indexes typically create dynamic fields from a known enumerable structure, such as a dictionary or collection. + + + + ```python + class Products_ByAnyField_JS(AbstractJavaScriptIndexCreationTask): + def __init__(self): + super().__init__() + # This will index EVERY FIELD under the top level of the document + self.maps = [ + """ + map('Products', function (p) { + return { + _: Object.keys(p).map(key => createField(key, p[key])) + }; + }) + """ + ] + ``` + + * **The query**: - * To get all documents with some 'LastName' use: - - - -{`# 'last_name' is a dynamic-index-field that was indexed from the document -matching_documents = list( - session.query_index_type(Products_ByAnyField_JS, Product).where_equals("last_name", "Doe") -) -`} - - - - -{`// 'LastName' is a dynamic-index-field that was indexed from the document -from index 'Products/ByAnyField/JS' where LastName = "Doe" -`} - - - + + To get all documents with a specific _LastName_ use: + + + + ```python + matching_documents = list( + session.advanced + .document_query_from_index_type(Products_ByAnyField_JS, Product) + # 'LastName' is a dynamic-index-field that was indexed from the document + .where_equals("LastName", "Doe") + ) + ``` + + + ```sql + // 'LastName' is a dynamic-index-field that was indexed from the document + from index 'Products/ByAnyField/JS' where LastName = "Doe" + ``` + + + + + + + + + + + +### Example - basic + + +The following example shows: + * Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. + * Documents can then be queried based on those indexed values. + * For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---list) below. + + +* **The document**: + + + ```python + class Product: + def __init__(self, Id: str = None, ProductType: str = None, PricePerUnit: int = None): + self.Id = Id + + # The VALUE of ProductType will be dynamically indexed + self.ProductType = ProductType + self.PricePerUnit = PricePerUnit + ``` + + + + ```json + // Sample document content + { + "ProductType": "Electronics", + "PricePerUnit": 23 + } + ``` + + +* **The index**: + + The following index will index the **value** of document field 'ProductType'. + + This value will be the dynamic-index-field name on which you can query. + E.g., Field value `Electronics` will be the dynamic-index-field. + + + + ```python + class Products_ByProductType(AbstractIndexCreationTask): + def __init__(self): + super().__init__() + self.map = ( + "from p in docs.Products select new { " + # Call 'CreateField' to generate the dynamic-index-fields + # The field name will be the value of document field 'ProductType' + # The field terms will be derived from document field 'PricePerUnit' + "_ = CreateField(p.ProductType, p.PricePerUnit) " + "}" + ) + ``` + + + ```python + class Products_ByProductType_JS(AbstractJavaScriptIndexCreationTask): + def __init__(self): + super().__init__() + self.maps = [ + """ + map('Products', function (p) { + return { + // The field name will be the value of document field 'ProductType' + // The field terms will be derived from document field 'PricePerUnit' + _: createField(p.ProductType, p.PricePerUnit) + }; + }) + """ + ] + ``` + + + +* **The query**: + + To get all documents of some product type having a specific price per unit use: + + + + ```python + matching_documents = list( + session.advanced + .document_query_from_index_type(Products_ByProductType, Product) + # 'Electronics' is the dynamic-index-field that was indexed + # from document field 'ProductType' + .where_equals("Electronics", 23) + ) + ``` + + + ```sql + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'ProductType' + from index 'Products/ByProductType' where Electronics = 23 + ``` + + + + + + + +### Example - list + + +The following example allows you to: + * Index **values** from items in a list + * After index is deployed, any item added to this list in the document will be dynamically indexed as well. + + +* **The document**: + + + ```python + class Attribute: + def __init__(self, PropName: str = None, PropValue: str = None): + self.PropName = PropName + self.PropValue = PropValue + + + class Product: + def __init__(self, Id: str = None, Name: str = None, Attributes: List[Attribute] = None): + self.Id = Id + self.Name = Name + + # For each element in this list, + # the VALUE of property 'PropName' will be dynamically indexed. + # e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields. + self.Attributes = Attributes + ``` + + + + ```json + // Sample document content + { + "Name": "SomeName", + "Attributes": [ + { + "PropName": "Color", + "PropValue": "Blue" + }, + { + "PropName": "Width", + "PropValue": "10" + }, + { + "PropName": "Length", + "PropValue": "20" + }, + // ... + ] + } + ``` + + +* **The index**: + + The following index will create a dynamic-index-field per item in the document's `Attributes` list. + New items added to the Attributes list after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the item's PropName **value**. + E.g., 'PropName' value `Width` will be a dynamic-index-field. + + + + ```python + class Attributes_ByName(AbstractIndexCreationTask): + def __init__(self): + super().__init__() + self.map = ( + "from a in docs.Products select new { " + # Define the dynamic-index-fields by calling 'CreateField' + # A dynamic-index-field will be generated for each item in 'Attributes' + + # For each item, the field name will be the value of field 'PropName' + # The field terms will be derived from field 'PropValue' + "_ = a.Attributes.Select(item => CreateField(item.PropName, item.PropValue)), " + + # A regular index field can be defined as well: + "Name = a.Name " + "}" + ) + ``` + + + ```python + class Attributes_ByName_JS(AbstractJavaScriptIndexCreationTask): + def __init__(self): + super().__init__() + self.maps = [ + """ + map('Products', function (p) { + return { + // For each item, + // the field name will be the value of field 'PropName' + // The field terms will be derived from field 'PropValue' + _: p.Attributes.map(item => createField(item.PropName, item.PropValue)), + + // A regular index field can be defined as well: + Name: p.Name + }; + }) + """ + ] + ``` + + + +* **The query**: + + To get all documents matching a specific attribute property use: + + + + ```python + matching_documents = list( + session.advanced + .document_query_from_index_type(Attributes_ByName, Product) + # 'Width' is a dynamic-index-field that was indexed from the Attributes list + .where_equals("Width", 10) + ) + ``` + + + ```sql + // 'Width' is a dynamic-index-field that was indexed from the Attributes list + from index 'Attributes/ByName' where Width = 10 + ``` + + + + + + + + + + + +Dynamic fields can be indexed for [Full-text search](../../indexes/querying/searching.mdx) by setting `FieldIndexing.Search` in `CreateFieldOptions`. +* **At indexing time**: + At indexing time, RavenDB analyzes the dynamic field value and creates the indexed terms. + The analyzer used at indexing time is resolved from the index definition. + You can configure an analyzer for a specific generated field name, or configure `ALL_FIELDS` as a fallback. + See [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) below. + * If the generated dynamic field name has an explicit analyzer configuration, that analyzer is used. + * Otherwise, if `ALL_FIELDS` has an analyzer configuration, RavenDB uses it as a fallback. + * If no analyzer is configured for the field, RavenDB uses the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). -## Indexing documents fields VALUES +* **At querying time**: + For full-text search to return the expected results, the analyzer used at indexing time and the analyzer used for the query term should produce compatible terms. -## Example - basic + When a `search()` query targets a dynamic field that does **not** have an explicit analyzer configuration for its generated field name, + RavenDB uses the [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) to analyze the query term by default. - + Set the [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) + configuration key to `true` to make RavenDB use the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) as the fallback analyzer for the query term instead. -* Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. -* Documents can then be queried based on those indexed values. + This configuration can be set at different scopes: server-wide, per database, or per index. + The example below sets it at the index scope, in the index definition. + + For a summary of how RavenDB resolves the analyzer used for the query term, + see [Which analyzer is used for the query term](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) below. + +--- + +The following example indexes localized product descriptions as dynamic fields and queries one of those generated fields using full-text search: + +* **The document**: + + + ```python + class Product: + def __init__(self, Id: str = None, Descriptions: Dict[str, str] = None): + self.Id = Id + self.Descriptions = Descriptions + ``` + + + + ```json + // Sample document content + { + "Descriptions": { + "English": "North wind jacket", + "French": "Veste vent du nord" + } + } + ``` + -* **The document**: - - -{`class Product: - def __init__(self, Id: str = None, product_type: str = None, price_per_unit: float = None): - self.Id = Id - - # The VALUE of ProductType will be dynamically indexed - self.product_type = product_type - self.price_per_unit = price_per_unit -`} - - +* **The index**: - - -{`// Sample document content -\{ - "ProductType": "Electronics", - "PricePerUnit": 23 -\} -`} - - + The following index creates one dynamic field for each entry in the `Descriptions` dictionary. + For example, the key `English` creates the dynamic field `Description_English`. -* **The index**: - The below index will index the **value** of document field 'ProductType'. - - This value will be the dynamic-index-field name on which you can query. - E.g., Field value `Electronics` will be the dynamic-index-field. + Each generated dynamic field is indexed for **full-text search**. + + + + ```python + class Products_ByLocalizedDescription(AbstractIndexCreationTask): + USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = ( + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery" + ) + + def __init__(self): + super().__init__() + self.map = ( + "from product in docs.Products select new { " + " _ = product.Descriptions.Select(x => " + " CreateField(" + " \"Description_\" + x.Key, " + " x.Value, " + # Index each generated dynamic field for FTS + " new CreateFieldOptions { " + " Indexing = FieldIndexing.Search, " + " Storage = FieldStorage.No " + " })) " + "}" + ) + + self._store_all_fields(FieldStorage.NO) + + # Set this configuration key to true so that search() queries on dynamic fields + # will use the 'Default Search Analyzer' when the generated field name + # has no explicit analyzer configuration. + self.configuration = { + Products_ByLocalizedDescription.USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS: "true" + } + ``` + + + ```python + class Products_ByLocalizedDescription_JS(AbstractJavaScriptIndexCreationTask): + USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = ( + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery" + ) + + def __init__(self): + super().__init__() + self.maps = [ + """ + map('Products', function (product) { + return { + _: Object.keys(product.Descriptions).map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + // Index each generated dynamic field for FTS + indexing: 'Search', + storage: 'No' + }); + }) + }; + }) + """ + ] + + # Apply a storage default to every generated field + self.fields = { + constants.Documents.Indexing.Fields.ALL_FIELDS: + IndexFieldOptions(storage=FieldStorage.NO) + } + + # Set this configuration key to true so that search() queries on dynamic fields + # will use the 'Default Search Analyzer' when the generated field name + # has no explicit analyzer configuration. + self.configuration = { + Products_ByLocalizedDescription_JS.USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS: "true" + } + ``` + + + +* **Full-text search query**: + + Query the generated dynamic field by its field name. + + In this example, the query targets the generated field `Description_English`. + + Since `Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery` is set to `true`, + the searched term `"north wind"` is analyzed with the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + + + + ```python + results = list( + session.advanced + .document_query_from_index_type(Products_ByLocalizedDescription, Product) + .search("Description_English", "north wind") + ) + ``` + + + + ```sql + from index "Products/ByLocalizedDescription" + where search(Description_English, "north wind") + ``` + + + +--- + + + +### Configuring analyzers for dynamic fields + +`CreateFieldOptions` does **not** let you specify a custom analyzer directly. +However, you can still configure which analyzer RavenDB will use for dynamic fields in the index definition by either: + +* Configuring an analyzer for a specific generated dynamic field name. +* Configuring `ALL_FIELDS` as a fallback for fields that do not have their own explicit analyzer configuration. + +Use `ALL_FIELDS` with care, since it can affect any field that does not have its own explicit analyzer configuration. + +--- + +#### Configure analyzer for a specific dynamic field - -{`class Products_ByProductType(AbstractIndexCreationTask): +```python +class Products_ByLocalizedDescription(AbstractIndexCreationTask): def __init__(self): super().__init__() + self.map = ( + "from product in docs.Products select new { " + " _ = product.Descriptions.Select(x => " + " CreateField(" + " \"Description_\" + x.Key, " + " x.Value, " + " new CreateFieldOptions { " + " Indexing = FieldIndexing.Search, " + " Storage = FieldStorage.No " + " })) " + "}" + ) - # Call 'CreateField' to generate the dynamic-index-fields - # The field name will be the value of document field 'product_type' - # The field terms will be derived from document field 'price_per_unit' - self.map = "from p in docs.Products select new { _ = CreateField(p.product_type, p.price_per_unit)}" -`} - + self._store_all_fields(FieldStorage.NO) + + # Configure an analyzer for a known generated dynamic field name + # ============================================================== + self._analyze("Description_English", "StopAnalyzer") +``` - - -{`class Products_ByProductType_JS(AbstractJavaScriptIndexCreationTask): + +```python +class Products_ByLocalizedDescription_JS(AbstractJavaScriptIndexCreationTask): def __init__(self): super().__init__() - self.maps = { + self.maps = [ """ - map('Products', function (p) { + map('Products', function (product) { return { - _: createField(p.product_type, p.price_per_unit, - { indexing: 'Search', storage: true, termVector: null }) + _: Object.keys(product.Descriptions).map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; }) """ + ] + + # Configure an analyzer for a known generated dynamic field name + # ============================================================== + self.fields = { + constants.Documents.Indexing.Fields.ALL_FIELDS: + IndexFieldOptions(storage=FieldStorage.NO), + "Description_English": + IndexFieldOptions(analyzer="StopAnalyzer") } -`} - - - - -* **The query**: - * To get all documents of some product type having a specific price per unit use: - - - -{`# 'electronics' is the dynamic-index-field that was indexed from the document 'product_type' -matching_documents = list( - session.advanced.document_query_from_index_type(Products_ByProductType, Product).where_equals( - "electronics", 23 - ) -) -`} - - - - -{`// 'Electronics' is the dynamic-index-field that was indexed from document field 'ProductType' -from index 'Products/ByProductType' where Electronics = 23 -`} - +``` -## Example - list - - - -* Index **values** from items in a list -* After index is deployed, any item added this list in the document will be dynamically indexed as well. - - - -* **The document**: - - -{`class Attribute: - def __init__(self, prop_name: str = None, prop_value: str = None): - self.prop_name = prop_name - self.prop_value = prop_value - - -class Product: - def __init__(self, Id: str = None, name: str = None, attributes: List[Attribute] = None): - self.Id = Id - self.name = name - # For each element in this list, the VALUE of property 'prop_name' will be dynamically indexed - # e.g. color, width, length (in ex. below) will become dynamic-index-field - self.attributes = attributes -`} - - - - - -{`// Sample document content -\{ -Name": "SomeName", -Attributes": [ - \{ - "PropName": "Color", - "PropValue": "Blue" - \}, - \{ - "PropName": "Width", - "PropValue": "10" - \}, - \{ - "PropName": "Length", - "PropValue": "20" - \}, - ... - -\} -`} - - - -* **The index**: - The below index will create a dynamic-index-field per item in the document's `Attributes` list. - New items added to the Attributes list after index creation time will be dynamically indexed as well. - - The actual dynamic-index-field name on which you can query will be the item's PropName **value**. - E.g., 'PropName' value `width` will be a dynamic-index-field. +#### Configure a fallback analyzer for all fields - -{`class Attributes_ByName(AbstractIndexCreationTask): +```python +class Products_ByLocalizedDescription(AbstractIndexCreationTask): def __init__(self): super().__init__() self.map = ( - "from a in docs.Products select new " - "{ _ = a.attributes.Select( item => CreateField(item.prop_name, item.prop_value)), name = a.name " + "from product in docs.Products select new { " + " _ = product.Descriptions.Select(x => " + " CreateField(" + " \"Description_\" + x.Key, " + " x.Value, " + " new CreateFieldOptions { " + " Indexing = FieldIndexing.Search, " + " Storage = FieldStorage.No " + " })) " "}" ) -`} - + + self._store_all_fields(FieldStorage.NO) + + # Use this as a fallback for fields that do not have + # their own explicit analyzer configuration + # ================================================== + self._analyze(constants.Documents.Indexing.Fields.ALL_FIELDS, "StopAnalyzer") +``` - - -{`class Attributes_ByName_JS(AbstractJavaScriptIndexCreationTask): + +```python +class Products_ByLocalizedDescription_JS(AbstractJavaScriptIndexCreationTask): def __init__(self): super().__init__() - self.maps = { + self.maps = [ """ - map('Products', function (p) { + map('Products', function (product) { return { - _: p.Attributes.map(item => createField(item.PropName, item.PropValue, - { indexing: 'Search', storage: true, termVector: null })), - Name: p.Name + _: Object.keys(product.Descriptions).map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; }) """ + ] + + # Use this as a fallback for fields that do not have + # their own explicit analyzer configuration + # ================================================== + self.fields = { + constants.Documents.Indexing.Fields.ALL_FIELDS: + IndexFieldOptions(storage=FieldStorage.NO, analyzer="StopAnalyzer") } -`} - - - - -* **The query**: - To get all documents matching a specific attribute property use: - - - -{`matching_documents = list( - session.advanced.document_query_from_index_type(Attributes_ByName, Product).where_equals( - "width", 10 - ) -) -`} - - - - -{`// 'Width' is a dynamic-index-field that was indexed from the Attributes list -from index 'Attributes/ByName' where Width = 10 -`} - +``` + + + + +### Which analyzer is used for the query term? + +The following applies when a `search()` query targets a **dynamic field**: + +| Index configuration | [Configuration key](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) | Analyzer used for the query term | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| No analyzer is explicitly configured for the generated field name | `false` | The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) | +| No analyzer is explicitly configured for the generated field name | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `false` | The analyzer resolved by the normal field fallback path,
e.g. `WhitespaceAnalyzer` | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| The generated field name is explicitly configured,
e.g. `self._analyze("Description_English", "StopAnalyzer")` | `false` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +| The generated field name is explicitly configured,
e.g. `self._analyze("Description_English", "StopAnalyzer")` | `true` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | + +
+
-## CreateField syntax + #### Syntax for Index: - - -{`object CreateField(string name, object value); + +```python +CreateField(name, value) -object CreateField(string name, object value, bool stored, bool analyzed); +CreateField(name, value, stored, analyzed) -object CreateField(string name, object value, CreateFieldOptions options); -`} - +CreateField(name, value, CreateFieldOptions) +``` #### Syntax for JavaScript-index: - - -{`createField(fieldName, fieldValue, options); // returns object -`} - + +```js +createField(fieldName, fieldValue, options); // returns object +``` -| Parameters | Type | Description | -|------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **name** | `string` | Name of the dynamic-index-field | -| **value** | `object` | Value of the dynamic-index-field
The field Terms are derived from this value. | -| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`False` - will set `FieldStorage.NO` (default value)
`True` - will set `FieldStorate.YES` | -| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`None` - `FieldIndexing.Default` (default value)
`False` - `FieldIndexing.Exact`
`True` - `FieldIndexing.Search` | -| **options** | `CreateFieldOptions` | Dynamic-index-field options | +| Parameters | Type | Description | +|----------------|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **fieldName** | `string` | Name of the dynamic-index-field | +| **fieldValue** | `object` | Value of the dynamic-index-field
The field terms are derived from this value. | +| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorage.Yes` | +| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing.Default` (default value)
`false` - `FieldIndexing.Exact`
`true` - `FieldIndexing.Search` | +| **options** | `CreateFieldOptions` | Dynamic-index-field options | -| CreateFieldOptions | | | -|--------------------|--------------------|----------------------------------------------------------------------------| +| CreateFieldOptions | | | +|--------------------|--------------------|-----------------------------------------------------------------------------------| | **Storage** | `FieldStorage?` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | -| **Indexing** | `FieldIndexing?` | Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | -| **TermVector** | `FieldTermVector?` | Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | +| **Indexing** | `FieldIndexing?` | Sets the field indexing behavior.
Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | +| **TermVector** | `FieldTermVector?` | Sets the field term-vector behavior.
Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | @@ -496,17 +892,15 @@ object CreateField(string name, object value, CreateFieldOptions options); +
+ + - -## Indexed fields & terms view - -The generated dynamic-index-fields and their indexed terms can be viewed in the **Terms View**. -Below are sample index fields & their terms generated from the last example. +The generated dynamic index fields and their indexed terms can be viewed in the **Terms View** in Studio. +Below are sample index fields & their terms generated from [this example](../../indexes/using-dynamic-fields.mdx#example---list). ![Figure 1. Go to terms view](../assets/dynamic-index-fields-1.png) ![Figure 2. Indexed fields & terms](../assets/dynamic-index-fields-2.png) - - - - + + diff --git a/versioned_docs/version-6.2/indexes/querying/content/_searching-csharp.mdx b/versioned_docs/version-6.2/indexes/querying/content/_searching-csharp.mdx index d20ef5a65e..f0be0e6ffd 100644 --- a/versioned_docs/version-6.2/indexes/querying/content/_searching-csharp.mdx +++ b/versioned_docs/version-6.2/indexes/querying/content/_searching-csharp.mdx @@ -16,11 +16,13 @@ import CodeBlock from '@theme/CodeBlock'; See examples below. * You can configure which analyzer will be used to tokenize this field. - See [selecting an analyzer](../../../indexes/using-analyzers.mdx#selecting-an-analyzer-for-a-field). + Learn more in [Setting analyzer for index-field](../../../indexes/using-analyzers.mdx#setting-analyzer-for-index-field). + * In this article: * [Indexing single field for FTS](../../../indexes/querying/searching.mdx#indexing-single-field-for-fts) * [Indexing multiple fields for FTS](../../../indexes/querying/searching.mdx#indexing-multiple-fields-for-fts) - * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-(using-asjson)) + * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-using-asjson) + * [Indexing dynamic fields for FTS](../../../indexes/querying/searching.mdx#indexing-dynamic-fields-for-fts) * [Boosting search results](../../../indexes/querying/searching.mdx#boosting-search-results) * [Searching with wildcards](../../../indexes/querying/searching.mdx#searching-with-wildcards) * [When using RavenStandardAnalyzer or StandardAnalyzer or NGramAnalyzer](../../../indexes/querying/searching.mdx#when-usingoror) @@ -28,6 +30,7 @@ import CodeBlock from '@theme/CodeBlock'; * [When using the Exact analyzer](../../../indexes/querying/searching.mdx#when-using-the-exact-analyzer) + ## Indexing single field for FTS #### The index: @@ -262,7 +265,7 @@ where (search(EmployeeData, "Manager") or search(EmployeeData, "French Spanish", ## Indexing all fields for FTS (using AsJson) -* To search across ALL fields in a document without defining each one explicitly, +* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method in the _Map_ function to extract all property values and index them in a single searchable field. * This approach makes the index robust to changes in the document schema. @@ -377,7 +380,14 @@ where search(AllValues, "tofu") +## Indexing dynamic fields for FTS + +* [Dynamic index fields](../../../indexes/using-dynamic-fields.mdx) are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time. +* Dynamic index fields can be configured for full-text search just like regular index fields. + Learn more in [Indexing dynamic fields for full-text search](../../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search). ## Boosting search results diff --git a/versioned_docs/version-6.2/indexes/querying/content/_searching-java.mdx b/versioned_docs/version-6.2/indexes/querying/content/_searching-java.mdx index 7d85246ced..4accf3f512 100644 --- a/versioned_docs/version-6.2/indexes/querying/content/_searching-java.mdx +++ b/versioned_docs/version-6.2/indexes/querying/content/_searching-java.mdx @@ -388,4 +388,11 @@ where search(allValues, "tofu") +## Indexing dynamic fields for FTS +* [Dynamic index fields](../../../indexes/using-dynamic-fields.mdx) are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time. + +* Dynamic index fields can be configured for full-text search just like regular index fields. + Learn more in [Indexing dynamic fields for full-text search](../../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search). \ No newline at end of file diff --git a/versioned_docs/version-6.2/indexes/querying/content/_searching-nodejs.mdx b/versioned_docs/version-6.2/indexes/querying/content/_searching-nodejs.mdx index 4ca66b5877..cb73bfd7c0 100644 --- a/versioned_docs/version-6.2/indexes/querying/content/_searching-nodejs.mdx +++ b/versioned_docs/version-6.2/indexes/querying/content/_searching-nodejs.mdx @@ -5,7 +5,7 @@ import CodeBlock from '@theme/CodeBlock'; -* Prior to reading this article, please refer to [full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) +* Prior to reading this article, please refer to [Full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) to learn about the `search` method. * **All capabilities** provided by `search` with a dynamic query can also be used when querying a static-index. @@ -17,11 +17,13 @@ import CodeBlock from '@theme/CodeBlock'; See examples below. * You can configure which analyzer will be used to tokenize this field. - See [selecting an analyzer](../../../indexes/using-analyzers.mdx#selecting-an-analyzer-for-a-field). + Learn more in [Setting analyzer for index-field](../../../indexes/using-analyzers.mdx#setting-analyzer-for-index-field). + * In this article: * [Indexing single field for FTS](../../../indexes/querying/searching.mdx#indexing-single-field-for-fts) * [Indexing multiple fields for FTS](../../../indexes/querying/searching.mdx#indexing-multiple-fields-for-fts) - * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-(using-asjson)) + * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-using-asjson) + * [Indexing dynamic fields for FTS](../../../indexes/querying/searching.mdx#indexing-dynamic-fields-for-fts) * [Boosting search results](../../../indexes/querying/searching.mdx#boosting-search-results) * [Searching with wildcards](../../../indexes/querying/searching.mdx#searching-with-wildcards) * [When using RavenStandardAnalyzer or StandardAnalyzer or NGramAnalyzer](../../../indexes/querying/searching.mdx#when-usingoror) @@ -29,6 +31,7 @@ import CodeBlock from '@theme/CodeBlock'; * [When using the Exact analyzer](../../../indexes/querying/searching.mdx#when-using-the-exact-analyzer) + ## Indexing single field for FTS #### The index: @@ -163,7 +166,7 @@ where (search(employeeData, "Manager") or search(employeeData, "French Spanish", ## Indexing all fields for FTS (using AsJson) -* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, +* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, which is available when using **a C# LINQ string** that is assigned to the `map` property in the Node.js index class, as shown in the example below. @@ -229,6 +232,14 @@ where search(AllValues, "tofu") +## Indexing dynamic fields for FTS + +* [Dynamic index fields](../../../indexes/using-dynamic-fields.mdx) are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time. + +* Dynamic index fields can be configured for full-text search just like regular index fields. + Learn more in [Indexing dynamic fields for full-text search](../../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search). ## Boosting search results diff --git a/versioned_docs/version-6.2/indexes/querying/content/_searching-php.mdx b/versioned_docs/version-6.2/indexes/querying/content/_searching-php.mdx index 55261ce19f..510a6f8752 100644 --- a/versioned_docs/version-6.2/indexes/querying/content/_searching-php.mdx +++ b/versioned_docs/version-6.2/indexes/querying/content/_searching-php.mdx @@ -5,7 +5,7 @@ import CodeBlock from '@theme/CodeBlock'; -* Prior to reading this article, please refer to [full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) +* Prior to reading this article, please refer to [Full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) to learn about the `search` method. * **All capabilities** provided by `search` with a dynamic query can also be used when querying a static-index. @@ -17,14 +17,17 @@ import CodeBlock from '@theme/CodeBlock'; See examples below. * You can configure which analyzer will be used to tokenize this field. - See [selecting an analyzer](../../../indexes/using-analyzers.mdx#selecting-an-analyzer-for-a-field). + Learn more in [Setting analyzer for index-field](../../../indexes/using-analyzers.mdx#setting-analyzer-for-index-field). + * In this article: * [Indexing single field for FTS](../../../indexes/querying/searching.mdx#indexing-single-field-for-fts) * [Indexing multiple fields for FTS](../../../indexes/querying/searching.mdx#indexing-multiple-fields-for-fts) - * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-(using-asjson)) + * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-using-asjson) + * [Indexing dynamic fields for FTS](../../../indexes/querying/searching.mdx#indexing-dynamic-fields-for-fts) * [Boosting search results](../../../indexes/querying/searching.mdx#boosting-search-results) + ## Indexing single field for FTS #### The index: @@ -253,7 +256,7 @@ where (search(EmployeeData, "Manager") or search(EmployeeData, "French Spanish", ## Indexing all fields for FTS (using AsJson) -* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, +* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, which is available in the **C# LINQ string** that is assigned to the `map` property in the PHP index class, as shown in the example below. @@ -332,6 +335,15 @@ where search(allValues, "tofu") +## Indexing dynamic fields for FTS + +* [Dynamic index fields](../../../indexes/using-dynamic-fields.mdx) are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time. + +* Dynamic index fields can be configured for full-text search just like regular index fields. + Learn more in [Indexing dynamic fields for full-text search](../../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search). + ## Boosting search results diff --git a/versioned_docs/version-6.2/indexes/querying/content/_searching-python.mdx b/versioned_docs/version-6.2/indexes/querying/content/_searching-python.mdx index f06c29d94f..88c6aa452f 100644 --- a/versioned_docs/version-6.2/indexes/querying/content/_searching-python.mdx +++ b/versioned_docs/version-6.2/indexes/querying/content/_searching-python.mdx @@ -5,7 +5,7 @@ import CodeBlock from '@theme/CodeBlock'; -* Prior to reading this article, please refer to [full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) +* Prior to reading this article, please refer to [Full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) to learn about the `search` method. * **All capabilities** provided by `search` with a dynamic query can also be used when querying a static-index. @@ -17,14 +17,17 @@ import CodeBlock from '@theme/CodeBlock'; See examples below. * You can configure which analyzer will be used to tokenize this field. - See [selecting an analyzer](../../../indexes/using-analyzers.mdx#selecting-an-analyzer-for-a-field). + Learn more in [Setting analyzer for index-field](../../../indexes/using-analyzers.mdx#setting-analyzer-for-index-field). + * In this article: * [Indexing single field for FTS](../../../indexes/querying/searching.mdx#indexing-single-field-for-fts) * [Indexing multiple fields for FTS](../../../indexes/querying/searching.mdx#indexing-multiple-fields-for-fts) - * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-(using-asjson)) + * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-using-asjson) + * [Indexing dynamic fields for FTS](../../../indexes/querying/searching.mdx#indexing-dynamic-fields-for-fts) * [Boosting search results](../../../indexes/querying/searching.mdx#boosting-search-results) + ## Indexing single field for FTS #### The index: @@ -167,7 +170,7 @@ where (search(EmployeeData, "Manager") or search(EmployeeData, "French Spanish", ## Indexing all fields for FTS (using AsJson) -* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, +* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, which is available in the **C# LINQ string** that is assigned to the `map` property in the Python index class, as shown in the example below. @@ -238,6 +241,14 @@ where search(all_values, "tofu") +## Indexing dynamic fields for FTS + +* [Dynamic index fields](../../../indexes/using-dynamic-fields.mdx) are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time. + +* Dynamic index fields can be configured for full-text search just like regular index fields. + Learn more in [Indexing dynamic fields for full-text search](../../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search). ## Boosting search results diff --git a/versioned_docs/version-6.2/indexes/using-dynamic-fields.mdx b/versioned_docs/version-6.2/indexes/using-dynamic-fields.mdx index e9ffb33a07..e465ba5431 100644 --- a/versioned_docs/version-6.2/indexes/using-dynamic-fields.mdx +++ b/versioned_docs/version-6.2/indexes/using-dynamic-fields.mdx @@ -1,7 +1,8 @@ --- -title: "Indexes: Dynamic Index Fields" -sidebar_label: Dynamic Fields -sidebar_position: 28 +title: "Dynamic Index Fields" +sidebar_label: "Dynamic Index Fields" +description: "Index document fields dynamically in RavenDB when field names are unknown at index definition time, using the CreateField method." +sidebar_position: 27 supported_languages: ["csharp", "java", "python", "php", "nodejs"] see_also: - title: "Boosting" @@ -31,7 +32,6 @@ import UsingDynamicFieldsPython from './content/_using-dynamic-fields-python.mdx import UsingDynamicFieldsPhp from './content/_using-dynamic-fields-php.mdx'; import UsingDynamicFieldsNodejs from './content/_using-dynamic-fields-nodejs.mdx'; - diff --git a/versioned_docs/version-6.2/server/configuration/indexing-configuration.mdx b/versioned_docs/version-6.2/server/configuration/indexing-configuration.mdx index fe64090eb5..e845bf3326 100644 --- a/versioned_docs/version-6.2/server/configuration/indexing-configuration.mdx +++ b/versioned_docs/version-6.2/server/configuration/indexing-configuration.mdx @@ -95,6 +95,7 @@ import LanguageContent from "@site/src/components/LanguageContent"; [Indexing.OrderByTicksAutomaticallyWhenDatesAreInvolved](../../server/configuration/indexing-configuration.mdx#indexingorderbyticksautomaticallywhendatesareinvolved) [Indexing.QueryClauseCache.Disabled](../../server/configuration/indexing-configuration.mdx#indexingqueryclausecachedisabled) [Indexing.QueryClauseCache.RepeatedQueriesTimeFrameInSec](../../server/configuration/indexing-configuration.mdx#indexingqueryclausecacherepeatedqueriestimeframeinsec) + [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) [Indexing.ScratchSpaceLimitInMb](../../server/configuration/indexing-configuration.mdx#indexingscratchspacelimitinmb) [Indexing.Static.SearchEngineType](../../server/configuration/indexing-configuration.mdx#indexingstaticsearchenginetype) [Indexing.Throttling.TimeIntervalInMs](../../server/configuration/indexing-configuration.mdx#indexingthrottlingtimeintervalinms) @@ -893,6 +894,33 @@ Queries that repeat within this time frame will be considered worth caching. +## Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery + +* This setting controls which analyzer is used for the **query term** when a **`search()` query** targets a [Dynamic index field](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + that was indexed for full-text search, and no analyzer is explicitly configured for that field in the index definition. + + * `false` (default): + The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) is used to analyze the **query term**. + This analyzer can be configured via the [Indexing.Analyzers.Default](../../server/configuration/indexing-configuration.mdx#indexinganalyzersdefault) configuration key. + + * `true`: + The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) is used to analyze the **query term**. + This analyzer can be configured via the [Indexing.Analyzers.Search.Default](../../server/configuration/indexing-configuration.mdx#indexinganalyzerssearchdefault) configuration key. + +* See the table in [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + for the complete analyzer-selection rules based on this configuration key and the analyzer configured for the dynamic index field. + +* This configuration applies to both the Lucene and Corax search engines. + The default is `false` to preserve backward compatibility. + +--- + +- **Type**: `bool` +- **Default**: `false` +- **Scope**: Server-wide, or per database, or per index + + + ## Indexing.ScratchSpaceLimitInMb * Amount of scratch space in megabytes that we allow to use for the index storage. diff --git a/versioned_docs/version-7.0/indexes/assets/dynamic-index-fields-1.png b/versioned_docs/version-7.0/indexes/assets/dynamic-index-fields-1.png index b13d9076f89e3363e97e2a4a6c01aecc4d101b67..a20adda0ece521b8d27172708c0a11aeea13f887 100644 GIT binary patch literal 51136 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 literal 26390 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 diff --git a/versioned_docs/version-7.0/indexes/assets/dynamic-index-fields-2.png b/versioned_docs/version-7.0/indexes/assets/dynamic-index-fields-2.png index dd2b4082eaa04bd406f9fac609adccde69e21b42..c5b1bc14bca3b86dd281347ae4c56e6545ff1bba 100644 GIT binary patch literal 67121 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%0Dmpj<&g2>Q& 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 X>}(5)fwv}4d^z}m?)_4g$D#iV5#_1q literal 43570 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

MYQ6EJKjSRtlshL!QySY5<Da z`%8-4&%g0-2QFP8U$1>~StwMzmc6^oj)S^aSPL!{1A{-mkTA6g!7hmWnKq`XU)H3G zV3bWNxg}wo_{za%Pa{HlZ$gh!oDN%h0z7d>&BSw5#2M<4^=*7VjD*5*1#DHf2x}MC2U_E5PJ-` z(~*tF>vw8-Nj-_@!}%vH!>5yFv>bI_-H#ZzszKTq}8!o`7gy@d~naqcI^`)(wf=?a*#+2O~(`Ox5 z$Hl|hbjLobY5ter;B&Z2-Qil_dmgZ>$6Bh}>hUVh-!kYiU_K0L zE}-?|<5B*)-qB?(P~^}ul!R0bGNg7p7tQ{ua))!s2FW7nP=0M$tjr>7=7W@IfN3G1UcuIsy{G1Np?w$oK4_k3pH3@E!LaAN(!PO|&(N34;Uu0)=q%^)8n@9j|M zY!kmlzdw;dpQo7ga^$I8xi>U9NWY;MW~trI({%JYtE5DaQgD?MOuK@nj0yr~k$!NuG^eBuZTwtDnBKqe?!wmt2byels!Q&H%Hg-<=^*R{y*5rd9E zzq_`@RMR_KjooqSKHV0x4F@Z`bkR%|+z|3$2}L?o%!`fp?mV1K!wS5%rZQ%&x;ZO} zsR#g}u}LdBQ`V+hW5?*B90+a3nzg6L+d@h4Uux(NY=u*x_w|d9mf3>#Mr~jQIGN+! z&74T{{2@H`kwnPo3@$*KTTK+DQ1+*^nb44}tGfM*lwbo#~IU4SDxj zi%w=M)2?JIXw(#-8a_B69g4J_YBbLV&6uF7%xpB}#H7EEg4I@ojLUeWf?R9<j(Z50iVaGI2N!ZFbGxeXU+6FYW>KE_x*8yj1H|9zZC zf&w;)ued-4?9@Cxzw|}%wfl>F;eOQ*%ZviD<0Yjxc}tJ0fp~?YqN8NLmM<{Wy!HsT z4S7hj(CbX=@vpj=3S6c_VCV6vGh$q6LW+D=XJ=u?w6M3>RV}t!Rohfnqt$liob5WDe0R(IJ6IhNhFqe$l&ztCmw!O5LJ%W#N6$ zA?duK@&(zX)s2ed+L-rS)+#gQ#0)n^KT-zLq8?)&QrN(375 zhSurQ+iDN~AVF)^^1(RdyV5^&%ygoELl8Q8NBlT-YW%v~K;9U4Vx7Ay5Il&Xac#JAA8gg%mIX6I~31&i_rGIT}EJr9f)f^^x z9NEk$R9y9K6&-mY2Odc@77wsEWyqm&Xshc_M6M{@QL0^Mg&*nKF+$G|-e~13wmy8^ z9HHx|;;=KOyDA>l=bYtT)9b4NDIle0&jgt{T1+}{zXzr~=b~;jAwD;{z#8Tjmt7iT zi7F9+sY$)Q6Z-Y~bO_$sMx}mTZd$o`l?|HcU=~9CLsd*6@LRv{v=-p$=n`y|mIBo! z?p6Z}?kh;Uk>auHwf>-w*2JXch}SX!Es6iwb=ewNY3>@P@_8QU}d>)wcd+2lxzxE4F$~RvW|r45K@ev zc50P+@ixz9{e}ft+|Bd&&Ff33c`eJP1^W&?{D#VJaZv&%k0BG9CP(?}0UedG%}(a?i=BDn3`hvLun`53t~8Lu{K${b(Ko%qAT_>hS6+9ju7GZ84-Fm0g) zy!BxJ_tgnts7ih%KR&qvZ*J3^S%KYvf?X-(R5y~O>k3f(WUrG)|2DBQFgt23wA>ok zR!K+{1TknYSFD_fU0S^;hT~H>E#2FoW1Rl-C;1kQuh>!$ zZoT7rTQ8TNHC1l~Uo~kJmY^d%^gw&W`L-T2@T);Hu%_JE6up(C5;GC5Qxyr+B@w6U zf#h$)=w(&;E5@(;X;mLUG!p2Rci^jcBMBmTKDAFd`B;bLKlZuzB zTaF&xJTcJMS|oE9KMEuZJIlG|7+uar=fIcKKN=^%U57tMD=qBPtle1COU8ol)krj- ztDhMe{vEtaNT^%F<>U}Q6YN)TvMX;SI*Kqr)*M!Ao+nX@X?-3Mt^aTItHiwSfKVw^bo&=jp=L(J!NRR z^)@R@Rw+*7(kR3n=vroHL`F0ZAT{Nog|+nZdWuR3-Z9F=aviK>nf5v-~}iW>QSmV}*{ zhiiaUpKM+Ojor#e9H-ch`#v@*iP(uUah4MjsO>a+-t*K(&Z(-MQaL^bHq6`Mj@i%0 zPV3!{=o#DUu<@VDw}Q;^-u3hAWlnz=n#)3tBd`p4n7rJMC+!(Tpi8(>RbH@(#ovFw zB4C^$=$(4{H0yPCye&kw7;r0h6=ZyaeMTRbQSO}Yx=p14Mrq7t${w?B{*;qew~()g zVqz)~pV<=?+C{r3D*MWU9fN@h?G`kf@)=^!mGgB|Khqqdl_8$2TH_FTscQ_JRc~wB zRyax=!bEb&{PF%wyYNS=WtvzZvPOnfHTEfSu>!28D-Elh66cj&1u=&s#2+@&)S>lpC!Jv={DJdohgVom+I- zReWrgW%U^yzB7&K=H)5AZov}Q2;Sgjuh@RAIhmFin#&|PH!5L^jNHkp*wty()It6k zL^gIUGaUsc9Nj2Ny2YQbk$LP{@}9-n>>W|5q?kDwn?`!ZEC2bz8J5jBf&;s9@!-BIO2pLIESr?; zc<06)lT(SELEFD+{N2s+HI&|gfyw-7h5>-Q+`hW1%Xps2JnVI#QuTy6RYPLqN#V;1 zUFh8#w259!X-vUNZ-IUnIdR3%qg1%-Ub{X}dOKOMjbd*0^9~(BtgIexX)!e|@zXw! z*c^zj->N7cEgB_ofuK^#Zf^vv7-TP}k8sz36_q>@J7-B7amB5nAChJR?_+pdRx23` zh&uk?RrVe9$#&36%h-z%#rArNb!#B3k&81zzG|aumSF3KW60N+WdFQRt13l9r~x`;rM3lL0kalK9yIDhGS< zz+Nc!VZ%u7%+@d&97QkE-h491CO{zBb5|&Fy#1Rp__v9R)>T}<*^H+>RaoV>c8eqa zV%-dUpg&gEhrMH3tqAI#rn%DVm6s6O6$a?ztQ@-s3C;V-Qc(WzouPf{ykuOEkS%hn zY9}8WWRij^>n|NaIx!1_k*9C5AYSl6Rx&7R1G6(h`qZY81+x1lhj$Kci?svozoIR+ zzf<4jXO_}T9e{-Tyh4ftg6B?2#o0vnm2ov{34d(a2%_bn(yVFoMc<{$=hjej1geLD z;GsscDW+{iHU+w)ybr@HutqwGp*cGYLY*h4jFSlTGeZfMTW_1j^xCoc{AZi}dy>a{cbjNBxc9_iC838U68Oi&a(E2tq962VeLOr|Cuq*EDKI-<+|V zj}XUa-_dIVq>1ot;sqdI`MhrlKs6nzLG2Tx#Z=kayS*hJ2edL9b{3VnEIyhJt+YE+J=;_1)*z&rN%M41WYcBK zz;8lu3D7=xrQ-^r=7+PD>wAl5W*boj#5&4!aodA+rYooUsyi0S`VRd;+UqX7tSXA8 zQ9LbR9B;r*>1x41eL>O&gQ5jaie6u=XY%8t6ycg#hoZX6C3YHN^XoUL5%$e=R92gmzA!gle+DPz&Gs#O#H8ikqz=u@arTy1@ zxvk0S)!3)nP92V}%g@n{c*8m}maBqRTuo*zDuSUz;@^b1HJn29rLJw_KM< zOwoc3!YU|wKL{>cv%Z)v0c4>IsilXZ+@Vz0tQbR`7@D0Of5?cX4(VTZ1m$<2zmfG} zls}`ZO1hjVOqm6}6|Agg(JkZB_YwBz@dhDBKajyNkt|TtrgPOa!Xd`GU z3fg3I4v~<${PF(3v;ZI=Y8`^!CqdT=IZ0_KPJzM_a0v4wUt)b=1%du?J~4LvOabbA z%|qdXZm`JaNx&$4)I{wD5B!}Ee_0{}FVzq~!2rrjQsQLd9YZCue{q|tq+7kq*~ zunzS9o&5E$Oj@R(8=_h___3HDSDOf!q;PxVNe-~3&t*E8nFg;ctX;vs8lHWywvA;DG9I9{C==+_l~#*NskR1{zf=<1Yu$)UkXTU@w+sG8{tX42xd zFz!xtzk$CIud?F?h>VXV*HHuYQNyXW%_4W%BMz@~3Y~df8#2Ho5w7tN{l)UiNU(ai z9*;XQXH6H|rZqIm5N}dSd3^RDxGC_oFZ!8Zl;J|ivFmNax=!mQP$JS{?19Dy4O|Jl zPtYQj{0=EISv2AfA;#`*4u)9$0zGEqE7VYVqo->a1q0pD&wTtp9d6}X(KE^nd^gllTG z8q{V=nb_HuNo_A#bG7p;s8P0BMb$@bK0AAFn;A?qn*;a!n&zyt;3pM*V<&SY)}Yl+ zHx+NV+M~`AduoV{NF9ri<57NUkq6p8_YdJhW7cn_=uMxU4HyxwS|J9BA?4Sf z?GNI*!>f*y&;0l#AViQZ1GlLuZ~*WQVZ1Vd%*O-y+UNicVTn6P(P@|s)zzCks^sBP zrh`Xuk{ZBQgRoN`v6*F>#U;fHlDbt+W33NPRigFeHRRmSXI37TmO+G4J~Cf+CKV3F8-Fyg0v zI5+7jZ=M_cx3;j+f%#$6tooy}68`XW4_(I-@Xk-(O-%Gwo93+#QR0Gtr)qH=>YbL^ zIPOX{&@v(K*45;!$jaw?s21731oE9HYSSNgY0><2h4y4(+8F2ithJ)y73j^AqAq^$>YVv%d&HBqA|dNZ!mJKp+gOy^`yRo0z?y zdwbeeCo+x$&bLTnt`UBYb#GQS-MmIn@e2Ix1p`wu#};)gTa0t(B*yj!9nYRM72ZzU zFj4V-buhuEkDxNb*1f_7vYZT^pMTt<`WU20zD!uXbDJtscJ$gJpr5N`Wchci-_3#2 zXqrIYzaT_?2&7%7Y;K4`_0b0nRW*N1@8~>oEgG~r!&>kH$%pDojzm9dv&^+j8Uvdh z5BeJUymi(!4`4D*cDP}5 zd`eKOig9@m*Wc#%em6yxS5b4AUQ_ta-=$kU9_Xgs(>6netgcCX+;}C#;r&14z#j%T zZ!$1QqOhX+hpL3{#s&h#UCh@CNaNPf#XXWF;C1Alh0WyftLy5VN^rcsbO;+avW$5N{GDqp1Ynu~3Hfz^^*CBL zRw4f=wCz9clNQ<0%Wz4d&SV#!On*AP-%#VaFHUjcMJ#UNB+K9n{AGS=Iepw4pCxv_ ze3sxC4`agxc-v%i3QRtj)765@nnWc&25Mh3ZbFrj2z4S7rTTh5m$N|g)QhT%%_f)Q zJPbX*gcU4R6q`*1WZ{a66Ag{?Q;`SUWT>_`WMr&fSGrrr#MltgQ=Ix??O?c%rMpO& z63W{+LK&2>f@jFrg{SJE0Z@-d4N#$MP~av`#4o5b4gxZtKHail=4mvb`1x^TvE;p? z+GC>$p>_T{#2{Co5y#goVWl7X;g_&aF0;+%0fjF|rrKStu@Aha_4{qRsLRXQKWVLc z3Ialtx(yth#|@CS)b@E*e!OWxsR!Rt+ezqVhUe2t=FmZ=s*AfjkIjXSK5&|BZ3MX4 z-1tc-|H!37u+e1`QOdQNh18Q^NQ)<`d)Fgz)wftC)DP@(9epF|e=@u3#*tCWvU4CO1##cjSLTsXTdbGa=QgAU z!uqye%suT@D?UOaj0T-5?mYY6&w9@>@$u)J$kZyWLTcs6>;mnI)T6k@2?M#pgY3U2 zeimsQ9K7bLy>0q&(}EQa;0WO7FTY4&{{SjJlLYwqY<^&VL8;b@L@gRgyPOS>!AF2f zm(KYdz~ugDn2a={9RvZ^OwH!+Z$Me#nf6Y~vuN;BW0|5%wQ!eBau}X^yL-xk)shyXO6u#!?ON5}ce9{5B-DF3f)%d$h>N^AZV3a7ONue1f^$krr2JD$4p9K*8lNRcAcOU6C z4bMX8oD2GR6ZvVf#F=uJR?f8Ir1t(Mo9!jrg5=#2M!Er^Hfu%wZI6t669n7onkzn8 z8dSjxbORdq54QFhea$k|Xo@@am(Hf==uR$Qzr3_@g-+(`er~B~I}|jXC(GgzP87?H!3hH7Gjt*I zM^h(-zAG1*7!vZh;K!34+5ycodrtL)fY;u>cXDlnY4YuXKm1ZYirV$8GvJ<7$c;fJ z&K%B>+YGbOSes4mM7-`a>&)_Ca1!n zZFbW6?)2@8S^4|+_xTg{?9>Kwe)~<>`3K+VHAmFq$R7xE;HJa;M<7@`$BiJen@FW` z78I(1VM#3-`FpY4>s5RGv;%0U?!ebLLVHepV9&9Fjc#C4TY9V^#7xH)9wBI3U{?-+ zPwU4|h*I5zYF}TOQ!f}eyi>j`s^z}(@-c}61~Mz)57`-hDTx3)2?$_av?Ff?Yb%b< zuaTKHP``mVd>R&Giw|$f8PN^AkH6-K(f{#XTKT6=*nDxt|IY8+SpdD+S+BtG*W-75 z2_B)gC1smFb~GRJuql985boe}bb&j@ub$MubvXxwipKqm5{9Z-OH22X5*>1k5epZ) zU8AZ@;ht$(QADo)R)FFDdXvgUm}z)s7X<|fCReIYfNQI%0Ia?{ui~1%frZ&HF0gtU zAe&u@A6QHyWNcb@Ul+O@who&NYaoVpm-b!XEdS}Y)Iyk(N00ii+E#NXNcdqhIq8;# z|4|o<*@BA7ARKhrSHDYhMLXZ%^C_CzjSI;UDxF1SaMC$LZ3IMhu1ohTEH}?xaL#8}N+5ZWL>H2@ zA~{hmt^MNCh_}$^yJh~6cpWdAT7AwD^=lLY!)~N@@-m&e2w>Wf4Lxq*0P2YrFo+xr zUL6!W)(A=5mwMe@FKIXL{^El(qb-BGU=zP~_u9M}mAvEC12qLBW zTQ$JyRQw7oPtpBI5|{dfz+8eI=)E_eD&OA~%4a9`t8x#&-0>r_N(V%oQ#gm{1IFIH zN6=kXCtjZvYFk=Rt|y?mXGiVV#h~?j2k{l_CZQivDPMu{-{5>8t*k?sohTn%k zt%JUEo8|UMzfSy?L$_Sh^9KXZf$`bc%>1yAJi7g0d(%1{Hti&FH)6L#{31wWiFrZT z^$SbtyR{<~2?0X@J?^shsSUQT;{h>js9DpStK87JU)8RXcvDA02vAc{4`-yDC>=nh zsg2oe8FA`^lP$OS9P^>;IxS`Zz=O;?!Hb)HeuNLQ(Xqdz&vhF(`cfm%Ut7?zh+65Z zLK&1^mGmxoCXO6=h`QxbXJxO4|GWhDjA6T`iXpjfUBLuT5Vh7r>OuJg3RkLNQnp7QUT??bRQn*(^`MAlnP)2L(FpV z3&u=?ERk!NvO)#Iu5Ws)?=Iz(+%XtVU8T}QK%CR=!28uCWMgJ4^=EDY-GqEK$bAuLW#jzN zB)3A7{59?(9k%34W4mVGq7t3hGv==Q?Y1Iu`J2>Pb%NK_Me}skoYn8jrmR_Jzi4TF z*&hQxB@&7r4mP7qXN;tj`w2~y8>VbaLjF^a>-N*;mS5`=zn0Law`gACs(8;FIxH+V z_787OGxbF_u0L-FxdOC9q?U_vx{I(Vbon}n99vki%EM{?l1IG5)4<@4R}{ zzI|Sz@xT_iNdAfTq-~S9k*c|&f&HT3OYn*ELg-i$2l1SJ=4l!57=DUk_F&0z?-NwNdy!#rW?xjJctgj5*+z>%r-_%s`B{WIp50*ZQ&>4 zIYeJt%d~e2TAyz_35R?GkE{=c{|4dyJ0BU(F!NS^iXPP$@gK7yB@`%*ujAv zeK-4Xa*KZ@O|nF~M5qGd)A2ESb}TJmZHU~*xt7QPNU91-|K1P&C1@JE(9P9d6y4(u8LC6$v6(AV)1(&W38vzHZjQl1cFIuT&=Fa&>i zlOw#?YYQouQFepzQb;jG;@A?#KMlLPR&URz#~2YedW5~V8&*`*oVRk z2j0qX(|^NKulCL&eb!Mx2;m4J7WNtV?2Vdt0m=gaqZ&;nxD#SLyDOUkB8%iPl<+6EZh8aM*uZ^!LZuMO) zQNH0~5ar*7nwv;3RJ=Do#x{AvCWGkD`Mc<*#X(P6_w%fqf0UBn_q#xL^p1B}oy`3=z&@UkZH3=|D$WXzf@0HDd?@7Y!PXun-pFv>l8`m~OzyT>R# z-R#ZD%+l_7C=f%4NGD2Be?p6=9_k<6RYT%qA4VeGr>m-fdPzNL?tHIsK&X~Z^`+9T zF-vhVZR?I4@9RGn#1kOAUj8sQaZQSXCN%DCyy3y|F^UXu#ldf!{QX+gfdT;DR@?dBv8@7VA)wBHWw z(7X4*Xl_g}iKgp2u-<3P7~iY9q=NZ4bK~eF)FTjme6RUxv=&cs4y7@V_5t#w<9C&s zuV<}GkhfhFeYDmUJJrJaUei*0UyI%gz1^xOxvtxKwZ`E{A?0#i{4Bh0=$;M2C!Iq^ z{ae>-KXjL9;1cK0`3AAf$5-2xYlczS;S@%b`#9%}; z>%NTx^q})M*YJlrX9q8Eyq8he6uS(owV|_Nq!l8|TdU!YtrhX7l__T!nxB?*t6GeO zwy`eN&ekga1qlA!v1B2d*3XsNtyF_p;C8Q|2X3$hWv+zCf|EUVwqpEfEGA97RS>sLrFf*WJ#4%j&&N9e0(^!Hv?lzkI4nhmQ~o#W4bgg zO{}{`E={-b*_2tH7|A6+GZ*^YM;ZR@dP7N$WKp`!Du*j8o&gi^?Vzz?`q# zOQ24+DS9kQSOIUitQ^i52BzrMBPBEEFV(Zo^B&al97^*Cp4$cE=i)qG^fv+!brbo^9SP>%#E25*@>AXA>@9S1NVgzgbgeUdn6*cnAR+ zLnqXS_L`bnT99>b0aBUp7x}jMS+kCO1p;h2m0YW#NzuU++u182>ufq++4Ss znZ{8~jlrP)p&P!1%xXDR5Nh2<&t($<#Qp!2+RVlJ#jXybGK5dj)ztl~Y+t{5L+X#4 zbBlL?daf?faqKpMQ0HgC8M#MRqX8x9)j%>Z2x!(b^a8zdT6D4@vd7npg!`rQS!?Enj)uKIE2CuE?6ALuE%WBfF>83=X2uh-ue+5rH} z$hY&o96`^3hywrzm$(ywriju2$n;%_&YAnNZa-~N3ER)S1NcJse=CyDW<=3|mJ7hb z%o3r#P2kJFihBU9A^?~DNuhXa&lUi6vQvJpfx~|MFBr}eOKypExRu#{!$rE@hqOBw z2>&k_j&Ij*VfW08({rni(-O#E%BmK#c4+WHm&5f4!mIcf52DX4!u%o(MbLyX+EoIF z+okZuby3%`JttH3T`V_Jw1A*b`+2$*a0+?El{n%1_6!Zz0DksbV4T0c4gB%1l`wSv z{}wUXC%Yv7|FVMZKNq_7oQQw59OP+opPOI6a6B-7MKTC~9?x!EaIJH7^q#w9 z-|%S3PgBt6={(ykXIs41VGnp?s$Buitru2t&!a< znK9VzSdkmJN4Gq#h@-zu`&+*JQ-Z-`7BM9fBSCFKWTUnarWFrO3yt zuveYmy?Ejr+3k>dPjedws)`!&gpI?;w1e>Rg>hBFnr%UKXIb^`evo;5GZOk) zF%?y9y}TscN9)*1N7uZpQHmG}qt=2-xm zq_?{%+6ivH=7`o03I*CtOH$?@w?}4ltE_ny3MbF4C54WV3mEFe^o(k*UH=w|1Q|s$ zBovyjzH+B^QWiO`XBgv61HGKZVJj zS!PhvK+uck*;)|dFu>i)z3Gv_tao8KkF((>GkAFr4?k4b#(Mt4z#RG=&V{Oc+%lF( z;`v7z{fh$J2rgRR1+T82+#Fgt4*JUOfoG&WxaITzNNVkLt_znLy|cKy6g|M?)8|YP zncZt>^syCmMw%3z@n|j*E42QzGVD%tCcHwR!LMtFE1sLM8>xCy@UhL*4QLGI_^~)m z0MqoWGT^i5a7W32`)IeZc@GZq-eI|@_`@6VB!TH?d@FJquRc6xRdned)<`HasSFxW z6;FLopuLkKq%X)1Vx4sA9@H?7xtFSqo`vsA(9S znfr)tUe!je7mk$52 z8`Nppklm&sdJ(kj0~iO;tu>`Pq4+#m4huzU*YwVGLzA&0dJ%f5-F`+xngvF^Qnyxp z+tv!~q(A`Wr|Z`7?BR-b3cvA-lMm0}0w4j9@SvE+hjO!QpP9Il0pS$#JJ2f3gjm!~ zL<>|)QmMNnHF@GzAKS@cySmoiQ)P{8yQ5&`r9iq@srItnN zOd5_~VNZ%(%BUW=)Y5Bl98T~tM>-f?v!MiANHr-kRrmL9NIXBdo?ftT&4VDlmB~_2 zg{grPG-n_6`{={8b&)l7i(?YANo+9#=YeVCdgSExaDskeb+HJPxBRi63!$mbyCle@ zo%;*xftj?Qn?+IrWi}%pip&%Dk-*%I>XY}P?vwSGG7&4{o29rZoH#C_MOH}mHnLBS zFg&<)z*H#Fvmucgmw51jN`zLe#Xtg&em`XPd%!2EbLm=gG&oB}U&Z?mi#C#Sns-5= zqF|y1-$a_jLvc|&-$criQHpl0NJG`VIUm=4qaJVFf@@9)`A;^9@$HT#nDnHEmsRlH z#oeKDYg^TL-n8c3dCXRM>9T1%MoA(Oxw2rWP?>(~os#8D=YzVk?ow*ijo#5w7X=@? z#k!vE(ZVr-;Cqe}Y^0tCAP&`C&Hc35)a11%X${Ra;yzS=J4=naLh^q3;|zt$tp^wg zED^pvqdb}Du$pciyg!j5i@68f=bB4DqgsjQJSlvBVwjUC z#KfE*rD)bYIR#hown1U15rW0y+=MFpoApVnN zxk_2AO8T(~3J>W5krP@-Vb;_HC3OFx|bo!?PGQ= zV>-Ry@}Z70w4kpm!Mw(a3_~H$MtLtBfesG}bHwL@ZCfB-$AYY)7OuzAj8ufA3T=x_E++&{xhk>X$-R(((4IU@{~Q zDL09)tZ8eQIIT{wEgq37;zne8dp^YWtsy!Y%e$G_SpTpvH`JRPLGC-@*P&IRb3_-S zZD{$O&ude|0k9?)%)w-v0wTypqd*?4BbA6WuYfDP8nC(!$J1SBn3 z)xmR(zVAPV7-{Gl`}D0>P|fjqOPQyA`cKpW!c`S9yj4Te)3}!rKkLq3PxZX}_m`?X z>Zek|d&X|${ODSD0Urn^t`mEuNA>17MMr7NOe0TWw79o3J*9+?JFrs5K+&|j(;Mjv z!CekK+mUEv<6(Xx<@#8lR`osGJpqtus+0I*jy~<}P0&y#TEC1q>Ot71Ec$@!`E|hM z@tByb=SCbz<>npw{V@uuS8Zp{uJ5lmE+?KFhqcE?ocmL3?)|I1Z7-trmliYeQ03y; z&E~lmp2Gp&#EjYgI-YSg&QkJ=hV9|#NBb~BY6yw3Zr%s{Bn}^JyZkmVWz-}gPo+PU zl6YK^@A^!2@4d@L<0kvI6mgGlrF&A-V}nd#27m@*ZDj9utkKc)V`W(j+XHSj?m||+7=d{P>g^*(oO8o+LysfR^1>e?2|b+ z`LwLW_V0oLIL?H#JBTf7%K$!hNg=1+wD>#`Xcl7c_d=hsg(G5lFB4n89&Y4~OOZ;1 z&)4b0EVo1d^I|8yYf}n;odGDjuSLn2yK?>&jI~gk#FPF85vUjQ@=B@1qQS`HWTXXX z=;ICDwyi}%U1KUR2A(cp)9~}uKBe}2rz-lN%W3_ zssziZwT<#8);#-^YANCl_HC(l)!b<5QCE)bc4<$R?ZgY;^<3upOnSk^t(Nsn;UIbC z4Dvc#L4NJwiyC2xJcX#xIw2zB$J*hTv_$;d= z2iuKZPX7XX(%&O_0&`0O7B)#(!)tp$SihdSxDX+-l#eO$UmG`0YD0M0C-QX>Jm%jt zka~t*mTtA|I6-|D69C7UMG`%*k{QJ%ArYjY+x@CF;@n9rIy|Fw^O5>B4i0`B%TDBj z-jrDx$rNS5tbG)-z?^GvxJ?;zS=S`{_ONoTg88hkefe{x1gI~pZ?0}y^(pn+)4yIr zKwFl!$D@OdU`<=Sp=2ZEx<$qb{w4t&nUYr{9n1ZFG)vdY%t?DOQE;JPttVAg3c3f}r-6?=~q`;+4qKLit1aNEm&NBNv|p^Mq& zgkdeVabzax;USf`wSwL_YDqU!QlFj<+;5?pOxmp}w@uNs1x?zrXH)Nfy-I1QmD5k$ zbqK{Mc9n;0gsltbCl@+C^mnA(1ngjb(!67Nf(;2-bad0hB{!PRq##6Kok`LHbp-;q z4|n#Kh6>Wn+EX~$y4t#?rd1Fa&-9b5Z8hKP2+OQ`rGU2$!seN>Voal^?3e)k zN!w6y+zHv{HwXy1D|&bUXAhhXS0K(~M@$5T%E<$BoNiaOkG%+y}85tU3VT`@RYjFUMnb@OV>Seg9& zKGh9mYf#fZVy6&uFf!?Ta_}aElywPi%TB*LZ_j*j_jka~Gv=2lP7B{0vipm;2{CH)s6^E#DS^zf4-=Q3*LQK!M{c&9kJKK!&6Ut-|nZ&&uCB|-6y-6 zvL@Ka*Pf3#glE~LaeL8jP!HT5HsUkSIr_UMJP(0=TD!LUxtz)Hh|_lLEth{*_D|&v zBWNHM-PvEF4VGVcjKME}M*WXp&5r&*-I(M0k>2y}RRKMowVQOFuiT~^H?xg6JufcrO&zZ6Xl}I{v{g<`=?3L|I9`M+3_>-QC5#MuL?ue=ca6Js_me{SU?60)Mc|TgpqOlm*J66E z!V|hns28(mkX~=RW81i z#mz)@DG*a*=U>?}o}9UBjfRcatKHE-GR4l%@HWMByY#p)3NlaD(*|n);ayQ8=6cP< zZ*&`0oVGm&F%E0>?Vou7FZR3`I2#TTOR_bxD0CYQsP=As8Vg{Yz(HMy0KJUa!KM!< zH`cD!Dm!OLg;DBAy0*5JT>!@MarRI^lz6Edcw%AM7r(+G-hJ3bY{-^zQhxs7z)I3W z+%`v?u6`g5M+y>3z%AI)>nZs8@6*AU8-S61DJb;I-w$kj1AjS9PL}jf)7JKNva)3% zE9uu-gw(1zIX)Sp{kGd*W~#YuoWjLL2t}s^cx~KE@S*2DL#NrUzHy4K5#i@XP#k}7 zT^tyV4|=~b;^?ls0wa>Ev-0=ECXa5Qg(?88-uU0;`TvZW`=d{pzdo&XpOGJbK=<_B zb6Q|H_c8?n{6qJ@5(rApZ7()V5|CR@V`!%cv%fDSlyDAVi_eaL!~Tt5joBUU++kVtpg2*Qv~UQub;V+*u#8xEmh?}S$-u&F=>k+G7pLb17%Ty36l*X#)QFw zgkmxRLSzWhQo#WNBqR_7qB0pEL;^yEDO#vV5{Q)u7^Y}J5P3 zbwBidxKDXF=j^@DK5MP4|8ISJ?>%Hg%#Wr%DNcLd*ChO^4adK|gvW+6A0M|i+qGm1 z+@qD5_h2{KaFCF=vAl;REq3FwsturFXGwdkS$1qyL(F`4VY}bf#)QCV?X*v!(Vf@8 zMv3CBoK=4HJtM6qlsuO4YU9MJ=0Gm{P14m}Cr>Xuu!9ihj9X1{B`!*h>+94ek96;2 zcHPJ~VLF?Yh!38@(vW78!d^A0U`*~S7PK_~A#z#lsoHsEro_ARlw7i~oU zZLh~fO#+r2;cZ$`5P?o6H_IAgd+FfwfqRGi&K87lb-3dR!KM?>fvms@E+o&AlBHSL z`t^F>^BQP!aGIB1~GDtJ+-{bj4Gy0S;|bo|EU)9SZCWg*TeJSq5$#ACUFsWL*&-sHwFmO=}g1 zNZECzMX>GU2?t!OZ6khQT}+1&g@6!_{;}8(=VVwdnT*kut;tFE>k|EfK$sR}$4>Lj zh#pxbX@MU;A!-hFE#hO}lSkH+y%0janKz&C%+NsZ3kNRYvNlTH<;Fd^x>s#X6f%CX zW2wl;SN!q$_bplNBSkFHy+SYj`5+P8VEPoaU%fj2IR$P+6hd8;Q=LW!1Vy8)jyGkM ztN3YNXk@gM+$|fI1gls5frCEBB||3gw5Cx?1g5k2Wl52f5;xm+F{qC_Orh4qnkOw3 z_YMe)+*@b!n*fD01U}hlo#?jFfWW?d{-(sytPmR;7qYz3dH{SCGdmYLaWgWO@A=8Q8w^Gd(}gUVbDwg1vors(rSrM4qZwGi@uZK}nH8YO$OwX!I-Vd* z^%+s{iUq^YPY^`0FaNP+3M(j#WD=ZWk49ds31HhiuHoD;=_zxKbD2k&J2xOo9aa&T zeObxk@=9Y38f&>EhBz!Z%GqlS35lk*_pI@r_K)( z6&^IeZa=uw%n#aotGXZVUqMqUF^Rklm=%St> z=CXu{L9zQ`Q7F6AB#@FiMxC&bEnbnMT?;izKnuz<-t~G1#Mupm0<(D$UZ%UUNzhX;C7IrmzWL64oAYQ&WmA`42*XMQb1V$8c-YIOyvdX&xjPj0jB5(bi{g-<#OrGbsg3x zdg^=d&@kNGj83DH9o+2OJjj!gIBi#F=utnhbwG# z(Gy+v&uBxw+vs}sWif1k&elVXZnHb2K*nU81HHDS*0;A*t|ZHP1jCe&i?68F7UEub z%7JcVhI+Y#oY0I4GRm0U>WXUW>^HnI5V3|nh@qz*k(G^<%q<2aJk#rW1M{Il1nMC! zj65;;RmJtlcE~~4N2~mU(lv+w|8PQiff7Si)N;< z!o}g*gcY?Hb0R!R&+S3cIlI5oCR=cW8$E`Q^LU$~!Ip^?n+;^HLwSJk*d+j}e3prX zXs(IuzAPXS`T!f1X339PI&`GAx-cZq1rsGf7dg~fm-Bsn0tL$%i`iyaeBL8Tla?GY ztnnoTMuFMRiDm<+N%?jnq#u%8nG`ttszE_7C7Gn5=dj;^s}7~b`VIGyEmuC6C5Ud! zMLn|kl{UWZn^!Z2q|=GB&)5vB#TV`ot5VCD+7ZSgOT{!2ZD{wsB1&cG&SN+YZ6kU> z*Bjb(%S1$MPKqI##BI$6c3Qj|{)z{<=tF5X1Ii2!CXa4}X}mrz2uKTb|4CUZwWI1# zdAd$A#w;|gO(3%P%Wx?^n8rJ-FU|bv9C=|Ej}WBo0%(kOk`KhR;;3gw zy`Ms&AG~j`ou}Nb-g(VfS}LFFYi(^6sFQO8BrQyro)0I!+1wq{^C|Bpt067;aWIfr z;KoWlb!g-{c^#TlU$gr`hsa%5Hqh`304nXwY!A#02mn% ziD>+I5vgS!4XJVXs~Ku`OJqZic_Bd%R_~ttXwqIat~=3m8zLYLhQ1B09$mT~ikzkp z1&Cl$K4d^MdHb5~aZ5{3`Bw!=YpEw^uDrYLn9+ohkE3gnxb-}Ajn7LT9w9RVaF)J~ zE57sWLfy6-1~o@uZ7K1n96EGt`Q*h7v`)1Zq-%lf*wJ6C%VbYDLgt@0$F`Z!AW6~R zP7<0T2R^)Cx&q{_-{AkilZVbs{u4Zz{vmj!@rkpN1DFiDXVGc2sP7CiB9Kt;G(Y98 z5X3|5Rv&gA3`&1^@IY;vr(5kHx^J!OcF%;fniq<-)e9Q7n2N^! zW0hT~6$TkgGj(C^r)Kz_7|r)DC2PiCrpq6ibbswO0s)1jfK*s^Wa-kKy0t~UDAy-P zMU8g+wRf+%25lHI-8m{`W7$vIF}{E9-ud+jcI4l!bh{w^9-p&x3MW}U0aV=*N=3moGlh`>Qk+hRW z)yOEX4d>F7B_I}`*|>k)`hbv(>zk=KL*jfDbU<*yqFGLSKWTQQIn=E%YwU%jaAR5Cl%DQe zg|FwlYLeYF8#^WZnauH}WVtJ@vlg$_AlN^l=JFmRq>y11Oi<{4 ztvIz1hZAS$9D1m{5i)`WxBMA#uTrzHXgS>< z2Af@qA!{{F_ z9-tL;2y+sCv6=_Fd=qI$kY!mb8zOMBW<4LJzM%y9j2Z4IU z0=9pXacSZAA&cCfk4oq;LMG3OS^#f!U{oAXI2?BHC(3EQz+f5`SGTY0AT2IxR?^Om zZyxycZgdZM=hv0fYCjQ?eP~d|%kGWRD-;q;+LX~z0W-dyM*0)`HzjXR^HMWl$g#CZ z9sS{<)%w#Jv0ssQ?r)nv+lffUKF+e+r*ku0d^+VjKo0FV$#+Y<->=g9;s(+n*=X}* z^QWhg*KokE*1r!V|8|I;oyq{668m8D;~7<7Hm&=vDqOggtIPgw-Rqi15)VH0>lc9 zE3hvL2gDuT+1<_ZUcw3Am^z+G+DFRoZyi*UjY1INrH-u5e0{-romnD$n{R07nRs8z zAS01^B_y0Fr>!^Mb~;W-3yOnAS5+XP1n)lBb^y?ZIP{eDT^FYxW>nhA3?_6f07F>i zzt-X6jSCw@1(B--h~~J_vI^6rZq@W@b=Z^fO6r7Y+A}5(P%WEqwI_zLU|x+&OiRAZ z*7m04(~k7uciJqOv(iB}XsF;=?-a1wHjMfJ;5ooJhxrB{B0Pu0Q`icL;Ngx)^-D)Y zYK6A;)7?+r=Z~!RdXA;6EywHK!`I;wHR8}%tPDh^Gh#W;IpYG`gFTrCj-LfuJN351;v=nz9GyFlxzGSk#i%Q`;t73)j#FoQGB&=8MaY@-qOJ`*H`Hf~*cD1GHS; z@ebFDVW-Q6P+B{5{bbL6bmEmHKwL#J)!c_~z5+(0)n#Gnvd&Cr&es3*VlMI9&F@|~ z@J!!O%d;!fGXdQolmPHoSHIKXyM{8Dc+m=2TB2gS56Eug#iTGT1WiIp5=lH32y6eazEsPxG9T5#fSXu#2FGW<@0K?4T-1sw^Lpsn~hbG_kDHd%UC)iQ>IvmH2}ssaTRdF=&`xN#kVKh+(D3+(=os-87qnqf*SFk8qd%}VNR2M)G)UbdRkpU!}HXM z7kCcu{flZ{?X)6JvQCi4D@T*POLbyj_qNp+RZgn|EMu;@;Fl{~R~>Yz--w z2owpvAM4=7mey|G_fXf1H9 -* In RavenDB different documents can have different shapes. - Documents are schemaless - new fields can be added or removed as needed. - -* For such dynamic data, you can define indexes with **dynamic-index-fields**. - -* This allows querying the index on fields that aren't yet known at index creation time, - which is very useful when working on highly dynamic systems. - -* Any value type can be indexed, string, number, date, etc. - -* An index definition can contain both dynamic-index-fields and regular-index-fields. - -* In this page: - - * [Indexing documents fields KEYS](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-keys) - * [Example - index any field under object](../../indexes/using-dynamic-fields.mdx#example---index-any-field-under-object) - * [Example - index any field](../../indexes/using-dynamic-fields.mdx#example---index-any-field) - * [Indexing documents fields VALUES](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-values) - * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) - * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) - * [CreateField syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) - * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields--terms-view) +* In RavenDB, different documents can have different shapes. + Documents are schemaless - new fields can be added or removed as needed. + For such dynamic data, you can define indexes with **dynamic-index-fields**. + +* Dynamic-index-fields are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time, + which is very useful when working with highly dynamic systems. + +* Any value type can be indexed, string, number, date, etc. + An index definition can contain both dynamic-index-fields and regular-index-fields. + +* In this article: + * [Indexing field KEYS](../../indexes/using-dynamic-fields.mdx#indexing-field-keys) + * [Example - index every field under an object](../../indexes/using-dynamic-fields.mdx#example---index-every-field-under-an-object) + * [Example - index every field](../../indexes/using-dynamic-fields.mdx#example---index-every-field) + * [Indexing field VALUES](../../indexes/using-dynamic-fields.mdx#indexing-field-values) + * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) + * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) + * [Indexing dynamic fields for full-text search](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + * [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) + * [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + * [`CreateField` syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) + * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields-terms-view) -## Indexing documents fields KEYS - -## Example - index any field under object - - + -* Index any field that is under the some object from the document. -* After index is deployed, any new field added to the this object will be indexed as well. + + +### Example - index every field under an object + +The following example allows you to: + * Index any field that is under the same object from the document. + * After index is deployed, any new field added to this object will be indexed as well. * **The document**: - - -{`public class Product -\{ - public string Id \{ get; set; \} - - // The KEYS under the Attributes object will be dynamically indexed - // Fields added to this object after index creation time will also get indexed - public Dictionary Attributes \{ get; set; \} -\} -`} - - - - -{`// Sample document content -\{ - "Attributes": \{ - "Color": "Red", - "Size": 42 - \} -\} -`} - - + + + ```csharp + public class Product + { + public string Id { get; set; } + + // The KEYS under the Attributes object will be dynamically indexed + // Fields added to this object after index creation time will also get indexed + public Dictionary Attributes { get; set; } + } + ``` + + + + ```json + // Sample document content + { + "Attributes": { + "Color": "Red", + "Size": 42 + } + } + ``` + * **The index**: - The below index will index any field under the `Attributes` object from the document, + + The following index will index any field under the `Attributes` object from the document, a dynamic-index-field will be created for each such field. New fields added to the object after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the attribute field **key**. - E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. + The actual dynamic-index-field name on which you can query will be the attribute field **key**. + E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. - - - -{`public class Products_ByAttributeKey : AbstractIndexCreationTask -{ - public Products_ByAttributeKey() + + + ```csharp + public class Products_ByAttributeKey : AbstractIndexCreationTask { - Map = products => from p in products - select new - { - // Call 'CreateField' to generate dynamic-index-fields from the Attributes object keys - // Using '_' is just a convention. Any other string can be used instead of '_' - - // The actual field name will be item.Key - // The actual field terms will be derived from item.Value - _ = p.Attributes.Select(item => CreateField(item.Key, item.Value)) - }; + public Products_ByAttributeKey() + { + Map = products => from p in products + select new + { + // Call 'CreateField' to generate dynamic-index-fields + // from the Attributes object keys. + + // Using '_' is just a convention. + // Any other string can be used instead of '_' + + // The actual field name will be 'item.Key' + // The actual field terms will be derived from 'item.Value' + _ = p.Attributes.Select(item => CreateField(item.Key, item.Value)) + }; + } } -} -`} - - - - -{`public class Products_ByAttributeKey_JS : AbstractJavaScriptIndexCreationTask -{ - public Products_ByAttributeKey_JS() + ``` + + + ```csharp + public class Products_ByAttributeKey_JS : AbstractJavaScriptIndexCreationTask { - Maps = new HashSet + public Products_ByAttributeKey_JS() { - @"map('Products', function (p) { - return { - _: Object.keys(p.Attributes).map(key => createField(key, p.Attributes[key], - { indexing: 'Search', storage: true, termVector: null })) - }; - })" - }; + Maps = new HashSet + { + @"map('Products', function (p) { + return { + // The field name will be the key + // The field terms will be derived from the corresponding value + _: Object.keys(p.Attributes) + .map(key => createField(key, p.Attributes[key])) + }; + })" + }; + } } -} -`} - - - - + ``` + + * **The query**: - * You can now query the generated dynamic-index fields. - The `_` property is Not queryable but used only in the index definition syntax. - * To get all documents with some 'Size' use: - - - -{`IList matchingDocuments = session - .Advanced - .DocumentQuery() - // 'Size' is a dynamic-index-field that was indexed from the Attributes object - .WhereEquals("Size", 42) - .ToList(); -`} - - - - -{`// 'Size' is a dynamic-index-field that was indexed from the Attributes object -from index 'Products/ByAttributeKey' where Size = 42 -`} - - - + + You can now query the generated dynamic-index fields. + The `_` property is Not queryable but used only in the index definition syntax. + + To get all documents with a specific _Size_ use: + + + + ```csharp + IList matchingDocuments = session + .Advanced + .DocumentQuery() + // 'Size' is a dynamic-index-field that was indexed from the Attributes object + .WhereEquals("Size", 42) + .ToList(); + ``` + + + ```sql + // 'Size' is a dynamic-index-field that was indexed from the Attributes object + from index 'Products/ByAttributeKey' where Size = 42 + ``` + + + + + Dynamic index fields are queried with _DocumentQuery_ or _RQL_ because the field names (e.g. `Size`, `Color`) + are generated at indexing time and are not properties on the _Product_ class. + + A strongly typed LINQ query such as `Query().Where(p => p.Size == 42)` would not compile. + Use the string-based `WhereEquals("Size", 42)` or RQL `where Size = 42` instead. + -## Example - index any field + - + + +### Example - index every field + +The following example allows you to: * Define an index on a collection **without** needing any common structure between the indexed documents. - * After index is deployed, any new field added to the document will be indexed as well. - + * After the index is deployed, any new field added to any document in the collection will be indexed as well. - + -Consider whether this is really necessary, as indexing every single field can end up costing time and disk space. +* Use this approach with care. +* Indexing every field can increase indexing time, memory usage, and disk space, + especially for large or frequently updated documents. * **The document**: - - -{`public class Product -\{ - public string Id \{ get; set; \} - - // All KEYS in the document will be dynamically indexed - // Fields added to the document after index creation time will also get indexed - public string FirstName \{ get; set; \} - public string LastName \{ get; set; \} - public string Title \{ get; set; \} - // ... -\} -`} - - - - - -{`// Sample document content - \{ - "FirstName": "John", - "LastName": "Doe", - "Title": "Engineer", + + + ```csharp + public class Product + { + public string Id { get; set; } + + // All KEYS in the document will be dynamically indexed + // Fields added to the document after index creation time will also get indexed + public string FirstName { get; set; } + public string LastName { get; set; } + public string Title { get; set; } // ... -\} -`} - - + } + ``` + + + + ```json + // Sample document content + { + "FirstName": "John", + "LastName": "Doe", + "Title": "Engineer", + // ... + } + ``` + * **The index**: - The below index will index any field from the document, + + The following index will index any field from the document, a dynamic-index-field will be created for each field. New fields added to the document after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the field **key**. - E.g., Keys `FirstName` & `LastName` will become the actual dynamic-index-fields. + The actual dynamic-index-field name on which you can query will be the field **key**. + E.g., Keys `FirstName` & `LastName` will become the actual dynamic-index-fields. + + The example uses a JavaScript index because it enumerates all top-level document fields at indexing time with `Object.keys(...)`. + LINQ indexes typically create dynamic fields from a known enumerable structure, such as a dictionary or collection. - - - -{`public class Products_ByAnyField_JS : AbstractJavaScriptIndexCreationTask -{ - public Products_ByAnyField_JS() + + + ```csharp + public class Products_ByAnyField_JS : AbstractJavaScriptIndexCreationTask { - // This will index EVERY FIELD under the top level of the document - Maps = new HashSet + public Products_ByAnyField_JS() { - @"map('Products', function (p) { - return { - _: Object.keys(p).map(key => createField(key, p[key], - { indexing: 'Search', storage: true, termVector: null })) - } - })" - }; + // This will index EVERY FIELD under the top level of the document + Maps = new HashSet + { + @"map('Products', function (p) { + return { + _: Object.keys(p).map(key => createField(key, p[key])) + }; + })" + }; + } } -} -`} - - - + ``` + + * **The query**: - * To get all documents with some 'LastName' use: - - - -{`IList matchingDocuments = session - .Advanced - .DocumentQuery() - // 'LastName' is a dynamic-index-field that was indexed from the document - .WhereEquals("LastName", "Doe") - .ToList(); -`} - - - - -{`// 'LastName' is a dynamic-index-field that was indexed from the document -from index 'Products/ByAnyField/JS' where LastName = "Doe" -`} - - - + + To get all documents with a specific _LastName_ use: + + + + ```csharp + IList matchingDocuments = session + .Advanced + .DocumentQuery() + // 'LastName' is a dynamic-index-field that was indexed from the document + .WhereEquals("LastName", "Doe") + .ToList(); + ``` + + + ```sql + // 'LastName' is a dynamic-index-field that was indexed from the document + from index 'Products/ByAnyField/JS' where LastName = "Doe" + ``` + + + + + + + + + +### Example - basic -## Indexing documents fields VALUES + +The following example shows: + * Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. + * Documents can then be queried based on those indexed values. + * For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---list) below. + -## Example - basic +* **The document**: + + + ```csharp + public class Product + { + public string Id { get; set; } + + // The VALUE of ProductType will be dynamically indexed + public string ProductType { get; set; } + public int PricePerUnit { get; set; } + } + ``` + + + + ```json + // Sample document content + { + "ProductType": "Electronics", + "PricePerUnit": 23 + } + ``` + - +* **The index**: + + The following index will index the **value** of document field 'ProductType'. + + This value will be the dynamic-index-field name on which you can query. + E.g., Field value `Electronics` will be the dynamic-index-field. + + + + ```csharp + public class Products_ByProductType : AbstractIndexCreationTask + { + public Products_ByProductType() + { + Map = products => from p in products + select new + { + // Call 'CreateField' to generate the dynamic-index-fields + // The field name will be the value of document field 'ProductType' + // The field terms will be derived from document field 'PricePerUnit' + _ = CreateField(p.ProductType, p.PricePerUnit) + }; + } + } + ``` + + + ```csharp + public class Products_ByProductType_JS : AbstractJavaScriptIndexCreationTask + { + public Products_ByProductType_JS() + { + Maps = new HashSet + { + @"map('Products', function (p) { + return { + // The field name will be the value of document field 'ProductType' + // The field terms will be derived from document field 'PricePerUnit' + _: createField(p.ProductType, p.PricePerUnit) + }; + })" + }; + } + } + ``` + + + +* **The query**: + + To get all documents of some product type having a specific price per unit use: + + + + ```csharp + IList matchingDocuments = session + .Advanced + .DocumentQuery() + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'ProductType' + .WhereEquals("Electronics", 23) + .ToList(); + ``` + + + ```sql + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'ProductType' + from index 'Products/ByProductType' where Electronics = 23 + ``` + + + + + + -* Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. -* Documents can then be queried based on those indexed values. -* For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---index-a-list-of-properties) below. +### Example - list + +The following example allows you to: + * Index **values** from items in a list + * After index is deployed, any item added to this list in the document will be dynamically indexed as well. * **The document**: - - -{`public class Product -\{ - public string Id \{ get; set; \} - - // The VALUE of ProductType will be dynamically indexed - public string ProductType \{ get; set; \} - public int PricePerUnit \{ get; set; \} -\} -`} - - - - - -{`// Sample document content -\{ - "ProductType": "Electronics", - "PricePerUnit": 23 -\} -`} - - + + + ```csharp + public class Product + { + public string Id { get; set; } + public string Name { get; set; } + + // For each element in this list, + // the VALUE of property 'PropName' will be dynamically indexed. + // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields. + public List Attributes { get; set; } + } + + public class Attribute + { + public string PropName { get; set; } + public string PropValue { get; set; } + } + ``` + + + + ```json + // Sample document content + { + "Name": "SomeName", + "Attributes": [ + { + "PropName": "Color", + "PropValue": "Blue" + }, + { + "PropName": "Width", + "PropValue": "10" + }, + { + "PropName": "Length", + "PropValue": "20" + }, + ... + ] + } + ``` + * **The index**: - The below index will index the **value** of document field 'ProductType'. + + The following index will create a dynamic-index-field per item in the document's `Attributes` list. + New items added to the Attributes list after index creation time will be dynamically indexed as well. - This value will be the dynamic-index-field name on which you can query. - E.g., Field value `Electronics` will be the dynamic-index-field. + The actual dynamic-index-field name on which you can query will be the item's PropName **value**. + E.g., 'PropName' value `Width` will be a dynamic-index-field. + + + + ```csharp + public class Attributes_ByName : AbstractIndexCreationTask + { + public Attributes_ByName() + { + Map = products => from a in products + select new + { + // Define the dynamic-index-fields by calling 'CreateField' + // A dynamic-index-field will be generated for each item in 'Attributes' + + // For each item, the field name will be the value of field 'PropName' + // The field terms will be derived from field 'PropValue' + _ = a.Attributes.Select(item => + CreateField(item.PropName, item.PropValue)), + + // A regular index field can be defined as well: + Name = a.Name + }; + } + } + ``` + + + ```csharp + public class Attributes_ByName_JS : AbstractJavaScriptIndexCreationTask + { + public Attributes_ByName_JS() + { + Maps = new HashSet + { + @"map('Products', function (p) { + return { + // For each item, + // the field name will be the value of field 'PropName' + // The field terms will be derived from field 'PropValue' + _: p.Attributes.map(item => createField( + item.PropName, item.PropValue)), + + // A regular index field can be defined as well: + Name: p.Name + }; + })" + }; + } + } + ``` + + + +* **The query**: + + To get all documents matching a specific attribute property use: + + + + ```csharp + IList matchingDocuments = session + .Advanced + .DocumentQuery() + // 'Width' is a dynamic-index-field that was indexed from the Attributes list + .WhereEquals("Width", 10) + .ToList(); + ``` + + + ```sql + // 'Width' is a dynamic-index-field that was indexed from the Attributes list + from index 'Attributes/ByName' where Width = 10 + ``` + + + + + + + + + + + +Dynamic fields can be indexed for [Full-text search](../../indexes/querying/searching.mdx) by setting `FieldIndexing.Search` in `CreateFieldOptions`. + +* **At indexing time**: + At indexing time, RavenDB analyzes the dynamic field value and creates the indexed terms. + The analyzer used at indexing time is resolved from the index definition. + You can configure an analyzer for a specific generated field name, or configure `AllFields` as a fallback. + See [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) below. + + * If the generated dynamic field name has an explicit analyzer configuration, that analyzer is used. + * Otherwise, if `AllFields` has an analyzer configuration, RavenDB uses it as a fallback. + * If no analyzer is configured for the field, RavenDB uses the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + +* **At querying time**: + For full-text search to return the expected results, the analyzer used at indexing time and the analyzer used for the query term should produce compatible terms. + + When a `search()` query targets a dynamic field that does **not** have an explicit analyzer configuration for its generated field name, + RavenDB uses the [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) to analyze the query term by default. + + Set the [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) + configuration key to `true` to make RavenDB use the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) as the fallback analyzer for the query term instead. + + This configuration can be set at different scopes: server-wide, per database, or per index. + The example below sets it at the index scope, in the index definition. + + For a summary of how RavenDB resolves the analyzer used for the query term, + see [Which analyzer is used for the query term](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) below. + + + +--- + +The following example indexes localized product descriptions as dynamic fields and queries one of those generated fields using full-text search: + +* **The document**: + + + ```csharp + public class Product + { + public string Id { get; set; } + + public Dictionary Descriptions { get; set; } + } + ``` + + + + ```json + // Sample document content + { + "Descriptions": { + "English": "North wind jacket", + "French": "Veste vent du nord" + } + } + ``` + + +* **The index**: + + The following index creates one dynamic field for each entry in the `Descriptions` dictionary. + For example, the key `English` creates the dynamic field `Description_English`. + + Each generated dynamic field is indexed for **full-text search**. + + + + ```csharp + public class Products_ByLocalizedDescription : AbstractIndexCreationTask + { + private const string UseSearchAnalyzerForDynamicFields = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public Products_ByLocalizedDescription() + { + Map = products => from product in products + select new + { + _ = product.Descriptions.Select(x => + CreateField( + "Description_" + x.Key, + x.Value, + new CreateFieldOptions + { + // Index each generated dynamic field for FTS + Indexing = FieldIndexing.Search, + Storage = FieldStorage.No + })) + }; + + StoreAllFields(FieldStorage.No); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + Configuration[UseSearchAnalyzerForDynamicFields] = "true"; + } + } + ``` + + + ```csharp + public class Products_ByLocalizedDescription_JS : AbstractJavaScriptIndexCreationTask + { + private const string UseSearchAnalyzerForDynamicFields = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public Products_ByLocalizedDescription_JS() + { + Maps = new HashSet + { + @"map('Products', function (product) { + return { + _: Object.keys(product.Descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + // Index each generated dynamic field for FTS + indexing: 'Search', + storage: 'No' + }); + }) + }; + })" + }; + + Fields[Constants.Documents.Indexing.Fields.AllFields] = new IndexFieldOptions + { + Storage = FieldStorage.No + }; + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + Configuration[UseSearchAnalyzerForDynamicFields] = "true"; + } + } + ``` + + + +* **Full-text search query**: + + Query the generated dynamic field by its field name. + + In this example, the query targets the generated field `Description_English`. + + Since `Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery` is set to `true`, + the searched term `"north wind"` is analyzed with the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + + + + ```csharp + List results = session.Advanced + .DocumentQuery() + .Search("Description_English", "north wind") + .ToList(); + ``` + + + + ```sql + from index "Products/ByLocalizedDescription" + where search(Description_English, "north wind") + ``` + + + +--- + + + +### Configuring analyzers for dynamic fields - - - -{`public class Products_ByProductType : AbstractIndexCreationTask +`CreateFieldOptions` does **not** let you specify a custom analyzer directly. +However, you can still configure which analyzer RavenDB will use for dynamic fields in the index definition by either: + +* Configuring an analyzer for a specific generated dynamic field name. +* Configuring `AllFields` as a fallback for fields that do not have their own explicit analyzer configuration. + +Use `AllFields` with care, since it can affect any field that does not have its own explicit analyzer configuration. + +--- + +#### Configure analyzer for a specific dynamic field + + + +```csharp +public class Products_ByLocalizedDescription : AbstractIndexCreationTask { - public Products_ByProductType() + public Products_ByLocalizedDescription() { - Map = products => from p in products + Map = products => from product in products select new { - // Call 'CreateField' to generate the dynamic-index-fields - // The field name will be the value of document field 'ProductType' - // The field terms will be derived from document field 'PricePerUnit' - _ = CreateField(p.ProductType, p.PricePerUnit) + _ = product.Descriptions.Select(x => + CreateField( + "Description_" + x.Key, + x.Value, + new CreateFieldOptions + { + Indexing = FieldIndexing.Search, + Storage = FieldStorage.No + })) }; + + StoreAllFields(FieldStorage.No); + + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + Analyze("Description_English", "StopAnalyzer"); } } -`} - +``` - - -{`public class Products_ByProductType_JS : AbstractJavaScriptIndexCreationTask + +```csharp +public class Products_ByLocalizedDescription_JS : AbstractJavaScriptIndexCreationTask { - public Products_ByProductType_JS() + public Products_ByLocalizedDescription_JS() { Maps = new HashSet { - @"map('Products', function (p) { + @"map('Products', function (product) { return { - _: createField(p.ProductType, p.PricePerUnit, - { indexing: 'Search', storage: true, termVector: null }) + _: Object.keys(product.Descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; })" }; + + Fields[Constants.Documents.Indexing.Fields.AllFields] = new IndexFieldOptions + { + Storage = FieldStorage.No + }; + + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + Fields["Description_English"] = new IndexFieldOptions + { + Analyzer = "StopAnalyzer" + }; } } -`} - - - - -* **The query**: - * To get all documents of some product type having a specific price per unit use: - - - -{`IList matchingDocuments = session - .Advanced - .DocumentQuery() - // 'Electronics' is the dynamic-index-field that was indexed from document field 'ProductType' - .WhereEquals("Electronics", 23) - .ToList(); -`} - - - - -{`// 'Electronics' is the dynamic-index-field that was indexed from document field 'ProductType' -from index 'Products/ByProductType' where Electronics = 23 -`} - +``` -## Example - list - - +#### Configure a fallback analyzer for all fields -* Index **values** from items in a list -* After index is deployed, any item added this list in the document will be dynamically indexed as well. - - - -* **The document**: - - -{`public class Product -\{ - public string Id \{ get; set; \} - public string Name \{ get; set; \} - - // For each element in this list, the VALUE of property 'PropName' will be dynamically indexed - // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields - public List Attributes \{ get; set; \} -\} - -public class Attribute -\{ - public string PropName \{ get; set; \} - public string PropValue \{ get; set; \} -\} -`} - - - - - -{`// Sample document content -\{ -Name": "SomeName", -Attributes": [ - \{ - "PropName": "Color", - "PropValue": "Blue" - \}, - \{ - "PropName": "Width", - "PropValue": "10" - \}, - \{ - "PropName": "Length", - "PropValue": "20" - \}, - ... - -\} -`} - - - -* **The index**: - The below index will create a dynamic-index-field per item in the document's `Attributes` list. - New items added to the Attributes list after index creation time will be dynamically indexed as well. - - The actual dynamic-index-field name on which you can query will be the item's PropName **value**. - E.g., 'PropName' value `Width` will be a dynamic-index-field. - - - - -{`public class Attributes_ByName : AbstractIndexCreationTask + + +```csharp +public class Products_ByLocalizedDescription : AbstractIndexCreationTask { - public Attributes_ByName() + public Products_ByLocalizedDescription() { - Map = products => from a in products + Map = products => from product in products select new { - // Define the dynamic-index-fields by calling CreateField - // A dynamic-index-field will be generated for each item in the Attributes list - - // For each item, the field name will be the value of field 'PropName' - // The field terms will be derived from field 'PropValue' - _ = a.Attributes.Select(item => CreateField(item.PropName, item.PropValue)), - - // A regular index field can be defined as well: - Name = a.Name + _ = product.Descriptions.Select(x => + CreateField( + "Description_" + x.Key, + x.Value, + new CreateFieldOptions + { + Indexing = FieldIndexing.Search, + Storage = FieldStorage.No + })) }; + + StoreAllFields(FieldStorage.No); + + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + Analyze(Constants.Documents.Indexing.Fields.AllFields, "StopAnalyzer"); } } -`} - +``` - - -{`public class Attributes_ByName_JS : AbstractJavaScriptIndexCreationTask + +```csharp +public class Products_ByLocalizedDescription_JS : AbstractJavaScriptIndexCreationTask { - public Attributes_ByName_JS() + public Products_ByLocalizedDescription_JS() { Maps = new HashSet { - @"map('Products', function (p) { + @"map('Products', function (product) { return { - _: p.Attributes.map(item => createField(item.PropName, item.PropValue, - { indexing: 'Search', storage: true, termVector: null })), - Name: p.Name + _: Object.keys(product.Descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; })" }; + + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + Fields[Constants.Documents.Indexing.Fields.AllFields] = new IndexFieldOptions + { + Storage = FieldStorage.No, + Analyzer = "StopAnalyzer" + }; } } -`} - - - - -* **The query**: - * To get all documents matching a specific attribute property use: - - - -{`IList matchingDocuments = session - .Advanced - .DocumentQuery() - // 'Width' is a dynamic-index-field that was indexed from the Attributes list - .WhereEquals("Width", 10) - .ToList(); -`} - - - - -{`// 'Width' is a dynamic-index-field that was indexed from the Attributes list -from index 'Attributes/ByName' where Width = 10 -`} - +``` + + + + +### Which analyzer is used for the query term? + +The following applies when a `search()` query targets a **dynamic field**: + +| Index configuration | [Configuration key](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) | Analyzer used for the query term | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| No analyzer is explicitly configured for the generated field name | `false` | The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) | +| No analyzer is explicitly configured for the generated field name | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| `AllFields` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `false` | The analyzer resolved by the normal field fallback path,
e.g. `WhitespaceAnalyzer` | +| `AllFields` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| The generated field name is explicitly configured,
e.g. `Analyze("Description_English", "StopAnalyzer")` | `false` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +| The generated field name is explicitly configured,
e.g. `Analyze("Description_English", "StopAnalyzer")` | `true` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +
-## CreateField syntax +
+ + #### Syntax for LINQ-index: - - -{`object CreateField(string name, object value); + +```csharp +object CreateField(string name, object value); object CreateField(string name, object value, bool stored, bool analyzed); object CreateField(string name, object value, CreateFieldOptions options); -`} - +``` #### Syntax for JavaScript-index: - - -{`createField(fieldName, fieldValue, options); // returns object -`} - + +```js +createField(fieldName, fieldValue, options); // returns object +``` | Parameters | Type | Description | |----------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| | **fieldName** | `string` | Name of the dynamic-index-field | -| **fieldValue** | `object` | Value of the dynamic-index-field
The field Terms are derived from this value. | -| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorate.Yes` | +| **fieldValue** | `object` | Value of the dynamic-index-field
The field terms are derived from this value. | +| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorage.Yes` | | **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing.Default` (default value)
`false` - `FieldIndexing.Exact`
`true` - `FieldIndexing.Search` | | **options** | `CreateFieldOptions` | Dynamic-index-field options | -| CreateFieldOptions | | | -|--------------------|--------------------|----------------------------------------------------------------------------| +| CreateFieldOptions | | | +|--------------------|--------------------|-----------------------------------------------------------------------------------| | **Storage** | `FieldStorage?` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | -| **Indexing** | `FieldIndexing?` | Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | -| **TermVector** | `FieldTermVector?` | Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | +| **Indexing** | `FieldIndexing?` | Sets the field indexing behavior.
Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | +| **TermVector** | `FieldTermVector?` | Sets the field term-vector behavior.
Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | @@ -540,11 +930,15 @@ object CreateField(string name, object value, CreateFieldOptions options); -## Indexed fields & terms view - -The generated dynamic-index-fields and their indexed terms can be viewed in the **Terms View**. -Below are sample index fields & their terms generated from the last example. +
+ + +The generated dynamic index fields and their indexed terms can be viewed in the **Terms View** in Studio. +Below are sample index fields & their terms generated from [this example](../../indexes/using-dynamic-fields.mdx#example---list). + ![Figure 1. Go to terms view](../assets/dynamic-index-fields-1.png) - + ![Figure 2. Indexed fields & terms](../assets/dynamic-index-fields-2.png) + + \ No newline at end of file diff --git a/versioned_docs/version-7.0/indexes/content/_using-dynamic-fields-java.mdx b/versioned_docs/version-7.0/indexes/content/_using-dynamic-fields-java.mdx index 17f3cbe360..d54ef6bea8 100644 --- a/versioned_docs/version-7.0/indexes/content/_using-dynamic-fields-java.mdx +++ b/versioned_docs/version-7.0/indexes/content/_using-dynamic-fields-java.mdx @@ -2,479 +2,918 @@ import Admonition from '@theme/Admonition'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; +import ContentFrame from '@site/src/components/ContentFrame'; +import Panel from '@site/src/components/Panel'; -* In RavenDB different documents can have different shapes. - Documents are schemaless - new fields can be added or removed as needed. +* In RavenDB, different documents can have different shapes. + Documents are schemaless - new fields can be added or removed as needed. + For such dynamic data, you can define indexes with **dynamic-index-fields**. + +* Dynamic-index-fields are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time, + which is very useful when working with highly dynamic systems. + +* Any value type can be indexed, string, number, date, etc. + An index definition can contain both dynamic-index-fields and regular-index-fields. + +* In this article: + * [Indexing field KEYS](../../indexes/using-dynamic-fields.mdx#indexing-field-keys) + * [Example - index every field under an object](../../indexes/using-dynamic-fields.mdx#example---index-every-field-under-an-object) + * [Example - index every field](../../indexes/using-dynamic-fields.mdx#example---index-every-field) + * [Indexing field VALUES](../../indexes/using-dynamic-fields.mdx#indexing-field-values) + * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) + * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) + * [Indexing dynamic fields for full-text search](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + * [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) + * [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + * [`CreateField` syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) + * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields-terms-view) -* For such dynamic data, you can define indexes with **dynamic-index-fields**. + -* This allows querying the index on fields that aren't yet known at index creation time, - which is very useful when working on highly dynamic systems. + -* Any value type can be indexed, string, number, date, etc. + + +### Example - index every field under an object -* An index definition can contain both dynamic-index-fields and regular-index-fields. + +The following example allows you to: + * Index any field that is under the same object from the document. + * After index is deployed, any new field added to this object will be indexed as well. + -* In this page: +* **The document**: + + + ```java + public class Product { + private String id; - * [Indexing documents fields KEYS](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-keys) - * [Example - index any field under object](../../indexes/using-dynamic-fields.mdx#example---index-any-field-under-object) - * [Example - index any field](../../indexes/using-dynamic-fields.mdx#example---index-any-field) - * [Indexing documents fields VALUES](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-values) - * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) - * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) - * [CreateField syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) - * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields--terms-view) + // The KEYS under the attributes object will be dynamically indexed + // Fields added to this object after index creation time will also get indexed + private Map attributes; -
+ // getters and setters ... + } + ``` + + + + ```json + // Sample document content + { + "attributes": { + "color": "Red", + "size": 42 + } + } + ``` + -## Indexing documents fields KEYS +* **The index**: + + The following index will index any field under the `attributes` object from the document, + a dynamic-index-field will be created for each such field. + New fields added to the object after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the attribute field **key**. + E.g., Keys `color` & `size` will become the actual dynamic-index-fields. + + + + ```java + public class Products_ByAttributeKey extends AbstractIndexCreationTask { + public Products_ByAttributeKey() { + + // Call 'CreateField' to generate dynamic-index-fields + // from the attributes object keys. + + // Using '_' is just a convention. + // Any other string can be used instead of '_'. + + // The actual field name will be 'item.Key' + // The actual field terms will be derived from 'item.Value' + map = "docs.Products.Select(p => new { " + + " _ = p.attributes.Select(item => " + + " this.CreateField(item.Key, item.Value)) " + + "})"; + } + } + ``` + + + ```java + public class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask { + public Products_ByAttributeKey_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (p) { " + + " return { " + + // The field name will be the key + // The field terms will be derived from the corresponding value + " _: Object.keys(p.attributes) " + + " .map(key => createField(key, p.attributes[key])) " + + " }; " + + "})" + )); + } + } + ``` + + -## Example - index any field under object +* **The query**: + + You can now query the generated dynamic-index fields. + The `_` property is Not queryable but used only in the index definition syntax. + + To get all documents with a specific _size_ use: + + + + ```java + List matchingDocuments = session + .advanced() + .documentQuery(Product.class, Products_ByAttributeKey.class) + // 'size' is a dynamic-index-field that was indexed from the attributes object + .whereEquals("size", 42) + .toList(); + ``` + + + ```sql + // 'size' is a dynamic-index-field that was indexed from the attributes object + from index 'Products/ByAttributeKey' where size = 42 + ``` + + + + + Dynamic index fields are queried with _DocumentQuery_ or _RQL_ because the field names (e.g. `size`, `color`) + are generated at indexing time and are not properties on the _Product_ class. + + Use the string-based `whereEquals("size", 42)` or RQL `where size = 42` instead. + - + -* Index any field that is under the some object from the document. -* After index is deployed, any new field added to the this object will be indexed as well. + + +### Example - index every field + +The following example allows you to: + * Define an index on a collection **without** needing any common structure between the indexed documents. + * After the index is deployed, any new field added to any document in the collection will be indexed as well. + + + +* Use this approach with care. +* Indexing every field can increase indexing time, memory usage, and disk space, + especially for large or frequently updated documents. * **The document**: - - -{`public class Product \{ - private String id; - - // The KEYS under the attributes object will be dynamically indexed - // Fields added to this object after index creation time will also get indexed - private Dictionary attributes; - - // get + set implementation ... -\} -`} - - - - -{`// Sample document content -\{ - "attributes": \{ - "color": "Red", - "size": 42 - \} -\} -`} - - + + + ```java + public class Product { + private String id; + + // All KEYS in the document will be dynamically indexed + // Fields added to the document after index creation time will also get indexed + private String firstName; + private String lastName; + private String title; + // ... + + // getters and setters ... + } + ``` + + + + ```json + // Sample document content + { + "firstName": "John", + "lastName": "Doe", + "title": "Engineer" + // ... + } + ``` + * **The index**: - The following index will index any field under the `attributes` object from the document, - a dynamic-index-field will be created for each such field. - New fields added to the object after index creation time will be dynamically indexed as well. + + The following index will index any field from the document, + a dynamic-index-field will be created for each field. + New fields added to the document after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the field **key**. + E.g., Keys `firstName` & `lastName` will become the actual dynamic-index-fields. + + The example uses an `AbstractJavaScriptIndexCreationTask` because it enumerates all top-level document fields at indexing time with `Object.keys(...)`. + LINQ indexes typically create dynamic fields from a known enumerable structure, such as a map or collection. + + + + ```java + public class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask { + public Products_ByAnyField_JS() { + + // This will index EVERY FIELD under the top level of the document + setMaps(Sets.newHashSet( + "map('Products', function (p) { " + + " return { " + + " _: Object.keys(p).map(key => createField(key, p[key])) " + + " }; " + + "})" + )); + } + } + ``` + + - The actual dynamic-index-field name on which you can query will be the attribute field **key**. - e.g. Keys `color` & `size` will become the actual dynamic-index-fields. +* **The query**: + + To get all documents with a specific _lastName_ use: + + + + ```java + List matchingDocuments = session + .advanced() + .documentQuery(Product.class, Products_ByAnyField_JS.class) + // 'lastName' is a dynamic-index-field that was indexed from the document + .whereEquals("lastName", "Doe") + .toList(); + ``` + + + ```sql + // 'lastName' is a dynamic-index-field that was indexed from the document + from index 'Products/ByAnyField/JS' where lastName = "Doe" + ``` + + + + + + - - - -{`public class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask { - public Products_ByAttributeKey_JS() { - // Call 'createField' to generate dynamic-index-fields from the attributes object keys - // Using '_' is just a convention. Any other string can be used instead of '_' + - // The actual field name will be the key - // The actual field terms will be derived from p.attributes[key] - setMaps(Sets.newHashSet( - "map('Products', function (p) { " + - " return { " + - " _: Object.keys(p.attributes).map(key => createField(key, p.attributes[key], " + - " { indexing: 'Search', storage: false, termVector: null })) " + - " }; " + - "}) " - )); - } -} -`} - - - + + +### Example - basic -* **The query**: - * You can now query the generated dynamic-index fields. - Property `_` is Not queryable, it is only used in the index definition syntax. - * To get all documents with some 'size' use: - - - -{`List matchingDocuments = session - .query(Product.class, Products_ByAttributeKey_JS.class) - .whereEquals("size", 42) - .toList(); -`} - - - - -{`// 'size' is a dynamic-index-field that was indexed from the Attributes object -from index 'Products/ByAttributeKey/JS' where size = 42 -`} - - - + +The following example shows: + * Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. + * Documents can then be queried based on those indexed values. + * For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---list) below. + -## Example - index any field +* **The document**: + + + ```java + public class Product { + private String id; - + // The VALUE of productType will be dynamically indexed + private String productType; + private int pricePerUnit; -* Define an index on a collection **without** needing any common structure between the indexed documents. -* After index is deployed, any new field added to the document will be indexed as well. + // getters and setters ... + } + ``` + + + + ```json + // Sample document content + { + "productType": "Electronics", + "pricePerUnit": 23 + } + ``` + - +* **The index**: + + The following index will index the **value** of document field 'productType'. + + This value will be the dynamic-index-field name on which you can query. + E.g., Field value `Electronics` will be the dynamic-index-field. + + + + ```java + public class Products_ByProductType extends AbstractIndexCreationTask { + public Products_ByProductType() { + + // Call 'CreateField' to generate the dynamic-index-field. + // The field name will be the value of document field 'productType'. + // The field terms will be derived from document field 'pricePerUnit'. + map = "docs.Products.Select(p => new { " + + " _ = this.CreateField(p.productType, p.pricePerUnit) " + + "})"; + } + } + ``` + + + ```java + public class Products_ByProductType_JS extends AbstractJavaScriptIndexCreationTask { + public Products_ByProductType_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (p) { " + + " return { " + + // The field name will be the value of document field 'productType' + // The field terms will be derived from document field 'pricePerUnit' + " _: createField(p.productType, p.pricePerUnit) " + + " }; " + + "})" + )); + } + } + ``` + + - -Consider if that is a true necessity, as indexing every single field can end up costing time and disk space. +* **The query**: + + To get all documents of some product type having a specific price per unit use: + + + + ```java + List matchingDocuments = session + .advanced() + .documentQuery(Product.class, Products_ByProductType.class) + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + .whereEquals("Electronics", 23) + .toList(); + ``` + + + ```sql + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + from index 'Products/ByProductType' where Electronics = 23 + ``` + + + + + + + +### Example - list + + +The following example allows you to: + * Index **values** from items in a list + * After index is deployed, any item added to this list in the document will be dynamically indexed as well. * **The document**: - - -{`public class Product \{ - private String id; - - // All KEYS in the document will be dynamically indexed - // Fields added to the document after index creation time will also get indexed - private String firstName; - private String lastName; - private String title; - // ... - - // get + set implementation ... -\} -`} - - - - - -{`// Sample document content -\{ - "firstName": "John", - "lastName": "Doe", - "title": "Engineer", - // ... -\} -`} - - + + + ```java + public class Product { + private String id; + private String name; + + // For each element in this list, + // the VALUE of property 'propName' will be dynamically indexed. + // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields. + private List attributes; + + // getters and setters ... + } -* **The index**: - The following index will index any field from the document, - a dynamic-index-field will be created for each field. - New fields added to the document after index creation time will be dynamically indexed as well. + public class Attribute { + private String propName; + private String propValue; - The actual dynamic-index-field name on which you can query will be the field **key**. - E.g., Keys `firstName` & `lastName` will become the actual dynamic-index-fields. + // getters and setters ... + } + ``` + + + + ```json + // Sample document content + { + "name": "SomeName", + "attributes": [ + { + "propName": "Color", + "propValue": "Blue" + }, + { + "propName": "Width", + "propValue": "10" + }, + { + "propName": "Length", + "propValue": "20" + } + // ... + ] + } + ``` + - - - -{`public class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask { - public Products_ByAnyField_JS() { +* **The index**: - // This will index EVERY FIELD under the top level of the document - setMaps(Sets.newHashSet( - "map('Products', function (p) { " + - " return { " + - " _: Object.keys(p).map(key => createField(key, p[key], " + - " { indexing: 'Search', storage: true, termVector: null })) " + - " }; " + - "}) " - )); + The following index will create a dynamic-index-field per item in the document's `attributes` list. + New items added to the attributes list after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the item's propName **value**. + E.g., 'propName' value `Width` will be a dynamic-index-field. + + + + ```java + public class Attributes_ByName extends AbstractIndexCreationTask { + public Attributes_ByName() { + + // Define the dynamic-index-fields by calling 'CreateField'. + // A dynamic-index-field will be generated for each item in 'attributes'. + + // For each item, the field name will be the value of field 'propName' + // The field terms will be derived from field 'propValue'. + // A regular index field (Name) is defined as well. + map = "docs.Products.Select(p => new { " + + " _ = p.attributes.Select(item => " + + " this.CreateField(item.propName, item.propValue)), " + + " Name = p.name " + + "})"; + } } -} -`} - - - + ``` + + + ```java + public class Attributes_ByName_JS extends AbstractJavaScriptIndexCreationTask { + public Attributes_ByName_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (p) { " + + " return { " + + // For each item, + // the field name will be the value of field 'propName'. + // The field terms will be derived from field 'propValue'. + " _: p.attributes.map(item => " + + " createField(item.propName, item.propValue)), " + + // A regular index field can be defined as well: + " Name: p.name " + + " }; " + + "})" + )); + } + } + ``` + + * **The query**: - * To get all documents with some 'lastName' use: - - - -{`List matchingDocuments = session - .query(Product.class, Products_ByAnyField_JS.class) - .whereEquals("lastName", "Doe") - .toList(); -`} - - - - -{`// 'lastName' is a dynamic-index-field that was indexed from the document -from index 'Products/ByAnyField/JS' where lastName = "Doe" -`} - - - + + To get all documents matching a specific attribute property use: + + + + ```java + List matchingDocuments = session + .advanced() + .documentQuery(Product.class, Attributes_ByName.class) + // 'Width' is a dynamic-index-field that was indexed from the attributes list + .whereEquals("Width", 10) + .toList(); + ``` + + + ```sql + // 'Width' is a dynamic-index-field that was indexed from the attributes list + from index 'Attributes/ByName' where Width = 10 + ``` + + + + + + + + + + +Dynamic fields can be indexed for [Full-text search](../../indexes/querying/searching.mdx) by setting `indexing: 'Search'` in the `createField` options object (`AbstractJavaScriptIndexCreationTask`) +or `FieldIndexing.Search` in the `CreateFieldOptions` (`AbstractIndexCreationTask`). +* **At indexing time**: + At indexing time, RavenDB analyzes the dynamic field value and creates the indexed terms. + The analyzer used at indexing time is resolved from the index definition. + You can configure an analyzer for a specific generated field name, or configure `ALL_FIELDS` as a fallback. + See [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) below. -## Indexing documents fields VALUES + * If the generated dynamic field name has an explicit analyzer configuration, that analyzer is used. + * Otherwise, if `ALL_FIELDS` has an analyzer configuration, RavenDB uses it as a fallback. + * If no analyzer is configured for the field, RavenDB uses the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). -## Example - basic +* **At querying time**: + For full-text search to return the expected results, the analyzer used at indexing time and the analyzer used for the query term should produce compatible terms. - + When a `search()` query targets a dynamic field that does **not** have an explicit analyzer configuration for its generated field name, + RavenDB uses the [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) to analyze the query term by default. -* Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. -* Documents can then be queried based on those indexed values. -* For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---index-a-list-of-properties) below. + Set the [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) + configuration key to `true` to make RavenDB use the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) as the fallback analyzer for the query term instead. + + This configuration can be set at different scopes: server-wide, per database, or per index. + The example below sets it at the index scope, in the index definition. + + For a summary of how RavenDB resolves the analyzer used for the query term, + see [Which analyzer is used for the query term](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) below. + +--- + +The following example indexes localized product descriptions as dynamic fields and queries one of those generated fields using full-text search: + +* **The document**: -* **The document**: - - -{`public class Product \{ - private String id; - - // The VALUE of productType will be dynamically indexed - private String productType; - private int pricePerUnit; - - // get + set implementation ... -\} -`} - - + + ```java + public class Product { + private String id; - - -{`// Sample document content -\{ - "productType": "Electronics", - "pricePerUnit": 23 -\} -`} - - + private Map descriptions; -* **The index**: - The following index will index the **value** of document field 'productType'. + // getters and setters ... + } + ``` + + + + ```json + // Sample document content + { + "descriptions": { + "English": "North wind jacket", + "French": "Veste vent du nord" + } + } + ``` + + +* **The index**: + + The following index creates one dynamic field for each entry in the `descriptions` map. + For example, the key `English` creates the dynamic field `Description_English`. + + Each generated dynamic field is indexed for **full-text search**. + + + + ```java + public class Products_ByLocalizedDescription extends AbstractIndexCreationTask { + + private static final String USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public Products_ByLocalizedDescription() { + map = "docs.Products.Select(product => new { " + + " _ = product.descriptions.Select(x => " + + " this.CreateField( " + + " \"Description_\" + x.Key, " + + " x.Value, " + + " new CreateFieldOptions { " + + // Index each generated dynamic field for FTS + " Indexing = FieldIndexing.Search, " + + " Storage = FieldStorage.No " + + " })) " + + "})"; + + storeAllFields(FieldStorage.NO); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + getConfiguration().put(USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS, "true"); + } + } + ``` + + + ```java + public class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + + private static final String USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public Products_ByLocalizedDescription_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (product) { " + + " return { " + + " _: Object.keys(product.descriptions) " + + " .map(function (key) { " + + " return createField( " + + " 'Description_' + key, " + + " product.descriptions[key], " + + " { " + + // Index each generated dynamic field for FTS + " indexing: 'Search', " + + " storage: false " + + " }); " + + " }) " + + " }; " + + "})" + )); + + Map fields = new HashMap<>(); + + IndexFieldOptions allFieldsOptions = new IndexFieldOptions(); + allFieldsOptions.setStorage(FieldStorage.NO); + + fields.put(Constants.Documents.Indexing.Fields.ALL_FIELDS, allFieldsOptions); + setFields(fields); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + getConfiguration().put(USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS, "true"); + } + } + ``` + + + +* **Full-text search query**: + + Query the generated dynamic field by its field name. + + In this example, the query targets the generated field `Description_English`. + + Since `Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery` is set to `true`, + the searched term `"north wind"` is analyzed with the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + + + + ```java + List results = session.advanced() + .documentQuery(Product.class, Products_ByLocalizedDescription.class) + .search("Description_English", "north wind") + .toList(); + ``` + + + + ```sql + from index "Products/ByLocalizedDescription" + where search(Description_English, "north wind") + ``` + + - This value will be the dynamic-index-field name on which you can query. - e.g. Field value `Electronics` will be the dynamic-index-field. +--- - - - -{`public class Products_ByProductType extends AbstractIndexCreationTask { - public Products_ByProductType() { + + +### Configuring analyzers for dynamic fields + +The `CreateFieldOptions` (and the JavaScript `createField` options) do **not** let you specify a custom analyzer directly. +However, you can still configure which analyzer RavenDB will use for dynamic fields in the index definition by either: - // The field name will be the value of document field 'productType' - // The field terms will be derived from document field 'pricePerUnit' - map = "docs.Products.Select(p => new { " + - " _ = this.CreateField(p.productType, p.pricePerUnit) " + +* Configuring an analyzer for a specific generated dynamic field name. +* Configuring `ALL_FIELDS` as a fallback for fields that do not have their own explicit analyzer configuration. + +Use `ALL_FIELDS` with care, since it can affect any field that does not have its own explicit analyzer configuration. + +--- + +#### Configure analyzer for a specific dynamic field + + + +```java +public class Products_ByLocalizedDescription extends AbstractIndexCreationTask { + public Products_ByLocalizedDescription() { + map = "docs.Products.Select(product => new { " + + " _ = product.descriptions.Select(x => " + + " this.CreateField( " + + " \"Description_\" + x.Key, " + + " x.Value, " + + " new CreateFieldOptions { " + + " Indexing = FieldIndexing.Search, " + + " Storage = FieldStorage.No " + + " })) " + "})"; + + storeAllFields(FieldStorage.NO); + + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + analyze("Description_English", "StopAnalyzer"); } } -`} - +``` - - -* **The query**: - * To get all documents of some product type having a specific price per unit use: - - - -{`List matchingDocuments = session - .query(Product.class, Products_ByProductType.class) - .whereEquals("Electronics", 23) - .toList(); -`} - - - - -{`// 'Electronics' is the dynamic-index-field that was indexed from document field 'productType' -from index 'Products/ByProductType' where Electronics = 23 -`} - - - - -## Example - list + +```java +public class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + public Products_ByLocalizedDescription_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (product) { " + + " return { " + + " _: Object.keys(product.descriptions) " + + " .map(function (key) { " + + " return createField( " + + " 'Description_' + key, " + + " product.descriptions[key], " + + " { " + + " indexing: 'Search', " + + " storage: false " + + " }); " + + " }) " + + " }; " + + "})" + )); - + Map fields = new HashMap<>(); -* Index **values** from items in a list -* After index is deployed, any item added this list in the document will be dynamically indexed as well. + IndexFieldOptions allFieldsOptions = new IndexFieldOptions(); + allFieldsOptions.setStorage(FieldStorage.NO); + fields.put(Constants.Documents.Indexing.Fields.ALL_FIELDS, allFieldsOptions); - + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + IndexFieldOptions englishOptions = new IndexFieldOptions(); + englishOptions.setAnalyzer("StopAnalyzer"); + fields.put("Description_English", englishOptions); -* **The document**: - - -{`public class Product \{ - private String id; - private String name; - - // For each element in this list, the VALUE of property 'propName' will be dynamically indexed - // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields - private List attributes; - - // get + set implementation ... -\} - -public class Attribute \{ - private String propName; - private String propValue; - - // get + set implementation ... -\} -`} - + setFields(fields); + } +} +``` + - - -{`// Sample document content -\{ -name": "SomeName", -attributes": [ - \{ - "propName": "Color", - "propValue": "Blue" - \}, - \{ - "propName": "Width", - "propValue": "10" - \}, - \{ - "propName": "Length", - "propValue": "20" - \}, - ... - -\} -`} - - +#### Configure a fallback analyzer for all fields + + + +```java +public class Products_ByLocalizedDescription extends AbstractIndexCreationTask { + public Products_ByLocalizedDescription() { + map = "docs.Products.Select(product => new { " + + " _ = product.descriptions.Select(x => " + + " this.CreateField( " + + " \"Description_\" + x.Key, " + + " x.Value, " + + " new CreateFieldOptions { " + + " Indexing = FieldIndexing.Search, " + + " Storage = FieldStorage.No " + + " })) " + + "})"; -* **The index**: - The following index will create a dynamic-index-field per item in the document's `attributes` list. - New items added to the attributes list after index creation time will be dynamically indexed as well. - - The actual dynamic-index-field name on which you can query will be the item's propName **value**. - E.g., 'propName' value `Width` will be a dynamic-index-field. - - - - -{`public class Attributes_ByName extends AbstractIndexCreationTask { - public Attributes_ByName() { - - // For each attribute item, the field name will be the value of field 'propName' - // The field terms will be derived from field 'propValue' - // A regular-index-field (Name) is defined as well - map = - "docs.Products.Select(p => new { " + - " _ = p.attributes.Select(item => this.CreateField(item.propName, item.propValue)), " + - " Name = p.name " + - "})"; + storeAllFields(FieldStorage.NO); + + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + analyze(Constants.Documents.Indexing.Fields.ALL_FIELDS, "StopAnalyzer"); } } -`} - +``` - + +```java +public class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + public Products_ByLocalizedDescription_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (product) { " + + " return { " + + " _: Object.keys(product.descriptions) " + + " .map(function (key) { " + + " return createField( " + + " 'Description_' + key, " + + " product.descriptions[key], " + + " { " + + " indexing: 'Search', " + + " storage: false " + + " }); " + + " }) " + + " }; " + + "})" + )); -* **The query**: - * To get all documents matching a specific attribute property use: - - - -{`List matchingDocuments = session - .query(Product.class, Attributes_ByName.class) - .whereEquals("Width", 10) - .toList(); -`} - - - - -{`// 'Width' is a dynamic-index-field that was indexed from the attributes list -from index 'Attributes/ByName' where Width = 10 -`} - + Map fields = new HashMap<>(); + + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + IndexFieldOptions allFieldsOptions = new IndexFieldOptions(); + allFieldsOptions.setStorage(FieldStorage.NO); + allFieldsOptions.setAnalyzer("StopAnalyzer"); + fields.put(Constants.Documents.Indexing.Fields.ALL_FIELDS, allFieldsOptions); + + setFields(fields); + } +} +``` + + + + +### Which analyzer is used for the query term? +The following applies when a `search()` query targets a **dynamic field**: -## CreateField syntax +| Index configuration | [Configuration key](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) | Analyzer used for the query term | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| No analyzer is explicitly configured for the generated field name | `false` | The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) | +| No analyzer is explicitly configured for the generated field name | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `false` | The analyzer resolved by the normal field fallback path,
e.g. `WhitespaceAnalyzer` | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| The generated field name is explicitly configured,
e.g. `analyze("Description_English", "StopAnalyzer")` | `false` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +| The generated field name is explicitly configured,
e.g. `analyze("Description_English", "StopAnalyzer")` | `true` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | -#### Syntax for LINQ-index: +
- - -{`object CreateField(string name, object value); +
+ + + +#### Syntax for `AbstractIndexCreationTask` + + +```csharp +object CreateField(string name, object value); object CreateField(string name, object value, bool stored, bool analyzed); object CreateField(string name, object value, CreateFieldOptions options); -`} - +``` -#### Syntax for JavaScript-index: +#### Syntax for `AbstractJavaScriptIndexCreationTask` - - -{`createField(fieldName, fieldValue, options); // returns object -`} - + +```js +createField(fieldName, fieldValue, options); // returns object +``` -| Parameters | Type | Description | -|----------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **fieldName** | `string` | Name of the dynamic-index-field | -| **fieldValue** | `object` | Value of the dynamic-index-field
The field Terms are derived from this value. | -| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorate.Yes` | -| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing.Default` (default value)
`false` - `FieldIndexing.Exact`
`true` - `FieldIndexing.Search` | +| Parameters | Type | Description | +|----------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **fieldName** | `string` | Name of the dynamic-index-field | +| **fieldValue** | `object` | Value of the dynamic-index-field
The field terms are derived from this value. | +| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorage.Yes` | +| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing.Default` (default value)
`false` - `FieldIndexing.Exact`
`true` - `FieldIndexing.Search` | | **options** | `CreateFieldOptions` | Dynamic-index-field options | -| CreateFieldOptions | | | -|--------------------|--------------------|----------------------------------------------------------------------------| +| CreateFieldOptions | | | +|--------------------|--------------------|-----------------------------------------------------------------------------------| | **Storage** | `FieldStorage?` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | -| **Indexing** | `FieldIndexing?` | Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | -| **TermVector** | `FieldTermVector?` | Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | +| **Indexing** | `FieldIndexing?` | Sets the field indexing behavior.
Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | +| **TermVector** | `FieldTermVector?` | Sets the field term-vector behavior.
Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | +* The `CreateField` syntax (and the `CreateFieldOptions` shape) above describes what is available inside the server-side `map` string of an `AbstractIndexCreationTask`, + since the Java client sends the `map` source to the server for compilation. + The JavaScript `createField` function is the equivalent for `AbstractJavaScriptIndexCreationTask` map sources. + * All above examples have used the character `_` in the dynamic-index-field definition. However, using `_` is just a convention. Any other string can be used instead. * This property is Not queryable, it is only used in the index definition syntax. - The actual dynamic-index-fields that are generated are defined by the `CreateField` method. + The actual dynamic-index-fields that are generated are defined by the `CreateField` method (or the `createField` JS function). +
+ + - -## Indexed fields & terms view - -The generated dynamic-index-fields and their indexed terms can be viewed in the **Terms View**. -Below are sample index fields & their terms generated from the last example. - +The generated dynamic index fields and their indexed terms can be viewed in the **Terms View** in Studio. +Below are sample index fields & their terms generated from [this example](../../indexes/using-dynamic-fields.mdx#example---list). + ![Figure 1. Go to terms view](../assets/dynamic-index-fields-1.png) - + ![Figure 2. Indexed fields & terms](../assets/dynamic-index-fields-2.png) - - - - + + diff --git a/versioned_docs/version-7.0/indexes/content/_using-dynamic-fields-nodejs.mdx b/versioned_docs/version-7.0/indexes/content/_using-dynamic-fields-nodejs.mdx index b5df2df4a7..c14184c2b5 100644 --- a/versioned_docs/version-7.0/indexes/content/_using-dynamic-fields-nodejs.mdx +++ b/versioned_docs/version-7.0/indexes/content/_using-dynamic-fields-nodejs.mdx @@ -2,533 +2,710 @@ import Admonition from '@theme/Admonition'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; +import ContentFrame from '@site/src/components/ContentFrame'; +import Panel from '@site/src/components/Panel'; -* In RavenDB different documents can have different shapes. - Documents are schemaless - new fields can be added or removed as needed. - -* For such dynamic data, you can define indexes with **dynamic-index-fields**. - -* This allows querying the index on fields that aren't yet known at index creation time, - which is very useful when working on highly dynamic systems. - -* Any value type can be indexed, string, number, date, etc. - -* An index definition can contain both dynamic-index-fields and regular-index-fields. - -* In this page: - - * [Indexing documents fields KEYS](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-keys) - * [Example - index any field under object](../../indexes/using-dynamic-fields.mdx#example---index-any-field-under-object) - * [Example - index any field](../../indexes/using-dynamic-fields.mdx#example---index-any-field) - * [Indexing documents fields VALUES](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-values) - * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) - * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) - * [CreateField syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) - * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields--terms-view) +* In RavenDB, different documents can have different shapes. + Documents are schemaless - new fields can be added or removed as needed. + For such dynamic data, you can define indexes with **dynamic-index-fields**. + +* Dynamic-index-fields are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time, + which is very useful when working with highly dynamic systems. + +* Any value type can be indexed, string, number, date, etc. + An index definition can contain both dynamic-index-fields and regular-index-fields. + +* In this article: + * [Indexing field KEYS](../../indexes/using-dynamic-fields.mdx#indexing-field-keys) + * [Example - index every field under an object](../../indexes/using-dynamic-fields.mdx#example---index-every-field-under-an-object) + * [Example - index every field](../../indexes/using-dynamic-fields.mdx#example---index-every-field) + * [Indexing field VALUES](../../indexes/using-dynamic-fields.mdx#indexing-field-values) + * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) + * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) + * [Indexing dynamic fields for full-text search](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + * [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) + * [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + * [`createField` syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) + * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields-terms-view) -## Indexing documents fields KEYS + - -#### Example - index any field under object -The following allows you to: - -* Index any field that is under the some object from the document. -* After index is deployed, any new field added to the this object will be indexed as well. -**The document**: - - -{`class Product \{ - constructor(id, attributes) \{ - this.id = id; - - // The KEYS under the attributes object will be dynamically indexed - // Fields added to this object after index creation time will also get indexed - this.attributes = attributes; - \} -\} -`} - - + + +### Example - index every field under an object - - -{`// Sample document content -\{ - "attributes": \{ - "color": "Red", - "size": 42 - \} -\} -`} - - + +The following example allows you to: + * Index any field that is under the same object from the document. + * After index is deployed, any new field added to this object will be indexed as well. + -**The index**: +* **The document**: + + + ```js + class Product { + constructor(id, attributes) { + this.id = id; + + // The KEYS under the attributes object will be dynamically indexed + // Fields added to this object after index creation time will also get indexed + this.attributes = attributes; + } + } + ``` + + + + ```json + // Sample document content + { + "attributes": { + "color": "Red", + "size": 42 + } + } + ``` + -* The following index will index any field under the `attributes` object from the document, +* **The index**: + + The following index will index any field under the `attributes` object from the document, a dynamic-index-field will be created for each such field. - New fields added to the object after index creation time will be dynamically indexed as well. - -* The actual dynamic-index-field name on which you can query will be the attribute field **key**. - e.g. Keys `color` & `size` will become the actual dynamic-index-fields. - - - - -{`class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask { - constructor() { - super(); - - const { createField } = this.mapUtils(); - - this.map("Products", p => { - return { - // Call 'createField' to generate dynamic-index-fields from the attributes object keys - // Using '_' is just a convention. Any other string can be used instead of '_' - - // The actual field name will be the key - // The actual field terms will be derived from p.attributes[key] - _: Object.keys(p.attributes).map(key => createField(key, p.attributes[key], { - indexing: "Search", - storage: false, - termVector: null - })) - }; - }); + New fields added to the object after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the attribute field **key**. + E.g., Keys `color` & `size` will become the actual dynamic-index-fields. + + + ```js + class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { + super(); + + const { createField } = this.mapUtils(); + + this.map("Products", p => { + return { + // Call 'createField' to generate dynamic-index-fields + // from the attributes object keys. + + // Using '_' is just a convention. + // Any other string can be used instead of '_' + + // The actual field name will be the key + // The actual field terms will be derived from p.attributes[key] + _: Object.keys(p.attributes).map(key => createField(key, p.attributes[key])) + }; + }); + } } -} -`} - - - - -**The query**: - -* You can now query the generated dynamic-index fields. - Property `_` is Not queryable, it is only used in the index definition syntax. - -* To get all documents with some 'size' use: - - - - -{`const matchingDocuments = session.query({indexName: 'Products_ByAttributeKey'}) - // 'size' is a dynamic-index-field that was indexed from the attributes object - .whereEquals('size', 42) - .all(); -`} - - - - -{`// 'size' is a dynamic-index-field that was indexed from the attributes object -from index 'Products/ByAttributeKey' where size = 42 -`} - - - - - + ``` + + +* **The query**: + + You can now query the generated dynamic-index fields. + The `_` property is Not queryable but used only in the index definition syntax. + + To get all documents with a specific _size_ use: + + + + ```js + const matchingDocuments = await session + .query({ indexName: "Products/ByAttributeKey/JS" }) + // 'size' is a dynamic-index-field that was indexed from the attributes object + .whereEquals("size", 42) + .all(); + ``` + + + ```sql + // 'size' is a dynamic-index-field that was indexed from the attributes object + from index 'Products/ByAttributeKey/JS' where size = 42 + ``` + + + + + + + +### Example - index every field -#### Example - index any field -The following allows you to: - -* Define an index on a collection **without** needing any common structure between the indexed documents. -* After index is deployed, any new field added to the document will be indexed as well. - +The following example allows you to: + * Define an index on a collection **without** needing any common structure between the indexed documents. + * After the index is deployed, any new field added to any document in the collection will be indexed as well. + + - -Consider if that is a true necessity, as indexing every single field can end up costing time and disk space. - +* Use this approach with care. +* Indexing every field can increase indexing time, memory usage, and disk space, + especially for large or frequently updated documents. -**The document**: - - -{`class Product \{ - constructor(id, firstName, lastName, title) \{ - this.id = id; - - // All KEYS in the document will be dynamically indexed - // Fields added to the document after index creation time will also get indexed - this.firstName = firstName; - this.lastName = lastName; - this.title = title; - // ... - \} -\} -`} - - - - - -{`// Sample document content -\{ - "firstName": "John", - "lastName": "Doe", - "title": "Engineer", - // ... -\} -`} - - -**The index**: +* **The document**: + + + ```js + class Product { + constructor(id, firstName, lastName, title) { + this.id = id; + + // All KEYS in the document will be dynamically indexed + // Fields added to the document after index creation time will also get indexed + this.firstName = firstName; + this.lastName = lastName; + this.title = title; + // ... + } + } + ``` + + + + ```json + // Sample document content + { + "firstName": "John", + "lastName": "Doe", + "title": "Engineer", + // ... + } + ``` + -* The following index will index any field from the document, +* **The index**: + + The following index will index any field from the document, a dynamic-index-field will be created for each field. - New fields added to the document after index creation time will be dynamically indexed as well. - -* The actual dynamic-index-field name on which you can query will be the field **key**. - e.g. Keys `firstName` & `lastName` will become the actual dynamic-index-fields. + New fields added to the document after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the field **key**. + E.g., Keys `firstName` & `lastName` will become the actual dynamic-index-fields. + + + ```js + class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { + super(); + + const { createField } = this.mapUtils(); + + // This will index EVERY FIELD under the top level of the document + this.map("Products", p => { + return { + _: Object.keys(p).map(key => createField(key, p[key])) + }; + }); + } + } + ``` + + +* **The query**: + + To get all documents with a specific _lastName_ use: + + + + ```js + const matchingDocuments = await session + .query({ indexName: "Products/ByAnyField/JS" }) + // 'lastName' is a dynamic-index-field that was indexed from the document + .whereEquals("lastName", "Doe") + .all(); + ``` + + + ```sql + // 'lastName' is a dynamic-index-field that was indexed from the document + from index 'Products/ByAnyField/JS' where lastName = "Doe" + ``` + + + + + + + + + + + +### Example - basic + + +The following example shows: + * Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. + * Documents can then be queried based on those indexed values. + * For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---list) below. + - - - -{`class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask { - constructor () { - super(); +* **The document**: + + + ```js + class Product { + constructor(id, productType, pricePerUnit) { + this.id = id; + + // The VALUE of productType will be dynamically indexed + this.productType = productType; + this.pricePerUnit = pricePerUnit; + } + } + ``` + + + + ```json + // Sample document content + { + "productType": "Electronics", + "pricePerUnit": 23 + } + ``` + + +* **The index**: + + The following index will index the **value** of document field 'productType'. + + This value will be the dynamic-index-field name on which you can query. + E.g., Field value `Electronics` will be the dynamic-index-field. + + + ```js + class Products_ByProductType_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { + super(); + + const { createField } = this.mapUtils(); + + this.map("Products", p => { + return { + // The field name will be the value of document field 'productType' + // The field terms will be derived from document field 'pricePerUnit' + _: createField(p.productType, p.pricePerUnit) + }; + }); + } + } + ``` + + +* **The query**: + + To get all documents of some product type having a specific price per unit use: + + + + ```js + const matchingDocuments = await session + .query({ indexName: "Products/ByProductType/JS" }) + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + .whereEquals("Electronics", 23) + .all(); + ``` + + + ```sql + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + from index 'Products/ByProductType/JS' where Electronics = 23 + ``` + + + + + + + +### Example - list - const { createField } = this.mapUtils(); + +The following example allows you to: + * Index **values** from items in a list + * After index is deployed, any item added to this list in the document will be dynamically indexed as well. + - this.map("Products", p => { - return { - // This will index EVERY FIELD under the top level of the document - _: Object.keys(p).map(key => createField(key, p[key], { - indexing: "Search", - storage: true, - termVector: null - })) - }; - }); +* **The document**: + + + ```js + class Product { + constructor(id, name, attributes) { + this.id = id; + this.name = name; + + // For each element in this list, + // the VALUE of property 'propName' will be dynamically indexed. + // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields. + this.attributes = attributes; + } } -} -`} - - - -**The query**: + class Attribute { + constructor(propName, propValue) { + this.propName = propName; + this.propValue = propValue; + } + } + ``` + + + + ```json + // Sample document content + { + "name": "SomeName", + "attributes": [ + { + "propName": "Color", + "propValue": "Blue" + }, + { + "propName": "Width", + "propValue": "10" + }, + { + "propName": "Length", + "propValue": "20" + }, + ... + ] + } + ``` + + +* **The index**: + + The following index will create a dynamic-index-field per item in the document's `attributes` list. + New items added to the attributes list after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the item's propName **value**. + E.g., 'propName' value `Width` will be a dynamic-index-field. + + + ```js + class Attributes_ByName_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { + super(); + + const { createField } = this.mapUtils(); + + this.map("Products", p => { + return { + // Define the dynamic-index-fields by calling 'createField' + // A dynamic-index-field will be generated for each item in 'attributes' + + // For each item, the field name will be the value of field 'propName' + // The field terms will be derived from field 'propValue' + _: p.attributes.map(item => + createField(item.propName, item.propValue)), + + // A regular index field can be defined as well: + name: p.name + }; + }); + } + } + ``` + + +* **The query**: + + To get all documents matching a specific attribute property use: + + + + ```js + const matchingDocuments = await session + .query({ indexName: "Attributes/ByName/JS" }) + // 'Width' is a dynamic-index-field that was indexed from the attributes list + .whereEquals("Width", 10) + .all(); + ``` + + + ```sql + // 'Width' is a dynamic-index-field that was indexed from the attributes list + from index 'Attributes/ByName/JS' where Width = 10 + ``` + + + + + + + + + + + +Dynamic fields can be indexed for [Full-text search](../../indexes/querying/searching.mdx) by setting `indexing: "Search"` in the `createField` options. + +* **At indexing time**: + At indexing time, RavenDB analyzes the dynamic field value and creates the indexed terms. + The analyzer used at indexing time is resolved from the index definition. + You can configure an analyzer for a specific generated field name, or configure `ALL_FIELDS` as a fallback. + See [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) below. + + * If the generated dynamic field name has an explicit analyzer configuration, that analyzer is used. + * Otherwise, if `ALL_FIELDS` has an analyzer configuration, RavenDB uses it as a fallback. + * If no analyzer is configured for the field, RavenDB uses the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + +* **At querying time**: + For full-text search to return the expected results, the analyzer used at indexing time and the analyzer used for the query term should produce compatible terms. + + When a `search()` query targets a dynamic field that does **not** have an explicit analyzer configuration for its generated field name, + RavenDB uses the [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) to analyze the query term by default. + + Set the [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) + configuration key to `true` to make RavenDB use the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) as the fallback analyzer for the query term instead. + + This configuration can be set at different scopes: server-wide, per database, or per index. + The example below sets it at the index scope, in the index definition. + + For a summary of how RavenDB resolves the analyzer used for the query term, + see [Which analyzer is used for the query term](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) below. -* To get all documents with some 'lastName' use: + + +--- + +The following example indexes localized product descriptions as dynamic fields and queries one of those generated fields using full-text search: + +* **The document**: + + + ```js + class Product { + constructor(id, descriptions) { + this.id = id; + this.descriptions = descriptions; + } + } + ``` + + + + ```json + // Sample document content + { + "descriptions": { + "English": "North wind jacket", + "French": "Veste vent du nord" + } + } + ``` + + +* **The index**: + + The following index creates one dynamic field for each entry in the `descriptions` dictionary. + For example, the key `English` creates the dynamic field `Description_English`. + + Each generated dynamic field is indexed for **full-text search**. + + + ```js + class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { + super(); + + const { createField } = this.mapUtils(); + + const useSearchAnalyzerForDynamicFields = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + this.map("Products", product => { + return { + _: Object.keys(product.descriptions).map(key => + createField( + "Description_" + key, + product.descriptions[key], + { + // Index each generated dynamic field for FTS + indexing: "Search", + storage: false + })) + }; + }); + + this.storeAllFields("No"); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + this.configuration[useSearchAnalyzerForDynamicFields] = "true"; + } + } + ``` + - - - -{`const matchingDocuments = session.query({ indexName: 'Products_ByAnyField_JS' }) - // 'lastName' is a dynamic-index-field that was indexed from the document - .whereEquals('lastName', 'Doe') - .all(); -`} - - - - -{`// 'lastName' is a dynamic-index-field that was indexed from the document -from index 'Products/ByAnyField/JS' where lastName = "Doe" -`} - - - +* **Full-text search query**: -
+ Query the generated dynamic field by its field name. + In this example, the query targets the generated field `Description_English`. + Since `Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery` is set to `true`, + the searched term `"north wind"` is analyzed with the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). -## Indexing documents fields VALUES + + + ```js + const results = await session + .query({ indexName: "Products/ByLocalizedDescription/JS" }) + .search("Description_English", "north wind") + .all(); + ``` + - -#### Example - basic -This example shows: - -* Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. -* Documents can then be queried based on those indexed values. -* For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---index-a-list-of-properties) below. -**The document**: - - -{`class Product \{ - constructor(id, productType, pricePerUnit) \{ - this.id = id; - - // The VALUE of productType will be dynamically indexed - this.productType = productType; - this.pricePerUnit = pricePerUnit; - \} -\} -`} - - + + ```sql + from index "Products/ByLocalizedDescription/JS" + where search(Description_English, "north wind") + ``` + + - - -{`// Sample document content -\{ - "productType": "Electronics", - "pricePerUnit": 23 -\} -`} - - +--- -**The index**: + + +### Configuring analyzers for dynamic fields -* The following index will index the **value** of document field 'productType'. +The `createField` options object does **not** let you specify a custom analyzer directly. +However, you can still configure which analyzer RavenDB will use for dynamic fields in the index definition by either: + +* Configuring an analyzer for a specific generated dynamic field name. +* Configuring `ALL_FIELDS` as a fallback for fields that do not have their own explicit analyzer configuration. + +Use `ALL_FIELDS` with care, since it can affect any field that does not have its own explicit analyzer configuration. -* This value will be the dynamic-index-field name on which you can query. - e.g. Field value `Electronics` will be the dynamic-index-field. +--- - - - -{`class Products_ByProductType extends AbstractCsharpIndexCreationTask { - constructor () { - super(); +#### Configure analyzer for a specific dynamic field - // The field name will be the value of document field 'productType' - // The field terms will be derived from document field 'pricePerUnit' - this.map = "docs.Products.Select(p => new { " + - " _ = this.CreateField(p.productType, p.pricePerUnit) " + - "})"; - } -} -`} - - - - -{`class Products_ByProductType_JS extends AbstractJavaScriptIndexCreationTask { - constructor () { + +```js +class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { super(); const { createField } = this.mapUtils(); - this.map("Products", p => { + this.map("Products", product => { return { - _: [ - // The field name will be the value of document field 'productType' - // The field terms will be derived from document field 'pricePerUnit' - createField(p.productType, p.pricePerUnit, { - indexing: "Search", - storage: false, - termVector: null - }) - ] + _: Object.keys(product.descriptions).map(key => + createField( + "Description_" + key, + product.descriptions[key], + { + indexing: "Search", + storage: false + })) }; }); - } -} -`} - - - - -**The query**: - -* To get all documents of some product type having a specific price per unit use: - - - - -{`const matchingDocuments = session.query({ indexName: 'Products_ByProductType' }) - // 'Electronics' is the dynamic-index-field that was indexed from document field 'productType' - .whereEquals('Electronics', 23) - .all(); -`} - - - - -{`// 'Electronics' is the dynamic-index-field that was indexed from document field 'productType' -from index 'Products/ByProductType' where Electronics = 23 -`} - - - - - - -#### Example - list -The following allows you to: - -* Index **values** from items in a list -* After index is deployed, any item added this list in the document will be dynamically indexed as well. -**The document**: - - -{`class Product \{ - constructor(id, name, attributes) \{ - this.id = id; - this.name = name; - - // For each element in this list, the VALUE of property 'propName' will be dynamically indexed - // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields - this.attributes = attributes; - \} -\} - -class Attribute \{ - constructor(propName, propValue) \{ - this.propName = propName; - this.propValue = propValue; - \} -\} -`} - - - - - -{`// Sample document content -\{ - "name": "SomeName", - "attributes": [ - \{ - "propName": "Color", - "propValue": "Blue" - \}, - \{ - "propName": "Width", - "propValue": "10" - \}, - \{ - "propName": "Length", - "propValue": "20" - \}, - ... - ] -\} -`} - - - -**The index**: - -* The following index will create a dynamic-index-field per item in the document's `attributes` list. - New items added to the attributes list after index creation time will be dynamically indexed as well. - -* The actual dynamic-index-field name on which you can query will be the item's propName **value**. - e.g. 'propName' value `Width` will be a dynamic-index-field. - - - - -{`class Attributes_ByName extends AbstractCsharpIndexCreationTask -{ - constructor () { - super(); + this.storeAllFields("No"); - // For each attribute item, the field name will be the value of field 'propName' - // The field terms will be derived from field 'propValue' - // A regular-index-field (Name) is defined as well - this.map = - "docs.Products.Select(p => new { " + - " _ = p.attributes.Select(item => this.CreateField(item.propName, item.propValue)), " + - " Name = p.name " + - "})"; + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + this.analyze("Description_English", "StopAnalyzer"); } } -`} - +``` - - -{`class Attributes_ByName_JS extends AbstractJavaScriptIndexCreationTask { - constructor () { + +#### Configure a fallback analyzer for all fields + + +```js +class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { super(); const { createField } = this.mapUtils(); - this.map("Products", p => { + this.map("Products", product => { return { - // For each item, the field name will be the value of field 'propName' - // The field terms will be derived from field 'propValue' - _: p.attributes.map(item => createField(item.propName, item.propValue, { - indexing: "Search", - storage: true, - termVector: null - })), - - // A regular-index-field can be defined as well: - Name: p.name + _: Object.keys(product.descriptions).map(key => + createField( + "Description_" + key, + product.descriptions[key], + { + indexing: "Search", + storage: false + })) }; }); - } -} -`} - - - - -**The query**: -* To get all documents matching a specific attribute property use: + this.storeAllFields("No"); - - - -{`const matchingDocuments = session.query({ indexName: 'Attributes/ByName' }) - // 'Width' is a dynamic-index-field that was indexed from the attributes list - .whereEquals('Width', 10) - .all(); -`} - - - - -{`// 'Width' is a dynamic-index-field that was indexed from the attributes list -from index 'Attributes/ByName' where Width = 10 -`} - + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + this.analyze(CONSTANTS.Documents.Indexing.Fields.ALL_FIELDS, "StopAnalyzer"); + } +} +``` - - - + + + + +### Which analyzer is used for the query term? -## CreateField syntax +The following applies when a `search()` query targets a **dynamic field**: -#### Syntax for LINQ-index: +| Index configuration | [Configuration key](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) | Analyzer used for the query term | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| No analyzer is explicitly configured for the generated field name | `false` | The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) | +| No analyzer is explicitly configured for the generated field name | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `false` | The analyzer resolved by the normal field fallback path,
e.g. `WhitespaceAnalyzer` | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| The generated field name is explicitly configured,
e.g. `this.analyze("Description_English", "StopAnalyzer")` | `false` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +| The generated field name is explicitly configured,
e.g. `this.analyze("Description_English", "StopAnalyzer")` | `true` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | - - -{`object CreateField(string name, object value); +
-object CreateField(string name, object value, bool stored, bool analyzed); + -object CreateField(string name, object value, CreateFieldOptions options); -`} - - + -#### Syntax for JavaScript-index: +#### Syntax: - - -{`createField(fieldName, fieldValue, options); // returns object -`} - + +```js +createField(fieldName, fieldValue, options); // returns object +``` -| Parameters | Type | Description | -|----------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **fieldName** | `string` | Name of the dynamic-index-field | -| **fieldValue** | `object` | Value of the dynamic-index-field
The field Terms are derived from this value. | -| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorate.Yes` | -| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing.Default` (default value)
`false` - `FieldIndexing.Exact`
`true` - `FieldIndexing.Search` | -| **options** | `CreateFieldOptions` | Dynamic-index-field options | +| Parameters | Type | Description | +|----------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **fieldName** | `string` | Name of the dynamic-index-field | +| **fieldValue** | `object` | Value of the dynamic-index-field
The field terms are derived from this value. | +| **options** | `object` | Dynamic-index-field options | -| CreateFieldOptions | | | -|--------------------|--------------------|----------------------------------------------------------------------------| -| **Storage** | `FieldStorage?` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | -| **Indexing** | `FieldIndexing?` | Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | -| **TermVector** | `FieldTermVector?` | Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | +| options object | | | +|-----------------|--------------------|-----------------------------------------------------------------------------------| +| **storage** | `boolean` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | +| **indexing** | `FieldIndexing` | Sets the field indexing behavior.
Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | +| **termVector** | `FieldTermVector` | Sets the field term-vector behavior.
Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | @@ -536,21 +713,19 @@ object CreateField(string name, object value, CreateFieldOptions options); However, using `_` is just a convention. Any other string can be used instead. * This property is Not queryable, it is only used in the index definition syntax. - The actual dynamic-index-fields that are generated are defined by the `CreateField` method. + The actual dynamic-index-fields that are generated are defined by the `createField` method. +
+ + - -## Indexed fields & terms view - -The generated dynamic-index-fields and their indexed terms can be viewed in the **Terms View**. -Below are sample index fields & their terms generated from the last example. - +The generated dynamic index fields and their indexed terms can be viewed in the **Terms View** in Studio. +Below are sample index fields & their terms generated from [this example](../../indexes/using-dynamic-fields.mdx#example---list). + ![Figure 1. Go to terms view](../assets/dynamic-index-fields-1.png) - + ![Figure 2. Indexed fields & terms](../assets/dynamic-index-fields-2.png) - - - - + + diff --git a/versioned_docs/version-7.0/indexes/content/_using-dynamic-fields-php.mdx b/versioned_docs/version-7.0/indexes/content/_using-dynamic-fields-php.mdx index 09c58cc9fc..6992310715 100644 --- a/versioned_docs/version-7.0/indexes/content/_using-dynamic-fields-php.mdx +++ b/versioned_docs/version-7.0/indexes/content/_using-dynamic-fields-php.mdx @@ -2,537 +2,969 @@ import Admonition from '@theme/Admonition'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; +import ContentFrame from '@site/src/components/ContentFrame'; +import Panel from '@site/src/components/Panel'; -* In RavenDB different documents can have different shapes. - Documents are schemaless - new fields can be added or removed as needed. +* In RavenDB, different documents can have different shapes. + Documents are schemaless - new fields can be added or removed as needed. + For such dynamic data, you can define indexes with **dynamic-index-fields**. + +* Dynamic-index-fields are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time, + which is very useful when working with highly dynamic systems. + +* Any value type can be indexed, string, number, date, etc. + An index definition can contain both dynamic-index-fields and regular-index-fields. + +* In this article: + * [Indexing field KEYS](../../indexes/using-dynamic-fields.mdx#indexing-field-keys) + * [Example - index every field under an object](../../indexes/using-dynamic-fields.mdx#example---index-every-field-under-an-object) + * [Example - index every field](../../indexes/using-dynamic-fields.mdx#example---index-every-field) + * [Indexing field VALUES](../../indexes/using-dynamic-fields.mdx#indexing-field-values) + * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) + * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) + * [Indexing dynamic fields for full-text search](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + * [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) + * [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + * [`CreateField` syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) + * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields-terms-view) -* For such dynamic data, you can define indexes with **dynamic-index-fields**. - -* This allows querying the index on fields that aren't yet known at index creation time, - which is very useful when working on highly dynamic systems. - -* Any value type can be indexed, string, number, date, etc. - -* An index definition can contain both dynamic-index-fields and regular-index-fields. + -* In this page: + - * [Indexing documents fields KEYS](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-keys) - * [Example - index any field under object](../../indexes/using-dynamic-fields.mdx#example---index-any-field-under-object) - * [Example - index any field](../../indexes/using-dynamic-fields.mdx#example---index-any-field) - * [Indexing documents fields VALUES](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-values) - * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) - * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) - * [CreateField syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) - * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields--terms-view) + + +### Example - index every field under an object + +The following example allows you to: + * Index any field that is under the same object from the document. + * After index is deployed, any new field added to this object will be indexed as well. -## Indexing documents fields KEYS - -## Example - index any field under object +* **The document**: + + + ```php + use Ds\Map as DSMap; - + class Product + { + public ?string $id = null; -* Index any field that is under the some object from the document. -* After index is deployed, any new field added to the this object will be indexed as well. + // The KEYS under the attributes object will be dynamically indexed. + // Fields added to this object after index creation time will also get indexed. + public ?DSMap $attributes = null; - - -* **The document**: - - -{`use Ds\\Map as DSMap; - -class Product -\{ - private ?string $id = null; - - // The KEYS under the Attributes object will be dynamically indexed - // Fields added to this object after index creation time will also get indexed - public ?DSMap $attributes = null; -\} -`} - - - - -{`// Sample document content -\{ - "Attributes": \{ - "Color": "Red", - "Size": 42 - \} -\} -`} - - + // ... getters and setters + } + ``` + + + + ```json + // Sample document content + { + "attributes": { + "Color": "Red", + "Size": 42 + } + } + ``` + * **The index**: - The below index will index any field under the `Attributes` object from the document, + + The following index will index any field under the `attributes` object from the document, a dynamic-index-field will be created for each such field. New fields added to the object after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the attribute field **key**. - E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. + The actual dynamic-index-field name on which you can query will be the attribute field **key**. + E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. - - - -{`class Products_ByAttributeKey extends AbstractIndexCreationTask -{ - public function __construct() + + + ```php + class Products_ByAttributeKey extends AbstractIndexCreationTask { - parent::__construct(); - - $this->map = "from p in docs.Products select new {" . - "_ = p.attributes.Select(item => CreateField(item.Key, item.Value))" . - "}"; + public function __construct() + { + parent::__construct(); + + // Call 'CreateField' to generate dynamic-index-fields + // from the attributes object keys. + // + // Using '_' is just a convention. + // Any other string can be used instead of '_'. + // + // The actual field name will be 'item.Key'. + // The actual field terms will be derived from 'item.Value'. + $this->map = + "from p in docs.Products " . + "select new { " . + " _ = p.attributes.Select(item => CreateField(item.Key, item.Value)) " . + "}"; + } } -} -`} - - - - -{`class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask -{ - public function __construct() + ``` + + + ```php + class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask { - parent::__construct(); - - $this->setMaps([ - "map('Products', function (p) { " . - " return { " . - " _: Object.keys(p.attributes).map(key => createField(key, p.attributes[key], " . - " { indexing: 'Search', storage: false, termVector: null })) " . - " }; " . - "}) " - ]); + public function __construct() + { + parent::__construct(); + + $this->setMaps([ + "map('Products', function (p) { + return { + // The field name will be the key + // The field terms will be derived from the corresponding value + _: Object.keys(p.attributes) + .map(key => createField(key, p.attributes[key], null)) + }; + })" + ]); + } } -} -`} - - - - + ``` + + * **The query**: - * You can now query the generated dynamic-index fields. - * To get all documents with some 'size' use: - - - -{`$matchingDocuments = $session - ->advanced() - ->documentQuery(Product::class, Products_ByAttributeKey::class) - // 'Size' is a dynamic-index-field that was indexed from the Attributes object - ->whereEquals("Size", 42) - ->toList(); -`} - - - - -{`// 'Size' is a dynamic-index-field that was indexed from the Attributes object -from index 'Products/ByAttributeKey' where Size = 42 -`} - - - - -## Example - index any field - - + + You can now query the generated dynamic-index fields. + The `_` property is Not queryable but used only in the index definition syntax. + + To get all documents with a specific _Size_ use: + + + + ```php + /** @var array $matchingDocuments */ + $matchingDocuments = $session + ->advanced() + ->documentQuery(Product::class, Products_ByAttributeKey::class) + // 'Size' is a dynamic-index-field that was indexed from the attributes object + ->whereEquals("Size", 42) + ->toList(); + ``` + + + ```sql + // 'Size' is a dynamic-index-field that was indexed from the attributes object + from index 'Products/ByAttributeKey' where Size = 42 + ``` + + + + + Dynamic index fields are queried with _documentQuery_ or _RQL_ because the field names (e.g. `Size`, `Color`) + are generated at indexing time and are not properties on the _Product_ class. + + Use the string-based `whereEquals("Size", 42)` or RQL `where Size = 42` instead. + + + + + + +### Example - index every field + +The following example allows you to: * Define an index on a collection **without** needing any common structure between the indexed documents. - * After index is deployed, any new field added to the document will be indexed as well. - + * After the index is deployed, any new field added to any document in the collection will be indexed as well. - + -Consider whether this is really necessary, as indexing every single field can end up costing time and disk space. +* Use this approach with care. +* Indexing every field can increase indexing time, memory usage, and disk space, + especially for large or frequently updated documents. * **The document**: - - -{`class Product -\{ - private ?string $id = null; - - // All KEYS in the document will be dynamically indexed - // Fields added to the document after index creation time will also get indexed - public ?string $firstName = null; - public ?string $lastName = null; - public ?string $title = null; - - // ... getters and setters -\} -`} - - + + + ```php + class Product + { + public ?string $id = null; - - -{`// Sample document content - \{ - "FirstName": "John", - "LastName": "Doe", - "Title": "Engineer", + // All KEYS in the document will be dynamically indexed. + // Fields added to the document after index creation time will also get indexed. + public ?string $firstName = null; + public ?string $lastName = null; + public ?string $title = null; // ... -\} -`} - - + + // ... getters and setters + } + ``` + + + + ```json + // Sample document content + { + "firstName": "John", + "lastName": "Doe", + "title": "Engineer" + // ... + } + ``` + * **The index**: - The below index will index any field from the document, + + The following index will index any field from the document, a dynamic-index-field will be created for each field. New fields added to the document after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the field **key**. - E.g., Keys `FirstName` & `LastName` will become the actual dynamic-index-fields. - - - - -{`class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask -{ - public function __construct() + The actual dynamic-index-field name on which you can query will be the field **key**. + E.g., Keys `firstName` & `lastName` will become the actual dynamic-index-fields. + + The example uses a JavaScript index because it enumerates all top-level document fields at indexing time with `Object.keys(...)`. + LINQ indexes typically create dynamic fields from a known enumerable structure, such as a dictionary or collection. + + + + ```php + class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask { - parent::__construct(); - - // This will index EVERY FIELD under the top level of the document - $this->setMaps([ - "map('Products', function (p) { - return { - _: Object.keys(p).map(key => createField(key, p[key], - { indexing: 'Search', storage: true, termVector: null })) - } - })" - ]); + public function __construct() + { + parent::__construct(); + + // This will index EVERY FIELD under the top level of the document + $this->setMaps([ + "map('Products', function (p) { + return { + _: Object.keys(p).map(key => createField(key, p[key], null)) + }; + })" + ]); + } } -} -`} - - - + ``` + + * **The query**: - * To get all documents with some 'LastName' use: - - - -{`$matchingDocuments = $session - ->advanced() - ->documentQuery(Product::class, Products_ByAnyField_JS::class) - // 'LastName' is a dynamic-index-field that was indexed from the document - ->whereEquals("LastName", "Doe") - ->toList(); -`} - - - - -{`// 'LastName' is a dynamic-index-field that was indexed from the document -from index 'Products/ByAnyField/JS' where LastName = "Doe" -`} - - - + + To get all documents with a specific _lastName_ use: + + + + ```php + /** @var array $matchingDocuments */ + $matchingDocuments = $session + ->advanced() + ->documentQuery(Product::class, Products_ByAnyField_JS::class) + // 'lastName' is a dynamic-index-field that was indexed from the document + ->whereEquals("lastName", "Doe") + ->toList(); + ``` + + + ```sql + // 'lastName' is a dynamic-index-field that was indexed from the document + from index 'Products/ByAnyField/JS' where lastName = "Doe" + ``` + + + + + + + + + + + +### Example - basic + + +The following example shows: + * Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. + * Documents can then be queried based on those indexed values. + * For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---list) below. + +* **The document**: + + + ```php + class Product + { + public ?string $id = null; + // The VALUE of productType will be dynamically indexed + public ?string $productType = null; + public ?int $pricePerUnit = null; -## Indexing documents fields VALUES + // ... getters and setters + } + ``` + + + + ```json + // Sample document content + { + "productType": "Electronics", + "pricePerUnit": 23 + } + ``` + -## Example - basic +* **The index**: + + The following index will index the **value** of document field 'productType'. + + This value will be the dynamic-index-field name on which you can query. + E.g., Field value `Electronics` will be the dynamic-index-field. - + + + ```php + class Products_ByProductType extends AbstractIndexCreationTask + { + public function __construct() + { + parent::__construct(); + + // Call 'CreateField' to generate the dynamic-index-fields. + // The field name will be the value of document field 'productType'. + // The field terms will be derived from document field 'pricePerUnit'. + $this->map = + "from p in docs.Products " . + "select new { " . + " _ = CreateField(p.productType, p.pricePerUnit) " . + "}"; + } + } + ``` + + + ```php + class Products_ByProductType_JS extends AbstractJavaScriptIndexCreationTask + { + public function __construct() + { + parent::__construct(); + + $this->setMaps([ + "map('Products', function (p) { + return { + // The field name will be the value of document field 'productType' + // The field terms will be derived from document field 'pricePerUnit' + _: createField(p.productType, p.pricePerUnit, null) + }; + })" + ]); + } + } + ``` + + -* Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. -* Documents can then be queried based on those indexed values. +* **The query**: + + To get all documents of some product type having a specific price per unit use: + + + + ```php + /** @var array $matchingDocuments */ + $matchingDocuments = $session + ->advanced() + ->documentQuery(Product::class, Products_ByProductType::class) + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + ->whereEquals("Electronics", 23) + ->toList(); + ``` + + + ```sql + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + from index 'Products/ByProductType' where Electronics = 23 + ``` + + + + + + + +### Example - list + +The following example allows you to: + * Index **values** from items in a list + * After index is deployed, any item added to this list in the document will be dynamically indexed as well. * **The document**: - - -{`class Product -\{ - public ?string $id = null; - - // The VALUE of ProductType will be dynamically indexed - public ?string $productType = null; - public ?int $pricePerUnit = null; - - // ... getters and setters -\} -`} - - + + + ```php + class Product + { + public ?string $id = null; + public ?string $name = null; - - -{`// Sample document content -\{ - "ProductType": "Electronics", - "PricePerUnit": 23 -\} -`} - - + // For each element in this list, + // the VALUE of property 'propName' will be dynamically indexed. + // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields. + public ?AttributeList $attributes = null; + + // ... getters and setters + } + + class Attribute + { + public ?string $propName = null; + public ?string $propValue = null; + + // ... getters and setters + } + + class AttributeList extends TypedList + { + protected function __construct() + { + parent::__construct(Attribute::class); + } + } + ``` + + + + ```json + // Sample document content + { + "name": "SomeName", + "attributes": [ + { + "propName": "Color", + "propValue": "Blue" + }, + { + "propName": "Width", + "propValue": "10" + }, + { + "propName": "Length", + "propValue": "20" + } + // ... + ] + } + ``` + * **The index**: - The below index will index the **value** of document field 'ProductType'. + + The following index will create a dynamic-index-field per item in the document's `attributes` list. + New items added to the attributes list after index creation time will be dynamically indexed as well. - This value will be the dynamic-index-field name on which you can query. - E.g., Field value `Electronics` will be the dynamic-index-field. + The actual dynamic-index-field name on which you can query will be the item's propName **value**. + E.g., 'propName' value `Width` will be a dynamic-index-field. + + + + ```php + class Attributes_ByName extends AbstractIndexCreationTask + { + public function __construct() + { + parent::__construct(); + + // Define the dynamic-index-fields by calling 'CreateField'. + // A dynamic-index-field will be generated for each item in 'attributes'. + // + // For each item, the field name will be the value of field 'propName'. + // The field terms will be derived from field 'propValue'. + $this->map = + "from p in docs.Products " . + "select new { " . + " _ = p.attributes.Select(item => CreateField(item.propName, item.propValue)), " . + " Name = p.name " . + "}"; + } + } + ``` + + + ```php + class Attributes_ByName_JS extends AbstractJavaScriptIndexCreationTask + { + public function __construct() + { + parent::__construct(); + + $this->setMaps([ + "map('Products', function (p) { + return { + // For each item, + // the field name will be the value of field 'propName' + // The field terms will be derived from field 'propValue' + _: p.attributes.map(item => createField( + item.propName, item.propValue, null)), + + // A regular index field can be defined as well: + Name: p.name + }; + })" + ]); + } + } + ``` + + + +* **The query**: + + To get all documents matching a specific attribute property use: + + + + ```php + /** @var array $matchingDocuments */ + $matchingDocuments = $session + ->advanced() + ->documentQuery(Product::class, Attributes_ByName::class) + // 'Width' is a dynamic-index-field that was indexed from the attributes list + ->whereEquals("Width", 10) + ->toList(); + ``` + + + ```sql + // 'Width' is a dynamic-index-field that was indexed from the attributes list + from index 'Attributes/ByName' where Width = 10 + ``` + + + + + + + + + + + +Dynamic fields can be indexed for [Full-text search](../../indexes/querying/searching.mdx) by setting `FieldIndexing::search()` in `CreateFieldOptions`. + +* **At indexing time**: + At indexing time, RavenDB analyzes the dynamic field value and creates the indexed terms. + The analyzer used at indexing time is resolved from the index definition. + You can configure an analyzer for a specific generated field name, or configure `ALL_FIELDS` as a fallback. + See [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) below. + + * If the generated dynamic field name has an explicit analyzer configuration, that analyzer is used. + * Otherwise, if `ALL_FIELDS` has an analyzer configuration, RavenDB uses it as a fallback. + * If no analyzer is configured for the field, RavenDB uses the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + +* **At querying time**: + For full-text search to return the expected results, the analyzer used at indexing time and the analyzer used for the query term should produce compatible terms. + + When a `search()` query targets a dynamic field that does **not** have an explicit analyzer configuration for its generated field name, + RavenDB uses the [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) to analyze the query term by default. + + Set the [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) + configuration key to `true` to make RavenDB use the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) as the fallback analyzer for the query term instead. + + This configuration can be set at different scopes: server-wide, per database, or per index. + The example below sets it at the index scope, in the index definition. + + For a summary of how RavenDB resolves the analyzer used for the query term, + see [Which analyzer is used for the query term](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) below. + + + +--- + +The following example indexes localized product descriptions as dynamic fields and queries one of those generated fields using full-text search: + +* **The document**: + + + ```php + use Ds\Map as DSMap; + + class Product + { + public ?string $id = null; + public ?DSMap $descriptions = null; + + // ... getters and setters + } + ``` + + + + ```json + // Sample document content + { + "descriptions": { + "English": "North wind jacket", + "French": "Veste vent du nord" + } + } + ``` + + +* **The index**: + + The following index creates one dynamic field for each entry in the `descriptions` dictionary. + For example, the key `English` creates the dynamic field `Description_English`. - + Each generated dynamic field is indexed for **full-text search**. + + + + ```php + class Products_ByLocalizedDescription extends AbstractIndexCreationTask + { + private const USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public function __construct() + { + parent::__construct(); + + $this->map = + "from product in docs.Products " . + "select new { " . + " _ = product.descriptions.Select(x => " . + " CreateField(" . + " \"Description_\" + x.Key, " . + " x.Value, " . + " new CreateFieldOptions { " . + // Index each generated dynamic field for FTS + " Indexing = FieldIndexing.Search, " . + " Storage = FieldStorage.No " . + " })) " . + "}"; + + $this->storeAllFields(FieldStorage::no()); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + $this->getConfiguration()[self::USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS] = "true"; + } + } + ``` + + + ```php + class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask + { + private const USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public function __construct() + { + parent::__construct(); + + $this->setMaps([ + "map('Products', function (product) { + return { + _: Object.keys(product.descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.descriptions[key], + { + // Index each generated dynamic field for FTS + indexing: 'Search', + storage: 'No' + }); + }) + }; + })" + ]); + + // Apply a storage default to every generated field + $fields = new IndexFieldOptionsArray(); + $allFieldsOptions = new IndexFieldOptions(); + $allFieldsOptions->setStorage(FieldStorage::no()); + $fields->offsetSet(DocumentsIndexingFields::ALL_FIELDS, $allFieldsOptions); + $this->setFields($fields); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + $this->getConfiguration()[self::USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS] = "true"; + } + } + ``` + + + +* **Full-text search query**: + + Query the generated dynamic field by its field name. + + In this example, the query targets the generated field `Description_English`. + + Since `Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery` is set to `true`, + the searched term `"north wind"` is analyzed with the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + + + + ```php + /** @var array $results */ + $results = $session->advanced() + ->documentQuery(Product::class, Products_ByLocalizedDescription::class) + ->search("Description_English", "north wind") + ->toList(); + ``` + + + + ```sql + from index "Products/ByLocalizedDescription" + where search(Description_English, "north wind") + ``` + + + +--- + + + +### Configuring analyzers for dynamic fields + +`CreateFieldOptions` does **not** let you specify a custom analyzer directly. +However, you can still configure which analyzer RavenDB will use for dynamic fields in the index definition by either: + +* Configuring an analyzer for a specific generated dynamic field name. +* Configuring `ALL_FIELDS` as a fallback for fields that do not have their own explicit analyzer configuration. + +Use `ALL_FIELDS` with care, since it can affect any field that does not have its own explicit analyzer configuration. + +--- + +#### Configure analyzer for a specific dynamic field + + - -{`class Products_ByProductType extends AbstractIndexCreationTask +```php +class Products_ByLocalizedDescription extends AbstractIndexCreationTask { public function __construct() { parent::__construct(); - // Call 'CreateField' to generate the dynamic-index-fields - // The field name will be the value of document field 'ProductType' - // The field terms will be derived from document field 'PricePerUnit' - $this->map = "docs.Products.Select(p => new { " . - " _ = this.CreateField(p.productType, p.pricePerUnit) " . - "})"; + $this->map = + "from product in docs.Products " . + "select new { " . + " _ = product.descriptions.Select(x => " . + " CreateField(" . + " \"Description_\" + x.Key, " . + " x.Value, " . + " new CreateFieldOptions { " . + " Indexing = FieldIndexing.Search, " . + " Storage = FieldStorage.No " . + " })) " . + "}"; + + $this->storeAllFields(FieldStorage::no()); + + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + $this->analyze("Description_English", "StopAnalyzer"); } } -`} - +``` - - -{`class Products_ByProductType_JS extends AbstractJavaScriptIndexCreationTask + +```php +class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { public function __construct() { parent::__construct(); $this->setMaps([ - "map('Products', function (p) { + "map('Products', function (product) { return { - _: createField(p.ProductType, p.PricePerUnit, - { indexing: 'Search', storage: true, termVector: null }) + _: Object.keys(product.descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; })" ]); - } -} -`} - - - - -* **The query**: - * To get all documents of some product type having a specific price per unit use: - - - -{`$matchingDocuments = $session - ->advanced() - ->documentQuery(Product::class, Products_ByProductType::class) -// 'Electronics' is the dynamic-index-field that was indexed from document field 'ProductType' -->whereEquals("Electronics", 23) -->toList(); -`} - - - - -{`// 'Electronics' is the dynamic-index-field that was indexed from document field 'ProductType' -from index 'Products/ByProductType' where Electronics = 23 -`} - - - - -## Example - list - - -* Index **values** from items in a list -* After index is deployed, any item added this list in the document will be dynamically indexed as well. + // Apply a storage default to every generated field + + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + $fields = new IndexFieldOptionsArray(); - + $allFieldsOptions = new IndexFieldOptions(); + $allFieldsOptions->setStorage(FieldStorage::no()); + $fields->offsetSet(DocumentsIndexingFields::ALL_FIELDS, $allFieldsOptions); -* **The document**: - - -{`class Product -\{ - public ?string $id = null; - public ?string $name = null; - - // For each element in this list, the VALUE of property 'PropName' will be dynamically indexed - // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields - public ?AttributeList $attributes = null; - - // ... getters and setters -\} - -class Attribute -\{ - public ?string $propName = null; - public ?string $propValue = null; - - // ... getters and setters -\} - -class AttributeList extends TypedList -\{ - protected function __construct() - \{ - parent::__construct(Attribute::class); - \} -\} -`} - - + $englishFieldOptions = new IndexFieldOptions(); + $englishFieldOptions->setAnalyzer("StopAnalyzer"); + $fields->offsetSet("Description_English", $englishFieldOptions); - - -{`// Sample document content -\{ -Name": "SomeName", -Attributes": [ - \{ - "PropName": "Color", - "PropValue": "Blue" - \}, - \{ - "PropName": "Width", - "PropValue": "10" - \}, - \{ - "PropName": "Length", - "PropValue": "20" - \}, - ... - -\} -`} - + $this->setFields($fields); + } +} +``` + -* **The index**: - The below index will create a dynamic-index-field per item in the document's `Attributes` list. - New items added to the Attributes list after index creation time will be dynamically indexed as well. - - The actual dynamic-index-field name on which you can query will be the item's PropName **value**. - E.g., 'PropName' value `width` will be a dynamic-index-field. +#### Configure a fallback analyzer for all fields - + - -{`class Attributes_ByName extends AbstractIndexCreationTask +```php +class Products_ByLocalizedDescription extends AbstractIndexCreationTask { public function __construct() { parent::__construct(); - // Define the dynamic-index-fields by calling CreateField - // A dynamic-index-field will be generated for each item in the Attributes list + $this->map = + "from product in docs.Products " . + "select new { " . + " _ = product.descriptions.Select(x => " . + " CreateField(" . + " \"Description_\" + x.Key, " . + " x.Value, " . + " new CreateFieldOptions { " . + " Indexing = FieldIndexing.Search, " . + " Storage = FieldStorage.No " . + " })) " . + "}"; - // For each item, the field name will be the value of field 'PropName' - // The field terms will be derived from field 'PropValue' + $this->storeAllFields(FieldStorage::no()); - $this->map = - "docs.Products.Select(p => new { " . - " _ = p.attributes.Select(item => this.CreateField(item.propName, item.propValue)), " . - " Name = p.name " . - "})"; + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + $this->analyze(DocumentsIndexingFields::ALL_FIELDS, "StopAnalyzer"); } } -`} - +``` - - -{`class Attributes_ByName_JS extends AbstractJavaScriptIndexCreationTask + +```php +class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { public function __construct() { parent::__construct(); $this->setMaps([ - "map('Products', function (p) { + "map('Products', function (product) { return { - _: p.Attributes.map(item => createField(item.PropName, item.PropValue, - { indexing: 'Search', storage: true, termVector: null })), - Name: p.Name + _: Object.keys(product.descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; })" ]); + + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + $fields = new IndexFieldOptionsArray(); + $allFieldsOptions = new IndexFieldOptions(); + $allFieldsOptions->setStorage(FieldStorage::no()); + $allFieldsOptions->setAnalyzer("StopAnalyzer"); + $fields->offsetSet(DocumentsIndexingFields::ALL_FIELDS, $allFieldsOptions); + $this->setFields($fields); } } -`} - - - - -* **The query**: - To get all documents matching a specific attribute property use: - - - -{`/** @var array $matchingDocuments */ -$matchingDocuments = $session - ->advanced() - ->documentQuery(Product::class, Attributes_ByName::class) - // 'Width' is a dynamic-index-field that was indexed from the Attributes list - ->whereEquals("Width", 10) - ->toList(); -`} - - - - -{`// 'Width' is a dynamic-index-field that was indexed from the Attributes list -from index 'Attributes/ByName' where Width = 10 -`} - +``` + + + + +### Which analyzer is used for the query term? +The following applies when a `search()` query targets a **dynamic field**: -## CreateField syntax +| Index configuration | [Configuration key](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) | Analyzer used for the query term | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| No analyzer is explicitly configured for the generated field name | `false` | The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) | +| No analyzer is explicitly configured for the generated field name | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `false` | The analyzer resolved by the normal field fallback path,
e.g. `WhitespaceAnalyzer` | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| The generated field name is explicitly configured,
e.g. `$this->analyze("Description_English", "StopAnalyzer")` | `false` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +| The generated field name is explicitly configured,
e.g. `$this->analyze("Description_English", "StopAnalyzer")` | `true` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | - - -{`object CreateField(string name, object value); +
+ +
+ + + +#### Syntax for LINQ-index: + + +```php +object CreateField(string name, object value); object CreateField(string name, object value, bool stored, bool analyzed); object CreateField(string name, object value, CreateFieldOptions options); -`} - +``` -| Parameters | Type | Description | -|------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **name** | `string` | Name of the dynamic-index-field | -| **value** | `object` | Value of the dynamic-index-field
The field Terms are derived from this value. | -| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`False` - will set `FieldStorage.NO` (default value)
`True` - will set `FieldStorate.YES` | -| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`None` - `FieldIndexing.Default` (default value)
`False` - `FieldIndexing.Exact`
`True` - `FieldIndexing.Search` | -| **options** | `CreateFieldOptions` | Dynamic-index-field options | +#### Syntax for JavaScript-index: -| CreateFieldOptions | | | -|--------------------|--------------------|----------------------------------------------------------------------------| -| **Storage** | `FieldStorage` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | -| **Indexing** | `FieldIndexing` | | -| **TermVector** | `FieldTermVector` | | + +```js +createField(fieldName, fieldValue, options); // returns object +``` + + +| Parameters | Type | Description | +|----------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **fieldName** | `string` | Name of the dynamic-index-field | +| **fieldValue** | `object` | Value of the dynamic-index-field
The field terms are derived from this value. | +| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage::no()` (default value)
`true` - will set `FieldStorage::yes()` | +| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing::default()` (default value)
`false` - `FieldIndexing::exact()`
`true` - `FieldIndexing::search()` | +| **options** | `CreateFieldOptions` | Dynamic-index-field options | + +| CreateFieldOptions | | | +|--------------------|--------------------|-----------------------------------------------------------------------------------| +| **Storage** | `FieldStorage?` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | +| **Indexing** | `FieldIndexing?` | Sets the field indexing behavior.
Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | +| **TermVector** | `FieldTermVector?` | Sets the field term-vector behavior.
Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | @@ -544,17 +976,15 @@ object CreateField(string name, object value, CreateFieldOptions options); +
+ + - -## Indexed fields & terms view - -The generated dynamic-index-fields and their indexed terms can be viewed in the **Terms View**. -Below are sample index fields & their terms generated from the last example. - +The generated dynamic index fields and their indexed terms can be viewed in the **Terms View** in Studio. +Below are sample index fields & their terms generated from [this example](../../indexes/using-dynamic-fields.mdx#example---list). + ![Figure 1. Go to terms view](../assets/dynamic-index-fields-1.png) - + ![Figure 2. Indexed fields & terms](../assets/dynamic-index-fields-2.png) - - - - + + diff --git a/versioned_docs/version-7.0/indexes/content/_using-dynamic-fields-python.mdx b/versioned_docs/version-7.0/indexes/content/_using-dynamic-fields-python.mdx index 69514a29e2..6862d18067 100644 --- a/versioned_docs/version-7.0/indexes/content/_using-dynamic-fields-python.mdx +++ b/versioned_docs/version-7.0/indexes/content/_using-dynamic-fields-python.mdx @@ -1,490 +1,886 @@ import Admonition from '@theme/Admonition'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -import CodeBlock from '@theme/CodeBlock'; +import ContentFrame from '@site/src/components/ContentFrame'; +import Panel from '@site/src/components/Panel'; -* In RavenDB different documents can have different shapes. - Documents are schemaless - new fields can be added or removed as needed. - -* For such dynamic data, you can define indexes with **dynamic-index-fields**. - -* This allows querying the index on fields that aren't yet known at index creation time, - which is very useful when working on highly dynamic systems. - -* Any value type can be indexed, string, number, date, etc. - -* An index definition can contain both dynamic-index-fields and regular-index-fields. - -* In this page: - - * [Indexing documents fields KEYS](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-keys) - * [Example - index any field under object](../../indexes/using-dynamic-fields.mdx#example---index-any-field-under-object) - * [Example - index any field](../../indexes/using-dynamic-fields.mdx#example---index-any-field) - * [Indexing documents fields VALUES](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-values) - * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) - * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) - * [CreateField syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) - * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields--terms-view) +* In RavenDB, different documents can have different shapes. + Documents are schemaless - new fields can be added or removed as needed. + For such dynamic data, you can define indexes with **dynamic-index-fields**. + +* Dynamic-index-fields are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time, + which is very useful when working with highly dynamic systems. + +* Any value type can be indexed, string, number, date, etc. + An index definition can contain both dynamic-index-fields and regular-index-fields. + +* In this article: + * [Indexing field KEYS](../../indexes/using-dynamic-fields.mdx#indexing-field-keys) + * [Example - index every field under an object](../../indexes/using-dynamic-fields.mdx#example---index-every-field-under-an-object) + * [Example - index every field](../../indexes/using-dynamic-fields.mdx#example---index-every-field) + * [Indexing field VALUES](../../indexes/using-dynamic-fields.mdx#indexing-field-values) + * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) + * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) + * [Indexing dynamic fields for full-text search](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + * [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) + * [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + * [`CreateField` syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) + * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields-terms-view) -## Indexing documents fields KEYS - -## Example - index any field under object + - - -* Index any field that is under the some object from the document. -* After index is deployed, any new field added to the this object will be indexed as well. + + +### Example - index every field under an object + +The following example allows you to: + * Index any field that is under the same object from the document. + * After index is deployed, any new field added to this object will be indexed as well. * **The document**: - - -{`class Product: - def __init__(self, Id: str = None, attributes: Dict[str, object] = None): - self.Id = Id - - # The KEYS under the Attributes object will be dynamically indexed - # Fields added to this object after index creation time will also get indexed - self.attributes = attributes -`} - - - - -{`// Sample document content -\{ - "Attributes": \{ - "Color": "Red", - "Size": 42 - \} -\} -`} - - + + + ```python + class Product: + def __init__(self, Id: str = None, Attributes: Dict[str, object] = None): + self.Id = Id + + # The KEYS under the Attributes object will be dynamically indexed + # Fields added to this object after index creation time will also get indexed + self.Attributes = Attributes + ``` + + + + ```json + // Sample document content + { + "Attributes": { + "Color": "Red", + "Size": 42 + } + } + ``` + * **The index**: - The below index will index any field under the `Attributes` object from the document, + + The following index will index any field under the `Attributes` object from the document, a dynamic-index-field will be created for each such field. New fields added to the object after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the attribute field **key**. - E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. - - - - -{`class Products_ByAttributeKey(AbstractIndexCreationTask): - def __init__(self): - super().__init__() - self.map = ( - "from p in docs.Products select new {" - "_ = p.attributes.Select(item => CreateField(item.Key, item.Value))" - "}" - ) -`} - - - - -{`class Products_ByAttributeKey_JS(AbstractJavaScriptIndexCreationTask): - def __init__(self): - super().__init__() - self.maps = { - """ - map('Products', function (p) { - return { - _: Object.keys(p.attributes).map(key => createField(key, p.attributes[key], - { indexing: 'Search', storage: true, termVector: null })) - }; - }) - """ - } -`} - - - - + The actual dynamic-index-field name on which you can query will be the attribute field **key**. + E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. + + + + ```python + class Products_ByAttributeKey(AbstractIndexCreationTask): + def __init__(self): + super().__init__() + self.map = ( + "from p in docs.Products select new { " + # Call 'CreateField' to generate dynamic-index-fields + # from the Attributes object keys. + + # Using '_' is just a convention. + # Any other string can be used instead of '_' + + # The actual field name will be 'item.Key' + # The actual field terms will be derived from 'item.Value' + "_ = p.Attributes.Select(item => CreateField(item.Key, item.Value)) " + "}" + ) + ``` + + + ```python + class Products_ByAttributeKey_JS(AbstractJavaScriptIndexCreationTask): + def __init__(self): + super().__init__() + self.maps = [ + """ + map('Products', function (p) { + return { + // The field name will be the key + // The field terms will be derived from the corresponding value + _: Object.keys(p.Attributes) + .map(key => createField(key, p.Attributes[key])) + }; + }) + """ + ] + ``` + + * **The query**: - * You can now query the generated dynamic-index fields. - * To get all documents with some 'size' use: - - - -{`matching_documents = list( - session.query_index_type(Products_ByAttributeKey, Product) - # 'size' is a dynamic-index-field that was indexed from the attributes object - .where_equals("size", 42) -) -`} - - - - -{`// 'Size' is a dynamic-index-field that was indexed from the Attributes object -from index 'Products/ByAttributeKey' where Size = 42 -`} - - - - -## Example - index any field - - + + You can now query the generated dynamic-index fields. + The `_` property is Not queryable but used only in the index definition syntax. + + To get all documents with a specific _Size_ use: + + + + ```python + matching_documents = list( + session.advanced + .document_query_from_index_type(Products_ByAttributeKey, Product) + # 'Size' is a dynamic-index-field that was indexed from the Attributes object + .where_equals("Size", 42) + ) + ``` + + + ```sql + // 'Size' is a dynamic-index-field that was indexed from the Attributes object + from index 'Products/ByAttributeKey' where Size = 42 + ``` + + + + + Dynamic index fields are queried with _document_query_ or _RQL_ because the field names (e.g. `Size`, `Color`) + are generated at indexing time and are not properties on the _Product_ class. + + Use the string-based `where_equals("Size", 42)` or RQL `where Size = 42`. + + + + + + +### Example - index every field + +The following example allows you to: * Define an index on a collection **without** needing any common structure between the indexed documents. - * After index is deployed, any new field added to the document will be indexed as well. - + * After the index is deployed, any new field added to any document in the collection will be indexed as well. - + -Consider whether this is really necessary, as indexing every single field can end up costing time and disk space. +* Use this approach with care. +* Indexing every field can increase indexing time, memory usage, and disk space, + especially for large or frequently updated documents. * **The document**: - - -{`class Product: - def __init__(self, Id: str = None, first_name: str = None, last_name: str = None, title: str = None): - self.Id = Id - - # All KEYS in the document will be dynamically indexes - # Fields added to the document after index creation time wil also get indexed - self.first_name = first_name - self.last_name = last_name - self.title = title - # ... -`} - - - - - -{`// Sample document content - \{ + + + ```python + class Product: + def __init__( + self, + Id: str = None, + FirstName: str = None, + LastName: str = None, + Title: str = None, + ): + self.Id = Id + + # All KEYS in the document will be dynamically indexed + # Fields added to the document after index creation time will also get indexed + self.FirstName = FirstName + self.LastName = LastName + self.Title = Title + # ... + ``` + + + + ```json + // Sample document content + { "FirstName": "John", "LastName": "Doe", "Title": "Engineer", // ... -\} -`} - - + } + ``` + * **The index**: - The below index will index any field from the document, + + The following index will index any field from the document, a dynamic-index-field will be created for each field. New fields added to the document after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the field **key**. - E.g., Keys `FirstName` & `LastName` will become the actual dynamic-index-fields. - - - - -{`class Products_ByAnyField_JS(AbstractJavaScriptIndexCreationTask): - def __init__(self): - super().__init__() - # This will index EVERY FIELD under the top level of the document - self.maps = { - """ - map('Products', function (p) { - return { - _: Object.keys(p).map(key => createField(key, p[key], - { indexing: 'Search', storage: true, termVector: null })) - } - }) - """ - } -`} - - - + The actual dynamic-index-field name on which you can query will be the field **key**. + E.g., Keys `FirstName` & `LastName` will become the actual dynamic-index-fields. + + The example uses a JavaScript index because it enumerates all top-level document fields at indexing time with `Object.keys(...)`. + LINQ indexes typically create dynamic fields from a known enumerable structure, such as a dictionary or collection. + + + + ```python + class Products_ByAnyField_JS(AbstractJavaScriptIndexCreationTask): + def __init__(self): + super().__init__() + # This will index EVERY FIELD under the top level of the document + self.maps = [ + """ + map('Products', function (p) { + return { + _: Object.keys(p).map(key => createField(key, p[key])) + }; + }) + """ + ] + ``` + + * **The query**: - * To get all documents with some 'LastName' use: - - - -{`# 'last_name' is a dynamic-index-field that was indexed from the document -matching_documents = list( - session.query_index_type(Products_ByAnyField_JS, Product).where_equals("last_name", "Doe") -) -`} - - - - -{`// 'LastName' is a dynamic-index-field that was indexed from the document -from index 'Products/ByAnyField/JS' where LastName = "Doe" -`} - - - + + To get all documents with a specific _LastName_ use: + + + + ```python + matching_documents = list( + session.advanced + .document_query_from_index_type(Products_ByAnyField_JS, Product) + # 'LastName' is a dynamic-index-field that was indexed from the document + .where_equals("LastName", "Doe") + ) + ``` + + + ```sql + // 'LastName' is a dynamic-index-field that was indexed from the document + from index 'Products/ByAnyField/JS' where LastName = "Doe" + ``` + + + + + + + + + + + +### Example - basic + + +The following example shows: + * Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. + * Documents can then be queried based on those indexed values. + * For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---list) below. + + +* **The document**: + + + ```python + class Product: + def __init__(self, Id: str = None, ProductType: str = None, PricePerUnit: int = None): + self.Id = Id + + # The VALUE of ProductType will be dynamically indexed + self.ProductType = ProductType + self.PricePerUnit = PricePerUnit + ``` + + + + ```json + // Sample document content + { + "ProductType": "Electronics", + "PricePerUnit": 23 + } + ``` + + +* **The index**: + + The following index will index the **value** of document field 'ProductType'. + + This value will be the dynamic-index-field name on which you can query. + E.g., Field value `Electronics` will be the dynamic-index-field. + + + + ```python + class Products_ByProductType(AbstractIndexCreationTask): + def __init__(self): + super().__init__() + self.map = ( + "from p in docs.Products select new { " + # Call 'CreateField' to generate the dynamic-index-fields + # The field name will be the value of document field 'ProductType' + # The field terms will be derived from document field 'PricePerUnit' + "_ = CreateField(p.ProductType, p.PricePerUnit) " + "}" + ) + ``` + + + ```python + class Products_ByProductType_JS(AbstractJavaScriptIndexCreationTask): + def __init__(self): + super().__init__() + self.maps = [ + """ + map('Products', function (p) { + return { + // The field name will be the value of document field 'ProductType' + // The field terms will be derived from document field 'PricePerUnit' + _: createField(p.ProductType, p.PricePerUnit) + }; + }) + """ + ] + ``` + + + +* **The query**: + + To get all documents of some product type having a specific price per unit use: + + + + ```python + matching_documents = list( + session.advanced + .document_query_from_index_type(Products_ByProductType, Product) + # 'Electronics' is the dynamic-index-field that was indexed + # from document field 'ProductType' + .where_equals("Electronics", 23) + ) + ``` + + + ```sql + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'ProductType' + from index 'Products/ByProductType' where Electronics = 23 + ``` + + + + + + + +### Example - list + + +The following example allows you to: + * Index **values** from items in a list + * After index is deployed, any item added to this list in the document will be dynamically indexed as well. + + +* **The document**: + + + ```python + class Attribute: + def __init__(self, PropName: str = None, PropValue: str = None): + self.PropName = PropName + self.PropValue = PropValue + + + class Product: + def __init__(self, Id: str = None, Name: str = None, Attributes: List[Attribute] = None): + self.Id = Id + self.Name = Name + + # For each element in this list, + # the VALUE of property 'PropName' will be dynamically indexed. + # e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields. + self.Attributes = Attributes + ``` + + + + ```json + // Sample document content + { + "Name": "SomeName", + "Attributes": [ + { + "PropName": "Color", + "PropValue": "Blue" + }, + { + "PropName": "Width", + "PropValue": "10" + }, + { + "PropName": "Length", + "PropValue": "20" + }, + // ... + ] + } + ``` + + +* **The index**: + + The following index will create a dynamic-index-field per item in the document's `Attributes` list. + New items added to the Attributes list after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the item's PropName **value**. + E.g., 'PropName' value `Width` will be a dynamic-index-field. + + + + ```python + class Attributes_ByName(AbstractIndexCreationTask): + def __init__(self): + super().__init__() + self.map = ( + "from a in docs.Products select new { " + # Define the dynamic-index-fields by calling 'CreateField' + # A dynamic-index-field will be generated for each item in 'Attributes' + + # For each item, the field name will be the value of field 'PropName' + # The field terms will be derived from field 'PropValue' + "_ = a.Attributes.Select(item => CreateField(item.PropName, item.PropValue)), " + + # A regular index field can be defined as well: + "Name = a.Name " + "}" + ) + ``` + + + ```python + class Attributes_ByName_JS(AbstractJavaScriptIndexCreationTask): + def __init__(self): + super().__init__() + self.maps = [ + """ + map('Products', function (p) { + return { + // For each item, + // the field name will be the value of field 'PropName' + // The field terms will be derived from field 'PropValue' + _: p.Attributes.map(item => createField(item.PropName, item.PropValue)), + + // A regular index field can be defined as well: + Name: p.Name + }; + }) + """ + ] + ``` + + + +* **The query**: + + To get all documents matching a specific attribute property use: + + + + ```python + matching_documents = list( + session.advanced + .document_query_from_index_type(Attributes_ByName, Product) + # 'Width' is a dynamic-index-field that was indexed from the Attributes list + .where_equals("Width", 10) + ) + ``` + + + ```sql + // 'Width' is a dynamic-index-field that was indexed from the Attributes list + from index 'Attributes/ByName' where Width = 10 + ``` + + + + + + + + + + + +Dynamic fields can be indexed for [Full-text search](../../indexes/querying/searching.mdx) by setting `FieldIndexing.Search` in `CreateFieldOptions`. +* **At indexing time**: + At indexing time, RavenDB analyzes the dynamic field value and creates the indexed terms. + The analyzer used at indexing time is resolved from the index definition. + You can configure an analyzer for a specific generated field name, or configure `ALL_FIELDS` as a fallback. + See [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) below. + * If the generated dynamic field name has an explicit analyzer configuration, that analyzer is used. + * Otherwise, if `ALL_FIELDS` has an analyzer configuration, RavenDB uses it as a fallback. + * If no analyzer is configured for the field, RavenDB uses the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). -## Indexing documents fields VALUES +* **At querying time**: + For full-text search to return the expected results, the analyzer used at indexing time and the analyzer used for the query term should produce compatible terms. -## Example - basic + When a `search()` query targets a dynamic field that does **not** have an explicit analyzer configuration for its generated field name, + RavenDB uses the [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) to analyze the query term by default. - + Set the [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) + configuration key to `true` to make RavenDB use the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) as the fallback analyzer for the query term instead. -* Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. -* Documents can then be queried based on those indexed values. + This configuration can be set at different scopes: server-wide, per database, or per index. + The example below sets it at the index scope, in the index definition. + + For a summary of how RavenDB resolves the analyzer used for the query term, + see [Which analyzer is used for the query term](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) below. + +--- + +The following example indexes localized product descriptions as dynamic fields and queries one of those generated fields using full-text search: + +* **The document**: + + + ```python + class Product: + def __init__(self, Id: str = None, Descriptions: Dict[str, str] = None): + self.Id = Id + self.Descriptions = Descriptions + ``` + + + + ```json + // Sample document content + { + "Descriptions": { + "English": "North wind jacket", + "French": "Veste vent du nord" + } + } + ``` + -* **The document**: - - -{`class Product: - def __init__(self, Id: str = None, product_type: str = None, price_per_unit: float = None): - self.Id = Id - - # The VALUE of ProductType will be dynamically indexed - self.product_type = product_type - self.price_per_unit = price_per_unit -`} - - +* **The index**: - - -{`// Sample document content -\{ - "ProductType": "Electronics", - "PricePerUnit": 23 -\} -`} - - + The following index creates one dynamic field for each entry in the `Descriptions` dictionary. + For example, the key `English` creates the dynamic field `Description_English`. -* **The index**: - The below index will index the **value** of document field 'ProductType'. - - This value will be the dynamic-index-field name on which you can query. - E.g., Field value `Electronics` will be the dynamic-index-field. + Each generated dynamic field is indexed for **full-text search**. + + + + ```python + class Products_ByLocalizedDescription(AbstractIndexCreationTask): + USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = ( + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery" + ) + + def __init__(self): + super().__init__() + self.map = ( + "from product in docs.Products select new { " + " _ = product.Descriptions.Select(x => " + " CreateField(" + " \"Description_\" + x.Key, " + " x.Value, " + # Index each generated dynamic field for FTS + " new CreateFieldOptions { " + " Indexing = FieldIndexing.Search, " + " Storage = FieldStorage.No " + " })) " + "}" + ) + + self._store_all_fields(FieldStorage.NO) + + # Set this configuration key to true so that search() queries on dynamic fields + # will use the 'Default Search Analyzer' when the generated field name + # has no explicit analyzer configuration. + self.configuration = { + Products_ByLocalizedDescription.USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS: "true" + } + ``` + + + ```python + class Products_ByLocalizedDescription_JS(AbstractJavaScriptIndexCreationTask): + USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = ( + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery" + ) + + def __init__(self): + super().__init__() + self.maps = [ + """ + map('Products', function (product) { + return { + _: Object.keys(product.Descriptions).map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + // Index each generated dynamic field for FTS + indexing: 'Search', + storage: 'No' + }); + }) + }; + }) + """ + ] + + # Apply a storage default to every generated field + self.fields = { + constants.Documents.Indexing.Fields.ALL_FIELDS: + IndexFieldOptions(storage=FieldStorage.NO) + } + + # Set this configuration key to true so that search() queries on dynamic fields + # will use the 'Default Search Analyzer' when the generated field name + # has no explicit analyzer configuration. + self.configuration = { + Products_ByLocalizedDescription_JS.USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS: "true" + } + ``` + + + +* **Full-text search query**: + + Query the generated dynamic field by its field name. + + In this example, the query targets the generated field `Description_English`. + + Since `Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery` is set to `true`, + the searched term `"north wind"` is analyzed with the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + + + + ```python + results = list( + session.advanced + .document_query_from_index_type(Products_ByLocalizedDescription, Product) + .search("Description_English", "north wind") + ) + ``` + + + + ```sql + from index "Products/ByLocalizedDescription" + where search(Description_English, "north wind") + ``` + + + +--- + + + +### Configuring analyzers for dynamic fields + +`CreateFieldOptions` does **not** let you specify a custom analyzer directly. +However, you can still configure which analyzer RavenDB will use for dynamic fields in the index definition by either: + +* Configuring an analyzer for a specific generated dynamic field name. +* Configuring `ALL_FIELDS` as a fallback for fields that do not have their own explicit analyzer configuration. + +Use `ALL_FIELDS` with care, since it can affect any field that does not have its own explicit analyzer configuration. + +--- + +#### Configure analyzer for a specific dynamic field - -{`class Products_ByProductType(AbstractIndexCreationTask): +```python +class Products_ByLocalizedDescription(AbstractIndexCreationTask): def __init__(self): super().__init__() + self.map = ( + "from product in docs.Products select new { " + " _ = product.Descriptions.Select(x => " + " CreateField(" + " \"Description_\" + x.Key, " + " x.Value, " + " new CreateFieldOptions { " + " Indexing = FieldIndexing.Search, " + " Storage = FieldStorage.No " + " })) " + "}" + ) - # Call 'CreateField' to generate the dynamic-index-fields - # The field name will be the value of document field 'product_type' - # The field terms will be derived from document field 'price_per_unit' - self.map = "from p in docs.Products select new { _ = CreateField(p.product_type, p.price_per_unit)}" -`} - + self._store_all_fields(FieldStorage.NO) + + # Configure an analyzer for a known generated dynamic field name + # ============================================================== + self._analyze("Description_English", "StopAnalyzer") +``` - - -{`class Products_ByProductType_JS(AbstractJavaScriptIndexCreationTask): + +```python +class Products_ByLocalizedDescription_JS(AbstractJavaScriptIndexCreationTask): def __init__(self): super().__init__() - self.maps = { + self.maps = [ """ - map('Products', function (p) { + map('Products', function (product) { return { - _: createField(p.product_type, p.price_per_unit, - { indexing: 'Search', storage: true, termVector: null }) + _: Object.keys(product.Descriptions).map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; }) """ + ] + + # Configure an analyzer for a known generated dynamic field name + # ============================================================== + self.fields = { + constants.Documents.Indexing.Fields.ALL_FIELDS: + IndexFieldOptions(storage=FieldStorage.NO), + "Description_English": + IndexFieldOptions(analyzer="StopAnalyzer") } -`} - - - - -* **The query**: - * To get all documents of some product type having a specific price per unit use: - - - -{`# 'electronics' is the dynamic-index-field that was indexed from the document 'product_type' -matching_documents = list( - session.advanced.document_query_from_index_type(Products_ByProductType, Product).where_equals( - "electronics", 23 - ) -) -`} - - - - -{`// 'Electronics' is the dynamic-index-field that was indexed from document field 'ProductType' -from index 'Products/ByProductType' where Electronics = 23 -`} - +``` -## Example - list - - - -* Index **values** from items in a list -* After index is deployed, any item added this list in the document will be dynamically indexed as well. - - - -* **The document**: - - -{`class Attribute: - def __init__(self, prop_name: str = None, prop_value: str = None): - self.prop_name = prop_name - self.prop_value = prop_value - - -class Product: - def __init__(self, Id: str = None, name: str = None, attributes: List[Attribute] = None): - self.Id = Id - self.name = name - # For each element in this list, the VALUE of property 'prop_name' will be dynamically indexed - # e.g. color, width, length (in ex. below) will become dynamic-index-field - self.attributes = attributes -`} - - - - - -{`// Sample document content -\{ -Name": "SomeName", -Attributes": [ - \{ - "PropName": "Color", - "PropValue": "Blue" - \}, - \{ - "PropName": "Width", - "PropValue": "10" - \}, - \{ - "PropName": "Length", - "PropValue": "20" - \}, - ... - -\} -`} - - - -* **The index**: - The below index will create a dynamic-index-field per item in the document's `Attributes` list. - New items added to the Attributes list after index creation time will be dynamically indexed as well. - - The actual dynamic-index-field name on which you can query will be the item's PropName **value**. - E.g., 'PropName' value `width` will be a dynamic-index-field. +#### Configure a fallback analyzer for all fields - -{`class Attributes_ByName(AbstractIndexCreationTask): +```python +class Products_ByLocalizedDescription(AbstractIndexCreationTask): def __init__(self): super().__init__() self.map = ( - "from a in docs.Products select new " - "{ _ = a.attributes.Select( item => CreateField(item.prop_name, item.prop_value)), name = a.name " + "from product in docs.Products select new { " + " _ = product.Descriptions.Select(x => " + " CreateField(" + " \"Description_\" + x.Key, " + " x.Value, " + " new CreateFieldOptions { " + " Indexing = FieldIndexing.Search, " + " Storage = FieldStorage.No " + " })) " "}" ) -`} - + + self._store_all_fields(FieldStorage.NO) + + # Use this as a fallback for fields that do not have + # their own explicit analyzer configuration + # ================================================== + self._analyze(constants.Documents.Indexing.Fields.ALL_FIELDS, "StopAnalyzer") +``` - - -{`class Attributes_ByName_JS(AbstractJavaScriptIndexCreationTask): + +```python +class Products_ByLocalizedDescription_JS(AbstractJavaScriptIndexCreationTask): def __init__(self): super().__init__() - self.maps = { + self.maps = [ """ - map('Products', function (p) { + map('Products', function (product) { return { - _: p.Attributes.map(item => createField(item.PropName, item.PropValue, - { indexing: 'Search', storage: true, termVector: null })), - Name: p.Name + _: Object.keys(product.Descriptions).map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; }) """ + ] + + # Use this as a fallback for fields that do not have + # their own explicit analyzer configuration + # ================================================== + self.fields = { + constants.Documents.Indexing.Fields.ALL_FIELDS: + IndexFieldOptions(storage=FieldStorage.NO, analyzer="StopAnalyzer") } -`} - - - - -* **The query**: - To get all documents matching a specific attribute property use: - - - -{`matching_documents = list( - session.advanced.document_query_from_index_type(Attributes_ByName, Product).where_equals( - "width", 10 - ) -) -`} - - - - -{`// 'Width' is a dynamic-index-field that was indexed from the Attributes list -from index 'Attributes/ByName' where Width = 10 -`} - +``` + + + + +### Which analyzer is used for the query term? + +The following applies when a `search()` query targets a **dynamic field**: + +| Index configuration | [Configuration key](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) | Analyzer used for the query term | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| No analyzer is explicitly configured for the generated field name | `false` | The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) | +| No analyzer is explicitly configured for the generated field name | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `false` | The analyzer resolved by the normal field fallback path,
e.g. `WhitespaceAnalyzer` | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| The generated field name is explicitly configured,
e.g. `self._analyze("Description_English", "StopAnalyzer")` | `false` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +| The generated field name is explicitly configured,
e.g. `self._analyze("Description_English", "StopAnalyzer")` | `true` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | + +
+
-## CreateField syntax + #### Syntax for Index: - - -{`object CreateField(string name, object value); + +```python +CreateField(name, value) -object CreateField(string name, object value, bool stored, bool analyzed); +CreateField(name, value, stored, analyzed) -object CreateField(string name, object value, CreateFieldOptions options); -`} - +CreateField(name, value, CreateFieldOptions) +``` #### Syntax for JavaScript-index: - - -{`createField(fieldName, fieldValue, options); // returns object -`} - + +```js +createField(fieldName, fieldValue, options); // returns object +``` -| Parameters | Type | Description | -|------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **name** | `string` | Name of the dynamic-index-field | -| **value** | `object` | Value of the dynamic-index-field
The field Terms are derived from this value. | -| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`False` - will set `FieldStorage.NO` (default value)
`True` - will set `FieldStorate.YES` | -| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`None` - `FieldIndexing.Default` (default value)
`False` - `FieldIndexing.Exact`
`True` - `FieldIndexing.Search` | -| **options** | `CreateFieldOptions` | Dynamic-index-field options | +| Parameters | Type | Description | +|----------------|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **fieldName** | `string` | Name of the dynamic-index-field | +| **fieldValue** | `object` | Value of the dynamic-index-field
The field terms are derived from this value. | +| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorage.Yes` | +| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing.Default` (default value)
`false` - `FieldIndexing.Exact`
`true` - `FieldIndexing.Search` | +| **options** | `CreateFieldOptions` | Dynamic-index-field options | -| CreateFieldOptions | | | -|--------------------|--------------------|----------------------------------------------------------------------------| +| CreateFieldOptions | | | +|--------------------|--------------------|-----------------------------------------------------------------------------------| | **Storage** | `FieldStorage?` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | -| **Indexing** | `FieldIndexing?` | Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | -| **TermVector** | `FieldTermVector?` | Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | +| **Indexing** | `FieldIndexing?` | Sets the field indexing behavior.
Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | +| **TermVector** | `FieldTermVector?` | Sets the field term-vector behavior.
Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | @@ -496,17 +892,15 @@ object CreateField(string name, object value, CreateFieldOptions options); +
+ + - -## Indexed fields & terms view - -The generated dynamic-index-fields and their indexed terms can be viewed in the **Terms View**. -Below are sample index fields & their terms generated from the last example. +The generated dynamic index fields and their indexed terms can be viewed in the **Terms View** in Studio. +Below are sample index fields & their terms generated from [this example](../../indexes/using-dynamic-fields.mdx#example---list). ![Figure 1. Go to terms view](../assets/dynamic-index-fields-1.png) ![Figure 2. Indexed fields & terms](../assets/dynamic-index-fields-2.png) - - - - + + diff --git a/versioned_docs/version-7.0/indexes/querying/content/_searching-csharp.mdx b/versioned_docs/version-7.0/indexes/querying/content/_searching-csharp.mdx index d20ef5a65e..f0be0e6ffd 100644 --- a/versioned_docs/version-7.0/indexes/querying/content/_searching-csharp.mdx +++ b/versioned_docs/version-7.0/indexes/querying/content/_searching-csharp.mdx @@ -16,11 +16,13 @@ import CodeBlock from '@theme/CodeBlock'; See examples below. * You can configure which analyzer will be used to tokenize this field. - See [selecting an analyzer](../../../indexes/using-analyzers.mdx#selecting-an-analyzer-for-a-field). + Learn more in [Setting analyzer for index-field](../../../indexes/using-analyzers.mdx#setting-analyzer-for-index-field). + * In this article: * [Indexing single field for FTS](../../../indexes/querying/searching.mdx#indexing-single-field-for-fts) * [Indexing multiple fields for FTS](../../../indexes/querying/searching.mdx#indexing-multiple-fields-for-fts) - * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-(using-asjson)) + * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-using-asjson) + * [Indexing dynamic fields for FTS](../../../indexes/querying/searching.mdx#indexing-dynamic-fields-for-fts) * [Boosting search results](../../../indexes/querying/searching.mdx#boosting-search-results) * [Searching with wildcards](../../../indexes/querying/searching.mdx#searching-with-wildcards) * [When using RavenStandardAnalyzer or StandardAnalyzer or NGramAnalyzer](../../../indexes/querying/searching.mdx#when-usingoror) @@ -28,6 +30,7 @@ import CodeBlock from '@theme/CodeBlock'; * [When using the Exact analyzer](../../../indexes/querying/searching.mdx#when-using-the-exact-analyzer) + ## Indexing single field for FTS #### The index: @@ -262,7 +265,7 @@ where (search(EmployeeData, "Manager") or search(EmployeeData, "French Spanish", ## Indexing all fields for FTS (using AsJson) -* To search across ALL fields in a document without defining each one explicitly, +* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method in the _Map_ function to extract all property values and index them in a single searchable field. * This approach makes the index robust to changes in the document schema. @@ -377,7 +380,14 @@ where search(AllValues, "tofu") +## Indexing dynamic fields for FTS + +* [Dynamic index fields](../../../indexes/using-dynamic-fields.mdx) are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time. +* Dynamic index fields can be configured for full-text search just like regular index fields. + Learn more in [Indexing dynamic fields for full-text search](../../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search). ## Boosting search results diff --git a/versioned_docs/version-7.0/indexes/querying/content/_searching-java.mdx b/versioned_docs/version-7.0/indexes/querying/content/_searching-java.mdx index 7d85246ced..4accf3f512 100644 --- a/versioned_docs/version-7.0/indexes/querying/content/_searching-java.mdx +++ b/versioned_docs/version-7.0/indexes/querying/content/_searching-java.mdx @@ -388,4 +388,11 @@ where search(allValues, "tofu") +## Indexing dynamic fields for FTS +* [Dynamic index fields](../../../indexes/using-dynamic-fields.mdx) are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time. + +* Dynamic index fields can be configured for full-text search just like regular index fields. + Learn more in [Indexing dynamic fields for full-text search](../../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search). \ No newline at end of file diff --git a/versioned_docs/version-7.0/indexes/querying/content/_searching-nodejs.mdx b/versioned_docs/version-7.0/indexes/querying/content/_searching-nodejs.mdx index 4ca66b5877..cb73bfd7c0 100644 --- a/versioned_docs/version-7.0/indexes/querying/content/_searching-nodejs.mdx +++ b/versioned_docs/version-7.0/indexes/querying/content/_searching-nodejs.mdx @@ -5,7 +5,7 @@ import CodeBlock from '@theme/CodeBlock'; -* Prior to reading this article, please refer to [full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) +* Prior to reading this article, please refer to [Full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) to learn about the `search` method. * **All capabilities** provided by `search` with a dynamic query can also be used when querying a static-index. @@ -17,11 +17,13 @@ import CodeBlock from '@theme/CodeBlock'; See examples below. * You can configure which analyzer will be used to tokenize this field. - See [selecting an analyzer](../../../indexes/using-analyzers.mdx#selecting-an-analyzer-for-a-field). + Learn more in [Setting analyzer for index-field](../../../indexes/using-analyzers.mdx#setting-analyzer-for-index-field). + * In this article: * [Indexing single field for FTS](../../../indexes/querying/searching.mdx#indexing-single-field-for-fts) * [Indexing multiple fields for FTS](../../../indexes/querying/searching.mdx#indexing-multiple-fields-for-fts) - * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-(using-asjson)) + * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-using-asjson) + * [Indexing dynamic fields for FTS](../../../indexes/querying/searching.mdx#indexing-dynamic-fields-for-fts) * [Boosting search results](../../../indexes/querying/searching.mdx#boosting-search-results) * [Searching with wildcards](../../../indexes/querying/searching.mdx#searching-with-wildcards) * [When using RavenStandardAnalyzer or StandardAnalyzer or NGramAnalyzer](../../../indexes/querying/searching.mdx#when-usingoror) @@ -29,6 +31,7 @@ import CodeBlock from '@theme/CodeBlock'; * [When using the Exact analyzer](../../../indexes/querying/searching.mdx#when-using-the-exact-analyzer) + ## Indexing single field for FTS #### The index: @@ -163,7 +166,7 @@ where (search(employeeData, "Manager") or search(employeeData, "French Spanish", ## Indexing all fields for FTS (using AsJson) -* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, +* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, which is available when using **a C# LINQ string** that is assigned to the `map` property in the Node.js index class, as shown in the example below. @@ -229,6 +232,14 @@ where search(AllValues, "tofu") +## Indexing dynamic fields for FTS + +* [Dynamic index fields](../../../indexes/using-dynamic-fields.mdx) are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time. + +* Dynamic index fields can be configured for full-text search just like regular index fields. + Learn more in [Indexing dynamic fields for full-text search](../../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search). ## Boosting search results diff --git a/versioned_docs/version-7.0/indexes/querying/content/_searching-php.mdx b/versioned_docs/version-7.0/indexes/querying/content/_searching-php.mdx index 55261ce19f..510a6f8752 100644 --- a/versioned_docs/version-7.0/indexes/querying/content/_searching-php.mdx +++ b/versioned_docs/version-7.0/indexes/querying/content/_searching-php.mdx @@ -5,7 +5,7 @@ import CodeBlock from '@theme/CodeBlock'; -* Prior to reading this article, please refer to [full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) +* Prior to reading this article, please refer to [Full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) to learn about the `search` method. * **All capabilities** provided by `search` with a dynamic query can also be used when querying a static-index. @@ -17,14 +17,17 @@ import CodeBlock from '@theme/CodeBlock'; See examples below. * You can configure which analyzer will be used to tokenize this field. - See [selecting an analyzer](../../../indexes/using-analyzers.mdx#selecting-an-analyzer-for-a-field). + Learn more in [Setting analyzer for index-field](../../../indexes/using-analyzers.mdx#setting-analyzer-for-index-field). + * In this article: * [Indexing single field for FTS](../../../indexes/querying/searching.mdx#indexing-single-field-for-fts) * [Indexing multiple fields for FTS](../../../indexes/querying/searching.mdx#indexing-multiple-fields-for-fts) - * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-(using-asjson)) + * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-using-asjson) + * [Indexing dynamic fields for FTS](../../../indexes/querying/searching.mdx#indexing-dynamic-fields-for-fts) * [Boosting search results](../../../indexes/querying/searching.mdx#boosting-search-results) + ## Indexing single field for FTS #### The index: @@ -253,7 +256,7 @@ where (search(EmployeeData, "Manager") or search(EmployeeData, "French Spanish", ## Indexing all fields for FTS (using AsJson) -* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, +* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, which is available in the **C# LINQ string** that is assigned to the `map` property in the PHP index class, as shown in the example below. @@ -332,6 +335,15 @@ where search(allValues, "tofu") +## Indexing dynamic fields for FTS + +* [Dynamic index fields](../../../indexes/using-dynamic-fields.mdx) are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time. + +* Dynamic index fields can be configured for full-text search just like regular index fields. + Learn more in [Indexing dynamic fields for full-text search](../../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search). + ## Boosting search results diff --git a/versioned_docs/version-7.0/indexes/querying/content/_searching-python.mdx b/versioned_docs/version-7.0/indexes/querying/content/_searching-python.mdx index f06c29d94f..88c6aa452f 100644 --- a/versioned_docs/version-7.0/indexes/querying/content/_searching-python.mdx +++ b/versioned_docs/version-7.0/indexes/querying/content/_searching-python.mdx @@ -5,7 +5,7 @@ import CodeBlock from '@theme/CodeBlock'; -* Prior to reading this article, please refer to [full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) +* Prior to reading this article, please refer to [Full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) to learn about the `search` method. * **All capabilities** provided by `search` with a dynamic query can also be used when querying a static-index. @@ -17,14 +17,17 @@ import CodeBlock from '@theme/CodeBlock'; See examples below. * You can configure which analyzer will be used to tokenize this field. - See [selecting an analyzer](../../../indexes/using-analyzers.mdx#selecting-an-analyzer-for-a-field). + Learn more in [Setting analyzer for index-field](../../../indexes/using-analyzers.mdx#setting-analyzer-for-index-field). + * In this article: * [Indexing single field for FTS](../../../indexes/querying/searching.mdx#indexing-single-field-for-fts) * [Indexing multiple fields for FTS](../../../indexes/querying/searching.mdx#indexing-multiple-fields-for-fts) - * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-(using-asjson)) + * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-using-asjson) + * [Indexing dynamic fields for FTS](../../../indexes/querying/searching.mdx#indexing-dynamic-fields-for-fts) * [Boosting search results](../../../indexes/querying/searching.mdx#boosting-search-results) + ## Indexing single field for FTS #### The index: @@ -167,7 +170,7 @@ where (search(EmployeeData, "Manager") or search(EmployeeData, "French Spanish", ## Indexing all fields for FTS (using AsJson) -* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, +* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, which is available in the **C# LINQ string** that is assigned to the `map` property in the Python index class, as shown in the example below. @@ -238,6 +241,14 @@ where search(all_values, "tofu") +## Indexing dynamic fields for FTS + +* [Dynamic index fields](../../../indexes/using-dynamic-fields.mdx) are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time. + +* Dynamic index fields can be configured for full-text search just like regular index fields. + Learn more in [Indexing dynamic fields for full-text search](../../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search). ## Boosting search results diff --git a/versioned_docs/version-7.0/indexes/using-dynamic-fields.mdx b/versioned_docs/version-7.0/indexes/using-dynamic-fields.mdx index e9ffb33a07..e465ba5431 100644 --- a/versioned_docs/version-7.0/indexes/using-dynamic-fields.mdx +++ b/versioned_docs/version-7.0/indexes/using-dynamic-fields.mdx @@ -1,7 +1,8 @@ --- -title: "Indexes: Dynamic Index Fields" -sidebar_label: Dynamic Fields -sidebar_position: 28 +title: "Dynamic Index Fields" +sidebar_label: "Dynamic Index Fields" +description: "Index document fields dynamically in RavenDB when field names are unknown at index definition time, using the CreateField method." +sidebar_position: 27 supported_languages: ["csharp", "java", "python", "php", "nodejs"] see_also: - title: "Boosting" @@ -31,7 +32,6 @@ import UsingDynamicFieldsPython from './content/_using-dynamic-fields-python.mdx import UsingDynamicFieldsPhp from './content/_using-dynamic-fields-php.mdx'; import UsingDynamicFieldsNodejs from './content/_using-dynamic-fields-nodejs.mdx'; - diff --git a/versioned_docs/version-7.0/server/configuration/indexing-configuration.mdx b/versioned_docs/version-7.0/server/configuration/indexing-configuration.mdx index a3f9fce727..7bde291911 100644 --- a/versioned_docs/version-7.0/server/configuration/indexing-configuration.mdx +++ b/versioned_docs/version-7.0/server/configuration/indexing-configuration.mdx @@ -102,6 +102,7 @@ import LanguageContent from "@site/src/components/LanguageContent"; [Indexing.OrderByTicksAutomaticallyWhenDatesAreInvolved](../../server/configuration/indexing-configuration.mdx#indexingorderbyticksautomaticallywhendatesareinvolved) [Indexing.QueryClauseCache.Disabled](../../server/configuration/indexing-configuration.mdx#indexingqueryclausecachedisabled) [Indexing.QueryClauseCache.RepeatedQueriesTimeFrameInSec](../../server/configuration/indexing-configuration.mdx#indexingqueryclausecacherepeatedqueriestimeframeinsec) + [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) [Indexing.ScratchSpaceLimitInMb](../../server/configuration/indexing-configuration.mdx#indexingscratchspacelimitinmb) [Indexing.Static.SearchEngineType](../../server/configuration/indexing-configuration.mdx#indexingstaticsearchenginetype) [Indexing.Throttling.TimeIntervalInMs](../../server/configuration/indexing-configuration.mdx#indexingthrottlingtimeintervalinms) @@ -988,6 +989,33 @@ Queries that repeat within this time frame will be considered worth caching. +## Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery + +* This setting controls which analyzer is used for the **query term** when a **`search()` query** targets a [Dynamic index field](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + that was indexed for full-text search, and no analyzer is explicitly configured for that field in the index definition. + + * `false` (default): + The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) is used to analyze the **query term**. + This analyzer can be configured via the [Indexing.Analyzers.Default](../../server/configuration/indexing-configuration.mdx#indexinganalyzersdefault) configuration key. + + * `true`: + The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) is used to analyze the **query term**. + This analyzer can be configured via the [Indexing.Analyzers.Search.Default](../../server/configuration/indexing-configuration.mdx#indexinganalyzerssearchdefault) configuration key. + +* See the table in [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + for the complete analyzer-selection rules based on this configuration key and the analyzer configured for the dynamic index field. + +* This configuration applies to both the Lucene and Corax search engines. + The default is `false` to preserve backward compatibility. + +--- + +- **Type**: `bool` +- **Default**: `false` +- **Scope**: Server-wide, or per database, or per index + + + ## Indexing.ScratchSpaceLimitInMb * Amount of scratch space in megabytes that we allow to use for the index storage. diff --git a/versioned_docs/version-7.1/indexes/assets/dynamic-index-fields-1.png b/versioned_docs/version-7.1/indexes/assets/dynamic-index-fields-1.png index b13d9076f89e3363e97e2a4a6c01aecc4d101b67..a20adda0ece521b8d27172708c0a11aeea13f887 100644 GIT binary patch literal 51136 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 literal 26390 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 diff --git a/versioned_docs/version-7.1/indexes/assets/dynamic-index-fields-2.png b/versioned_docs/version-7.1/indexes/assets/dynamic-index-fields-2.png index dd2b4082eaa04bd406f9fac609adccde69e21b42..c5b1bc14bca3b86dd281347ae4c56e6545ff1bba 100644 GIT binary patch literal 67121 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%0Dmpj<&g2>Q& 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 X>}(5)fwv}4d^z}m?)_4g$D#iV5#_1q literal 43570 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

MYQ6EJKjSRtlshL!QySY5<Da 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 -* In RavenDB different documents can have different shapes. - Documents are schemaless - new fields can be added or removed as needed. - -* For such dynamic data, you can define indexes with **dynamic-index-fields**. - -* This allows querying the index on fields that aren't yet known at index creation time, - which is very useful when working on highly dynamic systems. - -* Any value type can be indexed, string, number, date, etc. - -* An index definition can contain both dynamic-index-fields and regular-index-fields. - -* In this page: - - * [Indexing documents fields KEYS](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-keys) - * [Example - index any field under object](../../indexes/using-dynamic-fields.mdx#example---index-any-field-under-object) - * [Example - index any field](../../indexes/using-dynamic-fields.mdx#example---index-any-field) - * [Indexing documents fields VALUES](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-values) - * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) - * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) - * [CreateField syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) - * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields--terms-view) +* In RavenDB, different documents can have different shapes. + Documents are schemaless - new fields can be added or removed as needed. + For such dynamic data, you can define indexes with **dynamic-index-fields**. + +* Dynamic-index-fields are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time, + which is very useful when working with highly dynamic systems. + +* Any value type can be indexed, string, number, date, etc. + An index definition can contain both dynamic-index-fields and regular-index-fields. + +* In this article: + * [Indexing field KEYS](../../indexes/using-dynamic-fields.mdx#indexing-field-keys) + * [Example - index every field under an object](../../indexes/using-dynamic-fields.mdx#example---index-every-field-under-an-object) + * [Example - index every field](../../indexes/using-dynamic-fields.mdx#example---index-every-field) + * [Indexing field VALUES](../../indexes/using-dynamic-fields.mdx#indexing-field-values) + * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) + * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) + * [Indexing dynamic fields for full-text search](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + * [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) + * [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + * [`CreateField` syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) + * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields-terms-view) -## Indexing documents fields KEYS - -## Example - index any field under object - - + -* Index any field that is under the some object from the document. -* After index is deployed, any new field added to the this object will be indexed as well. + + +### Example - index every field under an object + +The following example allows you to: + * Index any field that is under the same object from the document. + * After index is deployed, any new field added to this object will be indexed as well. * **The document**: - - -{`public class Product -\{ - public string Id \{ get; set; \} - - // The KEYS under the Attributes object will be dynamically indexed - // Fields added to this object after index creation time will also get indexed - public Dictionary Attributes \{ get; set; \} -\} -`} - - - - -{`// Sample document content -\{ - "Attributes": \{ - "Color": "Red", - "Size": 42 - \} -\} -`} - - + + + ```csharp + public class Product + { + public string Id { get; set; } + + // The KEYS under the Attributes object will be dynamically indexed + // Fields added to this object after index creation time will also get indexed + public Dictionary Attributes { get; set; } + } + ``` + + + + ```json + // Sample document content + { + "Attributes": { + "Color": "Red", + "Size": 42 + } + } + ``` + * **The index**: - The below index will index any field under the `Attributes` object from the document, + + The following index will index any field under the `Attributes` object from the document, a dynamic-index-field will be created for each such field. New fields added to the object after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the attribute field **key**. - E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. + The actual dynamic-index-field name on which you can query will be the attribute field **key**. + E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. - - - -{`public class Products_ByAttributeKey : AbstractIndexCreationTask -{ - public Products_ByAttributeKey() + + + ```csharp + public class Products_ByAttributeKey : AbstractIndexCreationTask { - Map = products => from p in products - select new - { - // Call 'CreateField' to generate dynamic-index-fields from the Attributes object keys - // Using '_' is just a convention. Any other string can be used instead of '_' - - // The actual field name will be item.Key - // The actual field terms will be derived from item.Value - _ = p.Attributes.Select(item => CreateField(item.Key, item.Value)) - }; + public Products_ByAttributeKey() + { + Map = products => from p in products + select new + { + // Call 'CreateField' to generate dynamic-index-fields + // from the Attributes object keys. + + // Using '_' is just a convention. + // Any other string can be used instead of '_' + + // The actual field name will be 'item.Key' + // The actual field terms will be derived from 'item.Value' + _ = p.Attributes.Select(item => CreateField(item.Key, item.Value)) + }; + } } -} -`} - - - - -{`public class Products_ByAttributeKey_JS : AbstractJavaScriptIndexCreationTask -{ - public Products_ByAttributeKey_JS() + ``` + + + ```csharp + public class Products_ByAttributeKey_JS : AbstractJavaScriptIndexCreationTask { - Maps = new HashSet + public Products_ByAttributeKey_JS() { - @"map('Products', function (p) { - return { - _: Object.keys(p.Attributes).map(key => createField(key, p.Attributes[key], - { indexing: 'Search', storage: true, termVector: null })) - }; - })" - }; + Maps = new HashSet + { + @"map('Products', function (p) { + return { + // The field name will be the key + // The field terms will be derived from the corresponding value + _: Object.keys(p.Attributes) + .map(key => createField(key, p.Attributes[key])) + }; + })" + }; + } } -} -`} - - - - + ``` + + * **The query**: - * You can now query the generated dynamic-index fields. - The `_` property is Not queryable but used only in the index definition syntax. - * To get all documents with some 'Size' use: - - - -{`IList matchingDocuments = session - .Advanced - .DocumentQuery() - // 'Size' is a dynamic-index-field that was indexed from the Attributes object - .WhereEquals("Size", 42) - .ToList(); -`} - - - - -{`// 'Size' is a dynamic-index-field that was indexed from the Attributes object -from index 'Products/ByAttributeKey' where Size = 42 -`} - - - + + You can now query the generated dynamic-index fields. + The `_` property is Not queryable but used only in the index definition syntax. + + To get all documents with a specific _Size_ use: + + + + ```csharp + IList matchingDocuments = session + .Advanced + .DocumentQuery() + // 'Size' is a dynamic-index-field that was indexed from the Attributes object + .WhereEquals("Size", 42) + .ToList(); + ``` + + + ```sql + // 'Size' is a dynamic-index-field that was indexed from the Attributes object + from index 'Products/ByAttributeKey' where Size = 42 + ``` + + + + + Dynamic index fields are queried with _DocumentQuery_ or _RQL_ because the field names (e.g. `Size`, `Color`) + are generated at indexing time and are not properties on the _Product_ class. + + A strongly typed LINQ query such as `Query().Where(p => p.Size == 42)` would not compile. + Use the string-based `WhereEquals("Size", 42)` or RQL `where Size = 42` instead. + -## Example - index any field + - + + +### Example - index every field + +The following example allows you to: * Define an index on a collection **without** needing any common structure between the indexed documents. - * After index is deployed, any new field added to the document will be indexed as well. - + * After the index is deployed, any new field added to any document in the collection will be indexed as well. - + -Consider whether this is really necessary, as indexing every single field can end up costing time and disk space. +* Use this approach with care. +* Indexing every field can increase indexing time, memory usage, and disk space, + especially for large or frequently updated documents. * **The document**: - - -{`public class Product -\{ - public string Id \{ get; set; \} - - // All KEYS in the document will be dynamically indexed - // Fields added to the document after index creation time will also get indexed - public string FirstName \{ get; set; \} - public string LastName \{ get; set; \} - public string Title \{ get; set; \} - // ... -\} -`} - - - - - -{`// Sample document content - \{ - "FirstName": "John", - "LastName": "Doe", - "Title": "Engineer", + + + ```csharp + public class Product + { + public string Id { get; set; } + + // All KEYS in the document will be dynamically indexed + // Fields added to the document after index creation time will also get indexed + public string FirstName { get; set; } + public string LastName { get; set; } + public string Title { get; set; } // ... -\} -`} - - + } + ``` + + + + ```json + // Sample document content + { + "FirstName": "John", + "LastName": "Doe", + "Title": "Engineer", + // ... + } + ``` + * **The index**: - The below index will index any field from the document, + + The following index will index any field from the document, a dynamic-index-field will be created for each field. New fields added to the document after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the field **key**. - E.g., Keys `FirstName` & `LastName` will become the actual dynamic-index-fields. + The actual dynamic-index-field name on which you can query will be the field **key**. + E.g., Keys `FirstName` & `LastName` will become the actual dynamic-index-fields. + + The example uses a JavaScript index because it enumerates all top-level document fields at indexing time with `Object.keys(...)`. + LINQ indexes typically create dynamic fields from a known enumerable structure, such as a dictionary or collection. - - - -{`public class Products_ByAnyField_JS : AbstractJavaScriptIndexCreationTask -{ - public Products_ByAnyField_JS() + + + ```csharp + public class Products_ByAnyField_JS : AbstractJavaScriptIndexCreationTask { - // This will index EVERY FIELD under the top level of the document - Maps = new HashSet + public Products_ByAnyField_JS() { - @"map('Products', function (p) { - return { - _: Object.keys(p).map(key => createField(key, p[key], - { indexing: 'Search', storage: true, termVector: null })) - } - })" - }; + // This will index EVERY FIELD under the top level of the document + Maps = new HashSet + { + @"map('Products', function (p) { + return { + _: Object.keys(p).map(key => createField(key, p[key])) + }; + })" + }; + } } -} -`} - - - + ``` + + * **The query**: - * To get all documents with some 'LastName' use: - - - -{`IList matchingDocuments = session - .Advanced - .DocumentQuery() - // 'LastName' is a dynamic-index-field that was indexed from the document - .WhereEquals("LastName", "Doe") - .ToList(); -`} - - - - -{`// 'LastName' is a dynamic-index-field that was indexed from the document -from index 'Products/ByAnyField/JS' where LastName = "Doe" -`} - - - + + To get all documents with a specific _LastName_ use: + + + + ```csharp + IList matchingDocuments = session + .Advanced + .DocumentQuery() + // 'LastName' is a dynamic-index-field that was indexed from the document + .WhereEquals("LastName", "Doe") + .ToList(); + ``` + + + ```sql + // 'LastName' is a dynamic-index-field that was indexed from the document + from index 'Products/ByAnyField/JS' where LastName = "Doe" + ``` + + + + + + + + + +### Example - basic -## Indexing documents fields VALUES + +The following example shows: + * Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. + * Documents can then be queried based on those indexed values. + * For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---list) below. + -## Example - basic +* **The document**: + + + ```csharp + public class Product + { + public string Id { get; set; } + + // The VALUE of ProductType will be dynamically indexed + public string ProductType { get; set; } + public int PricePerUnit { get; set; } + } + ``` + + + + ```json + // Sample document content + { + "ProductType": "Electronics", + "PricePerUnit": 23 + } + ``` + - +* **The index**: + + The following index will index the **value** of document field 'ProductType'. + + This value will be the dynamic-index-field name on which you can query. + E.g., Field value `Electronics` will be the dynamic-index-field. + + + + ```csharp + public class Products_ByProductType : AbstractIndexCreationTask + { + public Products_ByProductType() + { + Map = products => from p in products + select new + { + // Call 'CreateField' to generate the dynamic-index-fields + // The field name will be the value of document field 'ProductType' + // The field terms will be derived from document field 'PricePerUnit' + _ = CreateField(p.ProductType, p.PricePerUnit) + }; + } + } + ``` + + + ```csharp + public class Products_ByProductType_JS : AbstractJavaScriptIndexCreationTask + { + public Products_ByProductType_JS() + { + Maps = new HashSet + { + @"map('Products', function (p) { + return { + // The field name will be the value of document field 'ProductType' + // The field terms will be derived from document field 'PricePerUnit' + _: createField(p.ProductType, p.PricePerUnit) + }; + })" + }; + } + } + ``` + + + +* **The query**: + + To get all documents of some product type having a specific price per unit use: + + + + ```csharp + IList matchingDocuments = session + .Advanced + .DocumentQuery() + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'ProductType' + .WhereEquals("Electronics", 23) + .ToList(); + ``` + + + ```sql + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'ProductType' + from index 'Products/ByProductType' where Electronics = 23 + ``` + + + + + + -* Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. -* Documents can then be queried based on those indexed values. -* For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---index-a-list-of-properties) below. +### Example - list + +The following example allows you to: + * Index **values** from items in a list + * After index is deployed, any item added to this list in the document will be dynamically indexed as well. * **The document**: - - -{`public class Product -\{ - public string Id \{ get; set; \} - - // The VALUE of ProductType will be dynamically indexed - public string ProductType \{ get; set; \} - public int PricePerUnit \{ get; set; \} -\} -`} - - - - - -{`// Sample document content -\{ - "ProductType": "Electronics", - "PricePerUnit": 23 -\} -`} - - + + + ```csharp + public class Product + { + public string Id { get; set; } + public string Name { get; set; } + + // For each element in this list, + // the VALUE of property 'PropName' will be dynamically indexed. + // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields. + public List Attributes { get; set; } + } + + public class Attribute + { + public string PropName { get; set; } + public string PropValue { get; set; } + } + ``` + + + + ```json + // Sample document content + { + "Name": "SomeName", + "Attributes": [ + { + "PropName": "Color", + "PropValue": "Blue" + }, + { + "PropName": "Width", + "PropValue": "10" + }, + { + "PropName": "Length", + "PropValue": "20" + }, + ... + ] + } + ``` + * **The index**: - The below index will index the **value** of document field 'ProductType'. + + The following index will create a dynamic-index-field per item in the document's `Attributes` list. + New items added to the Attributes list after index creation time will be dynamically indexed as well. - This value will be the dynamic-index-field name on which you can query. - E.g., Field value `Electronics` will be the dynamic-index-field. + The actual dynamic-index-field name on which you can query will be the item's PropName **value**. + E.g., 'PropName' value `Width` will be a dynamic-index-field. + + + + ```csharp + public class Attributes_ByName : AbstractIndexCreationTask + { + public Attributes_ByName() + { + Map = products => from a in products + select new + { + // Define the dynamic-index-fields by calling 'CreateField' + // A dynamic-index-field will be generated for each item in 'Attributes' + + // For each item, the field name will be the value of field 'PropName' + // The field terms will be derived from field 'PropValue' + _ = a.Attributes.Select(item => + CreateField(item.PropName, item.PropValue)), + + // A regular index field can be defined as well: + Name = a.Name + }; + } + } + ``` + + + ```csharp + public class Attributes_ByName_JS : AbstractJavaScriptIndexCreationTask + { + public Attributes_ByName_JS() + { + Maps = new HashSet + { + @"map('Products', function (p) { + return { + // For each item, + // the field name will be the value of field 'PropName' + // The field terms will be derived from field 'PropValue' + _: p.Attributes.map(item => createField( + item.PropName, item.PropValue)), + + // A regular index field can be defined as well: + Name: p.Name + }; + })" + }; + } + } + ``` + + + +* **The query**: + + To get all documents matching a specific attribute property use: + + + + ```csharp + IList matchingDocuments = session + .Advanced + .DocumentQuery() + // 'Width' is a dynamic-index-field that was indexed from the Attributes list + .WhereEquals("Width", 10) + .ToList(); + ``` + + + ```sql + // 'Width' is a dynamic-index-field that was indexed from the Attributes list + from index 'Attributes/ByName' where Width = 10 + ``` + + + + + + + + + + + +Dynamic fields can be indexed for [Full-text search](../../indexes/querying/searching.mdx) by setting `FieldIndexing.Search` in `CreateFieldOptions`. + +* **At indexing time**: + At indexing time, RavenDB analyzes the dynamic field value and creates the indexed terms. + The analyzer used at indexing time is resolved from the index definition. + You can configure an analyzer for a specific generated field name, or configure `AllFields` as a fallback. + See [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) below. + + * If the generated dynamic field name has an explicit analyzer configuration, that analyzer is used. + * Otherwise, if `AllFields` has an analyzer configuration, RavenDB uses it as a fallback. + * If no analyzer is configured for the field, RavenDB uses the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + +* **At querying time**: + For full-text search to return the expected results, the analyzer used at indexing time and the analyzer used for the query term should produce compatible terms. + + When a `search()` query targets a dynamic field that does **not** have an explicit analyzer configuration for its generated field name, + RavenDB uses the [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) to analyze the query term by default. + + Set the [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) + configuration key to `true` to make RavenDB use the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) as the fallback analyzer for the query term instead. + + This configuration can be set at different scopes: server-wide, per database, or per index. + The example below sets it at the index scope, in the index definition. + + For a summary of how RavenDB resolves the analyzer used for the query term, + see [Which analyzer is used for the query term](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) below. + + + +--- + +The following example indexes localized product descriptions as dynamic fields and queries one of those generated fields using full-text search: + +* **The document**: + + + ```csharp + public class Product + { + public string Id { get; set; } + + public Dictionary Descriptions { get; set; } + } + ``` + + + + ```json + // Sample document content + { + "Descriptions": { + "English": "North wind jacket", + "French": "Veste vent du nord" + } + } + ``` + + +* **The index**: + + The following index creates one dynamic field for each entry in the `Descriptions` dictionary. + For example, the key `English` creates the dynamic field `Description_English`. + + Each generated dynamic field is indexed for **full-text search**. + + + + ```csharp + public class Products_ByLocalizedDescription : AbstractIndexCreationTask + { + private const string UseSearchAnalyzerForDynamicFields = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public Products_ByLocalizedDescription() + { + Map = products => from product in products + select new + { + _ = product.Descriptions.Select(x => + CreateField( + "Description_" + x.Key, + x.Value, + new CreateFieldOptions + { + // Index each generated dynamic field for FTS + Indexing = FieldIndexing.Search, + Storage = FieldStorage.No + })) + }; + + StoreAllFields(FieldStorage.No); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + Configuration[UseSearchAnalyzerForDynamicFields] = "true"; + } + } + ``` + + + ```csharp + public class Products_ByLocalizedDescription_JS : AbstractJavaScriptIndexCreationTask + { + private const string UseSearchAnalyzerForDynamicFields = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public Products_ByLocalizedDescription_JS() + { + Maps = new HashSet + { + @"map('Products', function (product) { + return { + _: Object.keys(product.Descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + // Index each generated dynamic field for FTS + indexing: 'Search', + storage: 'No' + }); + }) + }; + })" + }; + + Fields[Constants.Documents.Indexing.Fields.AllFields] = new IndexFieldOptions + { + Storage = FieldStorage.No + }; + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + Configuration[UseSearchAnalyzerForDynamicFields] = "true"; + } + } + ``` + + + +* **Full-text search query**: + + Query the generated dynamic field by its field name. + + In this example, the query targets the generated field `Description_English`. + + Since `Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery` is set to `true`, + the searched term `"north wind"` is analyzed with the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + + + + ```csharp + List results = session.Advanced + .DocumentQuery() + .Search("Description_English", "north wind") + .ToList(); + ``` + + + + ```sql + from index "Products/ByLocalizedDescription" + where search(Description_English, "north wind") + ``` + + + +--- + + + +### Configuring analyzers for dynamic fields - - - -{`public class Products_ByProductType : AbstractIndexCreationTask +`CreateFieldOptions` does **not** let you specify a custom analyzer directly. +However, you can still configure which analyzer RavenDB will use for dynamic fields in the index definition by either: + +* Configuring an analyzer for a specific generated dynamic field name. +* Configuring `AllFields` as a fallback for fields that do not have their own explicit analyzer configuration. + +Use `AllFields` with care, since it can affect any field that does not have its own explicit analyzer configuration. + +--- + +#### Configure analyzer for a specific dynamic field + + + +```csharp +public class Products_ByLocalizedDescription : AbstractIndexCreationTask { - public Products_ByProductType() + public Products_ByLocalizedDescription() { - Map = products => from p in products + Map = products => from product in products select new { - // Call 'CreateField' to generate the dynamic-index-fields - // The field name will be the value of document field 'ProductType' - // The field terms will be derived from document field 'PricePerUnit' - _ = CreateField(p.ProductType, p.PricePerUnit) + _ = product.Descriptions.Select(x => + CreateField( + "Description_" + x.Key, + x.Value, + new CreateFieldOptions + { + Indexing = FieldIndexing.Search, + Storage = FieldStorage.No + })) }; + + StoreAllFields(FieldStorage.No); + + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + Analyze("Description_English", "StopAnalyzer"); } } -`} - +``` - - -{`public class Products_ByProductType_JS : AbstractJavaScriptIndexCreationTask + +```csharp +public class Products_ByLocalizedDescription_JS : AbstractJavaScriptIndexCreationTask { - public Products_ByProductType_JS() + public Products_ByLocalizedDescription_JS() { Maps = new HashSet { - @"map('Products', function (p) { + @"map('Products', function (product) { return { - _: createField(p.ProductType, p.PricePerUnit, - { indexing: 'Search', storage: true, termVector: null }) + _: Object.keys(product.Descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; })" }; + + Fields[Constants.Documents.Indexing.Fields.AllFields] = new IndexFieldOptions + { + Storage = FieldStorage.No + }; + + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + Fields["Description_English"] = new IndexFieldOptions + { + Analyzer = "StopAnalyzer" + }; } } -`} - - - - -* **The query**: - * To get all documents of some product type having a specific price per unit use: - - - -{`IList matchingDocuments = session - .Advanced - .DocumentQuery() - // 'Electronics' is the dynamic-index-field that was indexed from document field 'ProductType' - .WhereEquals("Electronics", 23) - .ToList(); -`} - - - - -{`// 'Electronics' is the dynamic-index-field that was indexed from document field 'ProductType' -from index 'Products/ByProductType' where Electronics = 23 -`} - +``` -## Example - list - - +#### Configure a fallback analyzer for all fields -* Index **values** from items in a list -* After index is deployed, any item added this list in the document will be dynamically indexed as well. - - - -* **The document**: - - -{`public class Product -\{ - public string Id \{ get; set; \} - public string Name \{ get; set; \} - - // For each element in this list, the VALUE of property 'PropName' will be dynamically indexed - // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields - public List Attributes \{ get; set; \} -\} - -public class Attribute -\{ - public string PropName \{ get; set; \} - public string PropValue \{ get; set; \} -\} -`} - - - - - -{`// Sample document content -\{ -Name": "SomeName", -Attributes": [ - \{ - "PropName": "Color", - "PropValue": "Blue" - \}, - \{ - "PropName": "Width", - "PropValue": "10" - \}, - \{ - "PropName": "Length", - "PropValue": "20" - \}, - ... - -\} -`} - - - -* **The index**: - The below index will create a dynamic-index-field per item in the document's `Attributes` list. - New items added to the Attributes list after index creation time will be dynamically indexed as well. - - The actual dynamic-index-field name on which you can query will be the item's PropName **value**. - E.g., 'PropName' value `Width` will be a dynamic-index-field. - - - - -{`public class Attributes_ByName : AbstractIndexCreationTask + + +```csharp +public class Products_ByLocalizedDescription : AbstractIndexCreationTask { - public Attributes_ByName() + public Products_ByLocalizedDescription() { - Map = products => from a in products + Map = products => from product in products select new { - // Define the dynamic-index-fields by calling CreateField - // A dynamic-index-field will be generated for each item in the Attributes list - - // For each item, the field name will be the value of field 'PropName' - // The field terms will be derived from field 'PropValue' - _ = a.Attributes.Select(item => CreateField(item.PropName, item.PropValue)), - - // A regular index field can be defined as well: - Name = a.Name + _ = product.Descriptions.Select(x => + CreateField( + "Description_" + x.Key, + x.Value, + new CreateFieldOptions + { + Indexing = FieldIndexing.Search, + Storage = FieldStorage.No + })) }; + + StoreAllFields(FieldStorage.No); + + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + Analyze(Constants.Documents.Indexing.Fields.AllFields, "StopAnalyzer"); } } -`} - +``` - - -{`public class Attributes_ByName_JS : AbstractJavaScriptIndexCreationTask + +```csharp +public class Products_ByLocalizedDescription_JS : AbstractJavaScriptIndexCreationTask { - public Attributes_ByName_JS() + public Products_ByLocalizedDescription_JS() { Maps = new HashSet { - @"map('Products', function (p) { + @"map('Products', function (product) { return { - _: p.Attributes.map(item => createField(item.PropName, item.PropValue, - { indexing: 'Search', storage: true, termVector: null })), - Name: p.Name + _: Object.keys(product.Descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; })" }; + + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + Fields[Constants.Documents.Indexing.Fields.AllFields] = new IndexFieldOptions + { + Storage = FieldStorage.No, + Analyzer = "StopAnalyzer" + }; } } -`} - - - - -* **The query**: - * To get all documents matching a specific attribute property use: - - - -{`IList matchingDocuments = session - .Advanced - .DocumentQuery() - // 'Width' is a dynamic-index-field that was indexed from the Attributes list - .WhereEquals("Width", 10) - .ToList(); -`} - - - - -{`// 'Width' is a dynamic-index-field that was indexed from the Attributes list -from index 'Attributes/ByName' where Width = 10 -`} - +``` + + + + +### Which analyzer is used for the query term? + +The following applies when a `search()` query targets a **dynamic field**: + +| Index configuration | [Configuration key](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) | Analyzer used for the query term | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| No analyzer is explicitly configured for the generated field name | `false` | The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) | +| No analyzer is explicitly configured for the generated field name | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| `AllFields` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `false` | The analyzer resolved by the normal field fallback path,
e.g. `WhitespaceAnalyzer` | +| `AllFields` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| The generated field name is explicitly configured,
e.g. `Analyze("Description_English", "StopAnalyzer")` | `false` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +| The generated field name is explicitly configured,
e.g. `Analyze("Description_English", "StopAnalyzer")` | `true` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +
-## CreateField syntax +
+ + #### Syntax for LINQ-index: - - -{`object CreateField(string name, object value); + +```csharp +object CreateField(string name, object value); object CreateField(string name, object value, bool stored, bool analyzed); object CreateField(string name, object value, CreateFieldOptions options); -`} - +``` #### Syntax for JavaScript-index: - - -{`createField(fieldName, fieldValue, options); // returns object -`} - + +```js +createField(fieldName, fieldValue, options); // returns object +``` | Parameters | Type | Description | |----------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| | **fieldName** | `string` | Name of the dynamic-index-field | -| **fieldValue** | `object` | Value of the dynamic-index-field
The field Terms are derived from this value. | -| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorate.Yes` | +| **fieldValue** | `object` | Value of the dynamic-index-field
The field terms are derived from this value. | +| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorage.Yes` | | **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing.Default` (default value)
`false` - `FieldIndexing.Exact`
`true` - `FieldIndexing.Search` | | **options** | `CreateFieldOptions` | Dynamic-index-field options | -| CreateFieldOptions | | | -|--------------------|--------------------|----------------------------------------------------------------------------| +| CreateFieldOptions | | | +|--------------------|--------------------|-----------------------------------------------------------------------------------| | **Storage** | `FieldStorage?` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | -| **Indexing** | `FieldIndexing?` | Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | -| **TermVector** | `FieldTermVector?` | Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | +| **Indexing** | `FieldIndexing?` | Sets the field indexing behavior.
Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | +| **TermVector** | `FieldTermVector?` | Sets the field term-vector behavior.
Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | @@ -540,11 +930,15 @@ object CreateField(string name, object value, CreateFieldOptions options); -## Indexed fields & terms view - -The generated dynamic-index-fields and their indexed terms can be viewed in the **Terms View**. -Below are sample index fields & their terms generated from the last example. +
+ + +The generated dynamic index fields and their indexed terms can be viewed in the **Terms View** in Studio. +Below are sample index fields & their terms generated from [this example](../../indexes/using-dynamic-fields.mdx#example---list). + ![Figure 1. Go to terms view](../assets/dynamic-index-fields-1.png) - + ![Figure 2. Indexed fields & terms](../assets/dynamic-index-fields-2.png) + + \ No newline at end of file diff --git a/versioned_docs/version-7.1/indexes/content/_using-dynamic-fields-java.mdx b/versioned_docs/version-7.1/indexes/content/_using-dynamic-fields-java.mdx index 17f3cbe360..d54ef6bea8 100644 --- a/versioned_docs/version-7.1/indexes/content/_using-dynamic-fields-java.mdx +++ b/versioned_docs/version-7.1/indexes/content/_using-dynamic-fields-java.mdx @@ -2,479 +2,918 @@ import Admonition from '@theme/Admonition'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; +import ContentFrame from '@site/src/components/ContentFrame'; +import Panel from '@site/src/components/Panel'; -* In RavenDB different documents can have different shapes. - Documents are schemaless - new fields can be added or removed as needed. +* In RavenDB, different documents can have different shapes. + Documents are schemaless - new fields can be added or removed as needed. + For such dynamic data, you can define indexes with **dynamic-index-fields**. + +* Dynamic-index-fields are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time, + which is very useful when working with highly dynamic systems. + +* Any value type can be indexed, string, number, date, etc. + An index definition can contain both dynamic-index-fields and regular-index-fields. + +* In this article: + * [Indexing field KEYS](../../indexes/using-dynamic-fields.mdx#indexing-field-keys) + * [Example - index every field under an object](../../indexes/using-dynamic-fields.mdx#example---index-every-field-under-an-object) + * [Example - index every field](../../indexes/using-dynamic-fields.mdx#example---index-every-field) + * [Indexing field VALUES](../../indexes/using-dynamic-fields.mdx#indexing-field-values) + * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) + * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) + * [Indexing dynamic fields for full-text search](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + * [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) + * [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + * [`CreateField` syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) + * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields-terms-view) -* For such dynamic data, you can define indexes with **dynamic-index-fields**. + -* This allows querying the index on fields that aren't yet known at index creation time, - which is very useful when working on highly dynamic systems. + -* Any value type can be indexed, string, number, date, etc. + + +### Example - index every field under an object -* An index definition can contain both dynamic-index-fields and regular-index-fields. + +The following example allows you to: + * Index any field that is under the same object from the document. + * After index is deployed, any new field added to this object will be indexed as well. + -* In this page: +* **The document**: + + + ```java + public class Product { + private String id; - * [Indexing documents fields KEYS](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-keys) - * [Example - index any field under object](../../indexes/using-dynamic-fields.mdx#example---index-any-field-under-object) - * [Example - index any field](../../indexes/using-dynamic-fields.mdx#example---index-any-field) - * [Indexing documents fields VALUES](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-values) - * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) - * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) - * [CreateField syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) - * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields--terms-view) + // The KEYS under the attributes object will be dynamically indexed + // Fields added to this object after index creation time will also get indexed + private Map attributes; -
+ // getters and setters ... + } + ``` + + + + ```json + // Sample document content + { + "attributes": { + "color": "Red", + "size": 42 + } + } + ``` + -## Indexing documents fields KEYS +* **The index**: + + The following index will index any field under the `attributes` object from the document, + a dynamic-index-field will be created for each such field. + New fields added to the object after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the attribute field **key**. + E.g., Keys `color` & `size` will become the actual dynamic-index-fields. + + + + ```java + public class Products_ByAttributeKey extends AbstractIndexCreationTask { + public Products_ByAttributeKey() { + + // Call 'CreateField' to generate dynamic-index-fields + // from the attributes object keys. + + // Using '_' is just a convention. + // Any other string can be used instead of '_'. + + // The actual field name will be 'item.Key' + // The actual field terms will be derived from 'item.Value' + map = "docs.Products.Select(p => new { " + + " _ = p.attributes.Select(item => " + + " this.CreateField(item.Key, item.Value)) " + + "})"; + } + } + ``` + + + ```java + public class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask { + public Products_ByAttributeKey_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (p) { " + + " return { " + + // The field name will be the key + // The field terms will be derived from the corresponding value + " _: Object.keys(p.attributes) " + + " .map(key => createField(key, p.attributes[key])) " + + " }; " + + "})" + )); + } + } + ``` + + -## Example - index any field under object +* **The query**: + + You can now query the generated dynamic-index fields. + The `_` property is Not queryable but used only in the index definition syntax. + + To get all documents with a specific _size_ use: + + + + ```java + List matchingDocuments = session + .advanced() + .documentQuery(Product.class, Products_ByAttributeKey.class) + // 'size' is a dynamic-index-field that was indexed from the attributes object + .whereEquals("size", 42) + .toList(); + ``` + + + ```sql + // 'size' is a dynamic-index-field that was indexed from the attributes object + from index 'Products/ByAttributeKey' where size = 42 + ``` + + + + + Dynamic index fields are queried with _DocumentQuery_ or _RQL_ because the field names (e.g. `size`, `color`) + are generated at indexing time and are not properties on the _Product_ class. + + Use the string-based `whereEquals("size", 42)` or RQL `where size = 42` instead. + - + -* Index any field that is under the some object from the document. -* After index is deployed, any new field added to the this object will be indexed as well. + + +### Example - index every field + +The following example allows you to: + * Define an index on a collection **without** needing any common structure between the indexed documents. + * After the index is deployed, any new field added to any document in the collection will be indexed as well. + + + +* Use this approach with care. +* Indexing every field can increase indexing time, memory usage, and disk space, + especially for large or frequently updated documents. * **The document**: - - -{`public class Product \{ - private String id; - - // The KEYS under the attributes object will be dynamically indexed - // Fields added to this object after index creation time will also get indexed - private Dictionary attributes; - - // get + set implementation ... -\} -`} - - - - -{`// Sample document content -\{ - "attributes": \{ - "color": "Red", - "size": 42 - \} -\} -`} - - + + + ```java + public class Product { + private String id; + + // All KEYS in the document will be dynamically indexed + // Fields added to the document after index creation time will also get indexed + private String firstName; + private String lastName; + private String title; + // ... + + // getters and setters ... + } + ``` + + + + ```json + // Sample document content + { + "firstName": "John", + "lastName": "Doe", + "title": "Engineer" + // ... + } + ``` + * **The index**: - The following index will index any field under the `attributes` object from the document, - a dynamic-index-field will be created for each such field. - New fields added to the object after index creation time will be dynamically indexed as well. + + The following index will index any field from the document, + a dynamic-index-field will be created for each field. + New fields added to the document after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the field **key**. + E.g., Keys `firstName` & `lastName` will become the actual dynamic-index-fields. + + The example uses an `AbstractJavaScriptIndexCreationTask` because it enumerates all top-level document fields at indexing time with `Object.keys(...)`. + LINQ indexes typically create dynamic fields from a known enumerable structure, such as a map or collection. + + + + ```java + public class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask { + public Products_ByAnyField_JS() { + + // This will index EVERY FIELD under the top level of the document + setMaps(Sets.newHashSet( + "map('Products', function (p) { " + + " return { " + + " _: Object.keys(p).map(key => createField(key, p[key])) " + + " }; " + + "})" + )); + } + } + ``` + + - The actual dynamic-index-field name on which you can query will be the attribute field **key**. - e.g. Keys `color` & `size` will become the actual dynamic-index-fields. +* **The query**: + + To get all documents with a specific _lastName_ use: + + + + ```java + List matchingDocuments = session + .advanced() + .documentQuery(Product.class, Products_ByAnyField_JS.class) + // 'lastName' is a dynamic-index-field that was indexed from the document + .whereEquals("lastName", "Doe") + .toList(); + ``` + + + ```sql + // 'lastName' is a dynamic-index-field that was indexed from the document + from index 'Products/ByAnyField/JS' where lastName = "Doe" + ``` + + + + + + - - - -{`public class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask { - public Products_ByAttributeKey_JS() { - // Call 'createField' to generate dynamic-index-fields from the attributes object keys - // Using '_' is just a convention. Any other string can be used instead of '_' + - // The actual field name will be the key - // The actual field terms will be derived from p.attributes[key] - setMaps(Sets.newHashSet( - "map('Products', function (p) { " + - " return { " + - " _: Object.keys(p.attributes).map(key => createField(key, p.attributes[key], " + - " { indexing: 'Search', storage: false, termVector: null })) " + - " }; " + - "}) " - )); - } -} -`} - - - + + +### Example - basic -* **The query**: - * You can now query the generated dynamic-index fields. - Property `_` is Not queryable, it is only used in the index definition syntax. - * To get all documents with some 'size' use: - - - -{`List matchingDocuments = session - .query(Product.class, Products_ByAttributeKey_JS.class) - .whereEquals("size", 42) - .toList(); -`} - - - - -{`// 'size' is a dynamic-index-field that was indexed from the Attributes object -from index 'Products/ByAttributeKey/JS' where size = 42 -`} - - - + +The following example shows: + * Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. + * Documents can then be queried based on those indexed values. + * For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---list) below. + -## Example - index any field +* **The document**: + + + ```java + public class Product { + private String id; - + // The VALUE of productType will be dynamically indexed + private String productType; + private int pricePerUnit; -* Define an index on a collection **without** needing any common structure between the indexed documents. -* After index is deployed, any new field added to the document will be indexed as well. + // getters and setters ... + } + ``` + + + + ```json + // Sample document content + { + "productType": "Electronics", + "pricePerUnit": 23 + } + ``` + - +* **The index**: + + The following index will index the **value** of document field 'productType'. + + This value will be the dynamic-index-field name on which you can query. + E.g., Field value `Electronics` will be the dynamic-index-field. + + + + ```java + public class Products_ByProductType extends AbstractIndexCreationTask { + public Products_ByProductType() { + + // Call 'CreateField' to generate the dynamic-index-field. + // The field name will be the value of document field 'productType'. + // The field terms will be derived from document field 'pricePerUnit'. + map = "docs.Products.Select(p => new { " + + " _ = this.CreateField(p.productType, p.pricePerUnit) " + + "})"; + } + } + ``` + + + ```java + public class Products_ByProductType_JS extends AbstractJavaScriptIndexCreationTask { + public Products_ByProductType_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (p) { " + + " return { " + + // The field name will be the value of document field 'productType' + // The field terms will be derived from document field 'pricePerUnit' + " _: createField(p.productType, p.pricePerUnit) " + + " }; " + + "})" + )); + } + } + ``` + + - -Consider if that is a true necessity, as indexing every single field can end up costing time and disk space. +* **The query**: + + To get all documents of some product type having a specific price per unit use: + + + + ```java + List matchingDocuments = session + .advanced() + .documentQuery(Product.class, Products_ByProductType.class) + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + .whereEquals("Electronics", 23) + .toList(); + ``` + + + ```sql + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + from index 'Products/ByProductType' where Electronics = 23 + ``` + + + + + + + +### Example - list + + +The following example allows you to: + * Index **values** from items in a list + * After index is deployed, any item added to this list in the document will be dynamically indexed as well. * **The document**: - - -{`public class Product \{ - private String id; - - // All KEYS in the document will be dynamically indexed - // Fields added to the document after index creation time will also get indexed - private String firstName; - private String lastName; - private String title; - // ... - - // get + set implementation ... -\} -`} - - - - - -{`// Sample document content -\{ - "firstName": "John", - "lastName": "Doe", - "title": "Engineer", - // ... -\} -`} - - + + + ```java + public class Product { + private String id; + private String name; + + // For each element in this list, + // the VALUE of property 'propName' will be dynamically indexed. + // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields. + private List attributes; + + // getters and setters ... + } -* **The index**: - The following index will index any field from the document, - a dynamic-index-field will be created for each field. - New fields added to the document after index creation time will be dynamically indexed as well. + public class Attribute { + private String propName; + private String propValue; - The actual dynamic-index-field name on which you can query will be the field **key**. - E.g., Keys `firstName` & `lastName` will become the actual dynamic-index-fields. + // getters and setters ... + } + ``` + + + + ```json + // Sample document content + { + "name": "SomeName", + "attributes": [ + { + "propName": "Color", + "propValue": "Blue" + }, + { + "propName": "Width", + "propValue": "10" + }, + { + "propName": "Length", + "propValue": "20" + } + // ... + ] + } + ``` + - - - -{`public class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask { - public Products_ByAnyField_JS() { +* **The index**: - // This will index EVERY FIELD under the top level of the document - setMaps(Sets.newHashSet( - "map('Products', function (p) { " + - " return { " + - " _: Object.keys(p).map(key => createField(key, p[key], " + - " { indexing: 'Search', storage: true, termVector: null })) " + - " }; " + - "}) " - )); + The following index will create a dynamic-index-field per item in the document's `attributes` list. + New items added to the attributes list after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the item's propName **value**. + E.g., 'propName' value `Width` will be a dynamic-index-field. + + + + ```java + public class Attributes_ByName extends AbstractIndexCreationTask { + public Attributes_ByName() { + + // Define the dynamic-index-fields by calling 'CreateField'. + // A dynamic-index-field will be generated for each item in 'attributes'. + + // For each item, the field name will be the value of field 'propName' + // The field terms will be derived from field 'propValue'. + // A regular index field (Name) is defined as well. + map = "docs.Products.Select(p => new { " + + " _ = p.attributes.Select(item => " + + " this.CreateField(item.propName, item.propValue)), " + + " Name = p.name " + + "})"; + } } -} -`} - - - + ``` + + + ```java + public class Attributes_ByName_JS extends AbstractJavaScriptIndexCreationTask { + public Attributes_ByName_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (p) { " + + " return { " + + // For each item, + // the field name will be the value of field 'propName'. + // The field terms will be derived from field 'propValue'. + " _: p.attributes.map(item => " + + " createField(item.propName, item.propValue)), " + + // A regular index field can be defined as well: + " Name: p.name " + + " }; " + + "})" + )); + } + } + ``` + + * **The query**: - * To get all documents with some 'lastName' use: - - - -{`List matchingDocuments = session - .query(Product.class, Products_ByAnyField_JS.class) - .whereEquals("lastName", "Doe") - .toList(); -`} - - - - -{`// 'lastName' is a dynamic-index-field that was indexed from the document -from index 'Products/ByAnyField/JS' where lastName = "Doe" -`} - - - + + To get all documents matching a specific attribute property use: + + + + ```java + List matchingDocuments = session + .advanced() + .documentQuery(Product.class, Attributes_ByName.class) + // 'Width' is a dynamic-index-field that was indexed from the attributes list + .whereEquals("Width", 10) + .toList(); + ``` + + + ```sql + // 'Width' is a dynamic-index-field that was indexed from the attributes list + from index 'Attributes/ByName' where Width = 10 + ``` + + + + + + + + + + +Dynamic fields can be indexed for [Full-text search](../../indexes/querying/searching.mdx) by setting `indexing: 'Search'` in the `createField` options object (`AbstractJavaScriptIndexCreationTask`) +or `FieldIndexing.Search` in the `CreateFieldOptions` (`AbstractIndexCreationTask`). +* **At indexing time**: + At indexing time, RavenDB analyzes the dynamic field value and creates the indexed terms. + The analyzer used at indexing time is resolved from the index definition. + You can configure an analyzer for a specific generated field name, or configure `ALL_FIELDS` as a fallback. + See [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) below. -## Indexing documents fields VALUES + * If the generated dynamic field name has an explicit analyzer configuration, that analyzer is used. + * Otherwise, if `ALL_FIELDS` has an analyzer configuration, RavenDB uses it as a fallback. + * If no analyzer is configured for the field, RavenDB uses the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). -## Example - basic +* **At querying time**: + For full-text search to return the expected results, the analyzer used at indexing time and the analyzer used for the query term should produce compatible terms. - + When a `search()` query targets a dynamic field that does **not** have an explicit analyzer configuration for its generated field name, + RavenDB uses the [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) to analyze the query term by default. -* Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. -* Documents can then be queried based on those indexed values. -* For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---index-a-list-of-properties) below. + Set the [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) + configuration key to `true` to make RavenDB use the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) as the fallback analyzer for the query term instead. + + This configuration can be set at different scopes: server-wide, per database, or per index. + The example below sets it at the index scope, in the index definition. + + For a summary of how RavenDB resolves the analyzer used for the query term, + see [Which analyzer is used for the query term](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) below. + +--- + +The following example indexes localized product descriptions as dynamic fields and queries one of those generated fields using full-text search: + +* **The document**: -* **The document**: - - -{`public class Product \{ - private String id; - - // The VALUE of productType will be dynamically indexed - private String productType; - private int pricePerUnit; - - // get + set implementation ... -\} -`} - - + + ```java + public class Product { + private String id; - - -{`// Sample document content -\{ - "productType": "Electronics", - "pricePerUnit": 23 -\} -`} - - + private Map descriptions; -* **The index**: - The following index will index the **value** of document field 'productType'. + // getters and setters ... + } + ``` + + + + ```json + // Sample document content + { + "descriptions": { + "English": "North wind jacket", + "French": "Veste vent du nord" + } + } + ``` + + +* **The index**: + + The following index creates one dynamic field for each entry in the `descriptions` map. + For example, the key `English` creates the dynamic field `Description_English`. + + Each generated dynamic field is indexed for **full-text search**. + + + + ```java + public class Products_ByLocalizedDescription extends AbstractIndexCreationTask { + + private static final String USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public Products_ByLocalizedDescription() { + map = "docs.Products.Select(product => new { " + + " _ = product.descriptions.Select(x => " + + " this.CreateField( " + + " \"Description_\" + x.Key, " + + " x.Value, " + + " new CreateFieldOptions { " + + // Index each generated dynamic field for FTS + " Indexing = FieldIndexing.Search, " + + " Storage = FieldStorage.No " + + " })) " + + "})"; + + storeAllFields(FieldStorage.NO); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + getConfiguration().put(USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS, "true"); + } + } + ``` + + + ```java + public class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + + private static final String USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public Products_ByLocalizedDescription_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (product) { " + + " return { " + + " _: Object.keys(product.descriptions) " + + " .map(function (key) { " + + " return createField( " + + " 'Description_' + key, " + + " product.descriptions[key], " + + " { " + + // Index each generated dynamic field for FTS + " indexing: 'Search', " + + " storage: false " + + " }); " + + " }) " + + " }; " + + "})" + )); + + Map fields = new HashMap<>(); + + IndexFieldOptions allFieldsOptions = new IndexFieldOptions(); + allFieldsOptions.setStorage(FieldStorage.NO); + + fields.put(Constants.Documents.Indexing.Fields.ALL_FIELDS, allFieldsOptions); + setFields(fields); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + getConfiguration().put(USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS, "true"); + } + } + ``` + + + +* **Full-text search query**: + + Query the generated dynamic field by its field name. + + In this example, the query targets the generated field `Description_English`. + + Since `Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery` is set to `true`, + the searched term `"north wind"` is analyzed with the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + + + + ```java + List results = session.advanced() + .documentQuery(Product.class, Products_ByLocalizedDescription.class) + .search("Description_English", "north wind") + .toList(); + ``` + + + + ```sql + from index "Products/ByLocalizedDescription" + where search(Description_English, "north wind") + ``` + + - This value will be the dynamic-index-field name on which you can query. - e.g. Field value `Electronics` will be the dynamic-index-field. +--- - - - -{`public class Products_ByProductType extends AbstractIndexCreationTask { - public Products_ByProductType() { + + +### Configuring analyzers for dynamic fields + +The `CreateFieldOptions` (and the JavaScript `createField` options) do **not** let you specify a custom analyzer directly. +However, you can still configure which analyzer RavenDB will use for dynamic fields in the index definition by either: - // The field name will be the value of document field 'productType' - // The field terms will be derived from document field 'pricePerUnit' - map = "docs.Products.Select(p => new { " + - " _ = this.CreateField(p.productType, p.pricePerUnit) " + +* Configuring an analyzer for a specific generated dynamic field name. +* Configuring `ALL_FIELDS` as a fallback for fields that do not have their own explicit analyzer configuration. + +Use `ALL_FIELDS` with care, since it can affect any field that does not have its own explicit analyzer configuration. + +--- + +#### Configure analyzer for a specific dynamic field + + + +```java +public class Products_ByLocalizedDescription extends AbstractIndexCreationTask { + public Products_ByLocalizedDescription() { + map = "docs.Products.Select(product => new { " + + " _ = product.descriptions.Select(x => " + + " this.CreateField( " + + " \"Description_\" + x.Key, " + + " x.Value, " + + " new CreateFieldOptions { " + + " Indexing = FieldIndexing.Search, " + + " Storage = FieldStorage.No " + + " })) " + "})"; + + storeAllFields(FieldStorage.NO); + + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + analyze("Description_English", "StopAnalyzer"); } } -`} - +``` - - -* **The query**: - * To get all documents of some product type having a specific price per unit use: - - - -{`List matchingDocuments = session - .query(Product.class, Products_ByProductType.class) - .whereEquals("Electronics", 23) - .toList(); -`} - - - - -{`// 'Electronics' is the dynamic-index-field that was indexed from document field 'productType' -from index 'Products/ByProductType' where Electronics = 23 -`} - - - - -## Example - list + +```java +public class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + public Products_ByLocalizedDescription_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (product) { " + + " return { " + + " _: Object.keys(product.descriptions) " + + " .map(function (key) { " + + " return createField( " + + " 'Description_' + key, " + + " product.descriptions[key], " + + " { " + + " indexing: 'Search', " + + " storage: false " + + " }); " + + " }) " + + " }; " + + "})" + )); - + Map fields = new HashMap<>(); -* Index **values** from items in a list -* After index is deployed, any item added this list in the document will be dynamically indexed as well. + IndexFieldOptions allFieldsOptions = new IndexFieldOptions(); + allFieldsOptions.setStorage(FieldStorage.NO); + fields.put(Constants.Documents.Indexing.Fields.ALL_FIELDS, allFieldsOptions); - + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + IndexFieldOptions englishOptions = new IndexFieldOptions(); + englishOptions.setAnalyzer("StopAnalyzer"); + fields.put("Description_English", englishOptions); -* **The document**: - - -{`public class Product \{ - private String id; - private String name; - - // For each element in this list, the VALUE of property 'propName' will be dynamically indexed - // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields - private List attributes; - - // get + set implementation ... -\} - -public class Attribute \{ - private String propName; - private String propValue; - - // get + set implementation ... -\} -`} - + setFields(fields); + } +} +``` + - - -{`// Sample document content -\{ -name": "SomeName", -attributes": [ - \{ - "propName": "Color", - "propValue": "Blue" - \}, - \{ - "propName": "Width", - "propValue": "10" - \}, - \{ - "propName": "Length", - "propValue": "20" - \}, - ... - -\} -`} - - +#### Configure a fallback analyzer for all fields + + + +```java +public class Products_ByLocalizedDescription extends AbstractIndexCreationTask { + public Products_ByLocalizedDescription() { + map = "docs.Products.Select(product => new { " + + " _ = product.descriptions.Select(x => " + + " this.CreateField( " + + " \"Description_\" + x.Key, " + + " x.Value, " + + " new CreateFieldOptions { " + + " Indexing = FieldIndexing.Search, " + + " Storage = FieldStorage.No " + + " })) " + + "})"; -* **The index**: - The following index will create a dynamic-index-field per item in the document's `attributes` list. - New items added to the attributes list after index creation time will be dynamically indexed as well. - - The actual dynamic-index-field name on which you can query will be the item's propName **value**. - E.g., 'propName' value `Width` will be a dynamic-index-field. - - - - -{`public class Attributes_ByName extends AbstractIndexCreationTask { - public Attributes_ByName() { - - // For each attribute item, the field name will be the value of field 'propName' - // The field terms will be derived from field 'propValue' - // A regular-index-field (Name) is defined as well - map = - "docs.Products.Select(p => new { " + - " _ = p.attributes.Select(item => this.CreateField(item.propName, item.propValue)), " + - " Name = p.name " + - "})"; + storeAllFields(FieldStorage.NO); + + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + analyze(Constants.Documents.Indexing.Fields.ALL_FIELDS, "StopAnalyzer"); } } -`} - +``` - + +```java +public class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + public Products_ByLocalizedDescription_JS() { + setMaps(Sets.newHashSet( + "map('Products', function (product) { " + + " return { " + + " _: Object.keys(product.descriptions) " + + " .map(function (key) { " + + " return createField( " + + " 'Description_' + key, " + + " product.descriptions[key], " + + " { " + + " indexing: 'Search', " + + " storage: false " + + " }); " + + " }) " + + " }; " + + "})" + )); -* **The query**: - * To get all documents matching a specific attribute property use: - - - -{`List matchingDocuments = session - .query(Product.class, Attributes_ByName.class) - .whereEquals("Width", 10) - .toList(); -`} - - - - -{`// 'Width' is a dynamic-index-field that was indexed from the attributes list -from index 'Attributes/ByName' where Width = 10 -`} - + Map fields = new HashMap<>(); + + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + IndexFieldOptions allFieldsOptions = new IndexFieldOptions(); + allFieldsOptions.setStorage(FieldStorage.NO); + allFieldsOptions.setAnalyzer("StopAnalyzer"); + fields.put(Constants.Documents.Indexing.Fields.ALL_FIELDS, allFieldsOptions); + + setFields(fields); + } +} +``` + + + + +### Which analyzer is used for the query term? +The following applies when a `search()` query targets a **dynamic field**: -## CreateField syntax +| Index configuration | [Configuration key](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) | Analyzer used for the query term | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| No analyzer is explicitly configured for the generated field name | `false` | The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) | +| No analyzer is explicitly configured for the generated field name | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `false` | The analyzer resolved by the normal field fallback path,
e.g. `WhitespaceAnalyzer` | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| The generated field name is explicitly configured,
e.g. `analyze("Description_English", "StopAnalyzer")` | `false` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +| The generated field name is explicitly configured,
e.g. `analyze("Description_English", "StopAnalyzer")` | `true` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | -#### Syntax for LINQ-index: +
- - -{`object CreateField(string name, object value); +
+ + + +#### Syntax for `AbstractIndexCreationTask` + + +```csharp +object CreateField(string name, object value); object CreateField(string name, object value, bool stored, bool analyzed); object CreateField(string name, object value, CreateFieldOptions options); -`} - +``` -#### Syntax for JavaScript-index: +#### Syntax for `AbstractJavaScriptIndexCreationTask` - - -{`createField(fieldName, fieldValue, options); // returns object -`} - + +```js +createField(fieldName, fieldValue, options); // returns object +``` -| Parameters | Type | Description | -|----------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **fieldName** | `string` | Name of the dynamic-index-field | -| **fieldValue** | `object` | Value of the dynamic-index-field
The field Terms are derived from this value. | -| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorate.Yes` | -| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing.Default` (default value)
`false` - `FieldIndexing.Exact`
`true` - `FieldIndexing.Search` | +| Parameters | Type | Description | +|----------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **fieldName** | `string` | Name of the dynamic-index-field | +| **fieldValue** | `object` | Value of the dynamic-index-field
The field terms are derived from this value. | +| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorage.Yes` | +| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing.Default` (default value)
`false` - `FieldIndexing.Exact`
`true` - `FieldIndexing.Search` | | **options** | `CreateFieldOptions` | Dynamic-index-field options | -| CreateFieldOptions | | | -|--------------------|--------------------|----------------------------------------------------------------------------| +| CreateFieldOptions | | | +|--------------------|--------------------|-----------------------------------------------------------------------------------| | **Storage** | `FieldStorage?` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | -| **Indexing** | `FieldIndexing?` | Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | -| **TermVector** | `FieldTermVector?` | Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | +| **Indexing** | `FieldIndexing?` | Sets the field indexing behavior.
Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | +| **TermVector** | `FieldTermVector?` | Sets the field term-vector behavior.
Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | +* The `CreateField` syntax (and the `CreateFieldOptions` shape) above describes what is available inside the server-side `map` string of an `AbstractIndexCreationTask`, + since the Java client sends the `map` source to the server for compilation. + The JavaScript `createField` function is the equivalent for `AbstractJavaScriptIndexCreationTask` map sources. + * All above examples have used the character `_` in the dynamic-index-field definition. However, using `_` is just a convention. Any other string can be used instead. * This property is Not queryable, it is only used in the index definition syntax. - The actual dynamic-index-fields that are generated are defined by the `CreateField` method. + The actual dynamic-index-fields that are generated are defined by the `CreateField` method (or the `createField` JS function). +
+ + - -## Indexed fields & terms view - -The generated dynamic-index-fields and their indexed terms can be viewed in the **Terms View**. -Below are sample index fields & their terms generated from the last example. - +The generated dynamic index fields and their indexed terms can be viewed in the **Terms View** in Studio. +Below are sample index fields & their terms generated from [this example](../../indexes/using-dynamic-fields.mdx#example---list). + ![Figure 1. Go to terms view](../assets/dynamic-index-fields-1.png) - + ![Figure 2. Indexed fields & terms](../assets/dynamic-index-fields-2.png) - - - - + + diff --git a/versioned_docs/version-7.1/indexes/content/_using-dynamic-fields-nodejs.mdx b/versioned_docs/version-7.1/indexes/content/_using-dynamic-fields-nodejs.mdx index b5df2df4a7..c14184c2b5 100644 --- a/versioned_docs/version-7.1/indexes/content/_using-dynamic-fields-nodejs.mdx +++ b/versioned_docs/version-7.1/indexes/content/_using-dynamic-fields-nodejs.mdx @@ -2,533 +2,710 @@ import Admonition from '@theme/Admonition'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; +import ContentFrame from '@site/src/components/ContentFrame'; +import Panel from '@site/src/components/Panel'; -* In RavenDB different documents can have different shapes. - Documents are schemaless - new fields can be added or removed as needed. - -* For such dynamic data, you can define indexes with **dynamic-index-fields**. - -* This allows querying the index on fields that aren't yet known at index creation time, - which is very useful when working on highly dynamic systems. - -* Any value type can be indexed, string, number, date, etc. - -* An index definition can contain both dynamic-index-fields and regular-index-fields. - -* In this page: - - * [Indexing documents fields KEYS](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-keys) - * [Example - index any field under object](../../indexes/using-dynamic-fields.mdx#example---index-any-field-under-object) - * [Example - index any field](../../indexes/using-dynamic-fields.mdx#example---index-any-field) - * [Indexing documents fields VALUES](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-values) - * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) - * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) - * [CreateField syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) - * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields--terms-view) +* In RavenDB, different documents can have different shapes. + Documents are schemaless - new fields can be added or removed as needed. + For such dynamic data, you can define indexes with **dynamic-index-fields**. + +* Dynamic-index-fields are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time, + which is very useful when working with highly dynamic systems. + +* Any value type can be indexed, string, number, date, etc. + An index definition can contain both dynamic-index-fields and regular-index-fields. + +* In this article: + * [Indexing field KEYS](../../indexes/using-dynamic-fields.mdx#indexing-field-keys) + * [Example - index every field under an object](../../indexes/using-dynamic-fields.mdx#example---index-every-field-under-an-object) + * [Example - index every field](../../indexes/using-dynamic-fields.mdx#example---index-every-field) + * [Indexing field VALUES](../../indexes/using-dynamic-fields.mdx#indexing-field-values) + * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) + * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) + * [Indexing dynamic fields for full-text search](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + * [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) + * [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + * [`createField` syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) + * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields-terms-view) -## Indexing documents fields KEYS + - -#### Example - index any field under object -The following allows you to: - -* Index any field that is under the some object from the document. -* After index is deployed, any new field added to the this object will be indexed as well. -**The document**: - - -{`class Product \{ - constructor(id, attributes) \{ - this.id = id; - - // The KEYS under the attributes object will be dynamically indexed - // Fields added to this object after index creation time will also get indexed - this.attributes = attributes; - \} -\} -`} - - + + +### Example - index every field under an object - - -{`// Sample document content -\{ - "attributes": \{ - "color": "Red", - "size": 42 - \} -\} -`} - - + +The following example allows you to: + * Index any field that is under the same object from the document. + * After index is deployed, any new field added to this object will be indexed as well. + -**The index**: +* **The document**: + + + ```js + class Product { + constructor(id, attributes) { + this.id = id; + + // The KEYS under the attributes object will be dynamically indexed + // Fields added to this object after index creation time will also get indexed + this.attributes = attributes; + } + } + ``` + + + + ```json + // Sample document content + { + "attributes": { + "color": "Red", + "size": 42 + } + } + ``` + -* The following index will index any field under the `attributes` object from the document, +* **The index**: + + The following index will index any field under the `attributes` object from the document, a dynamic-index-field will be created for each such field. - New fields added to the object after index creation time will be dynamically indexed as well. - -* The actual dynamic-index-field name on which you can query will be the attribute field **key**. - e.g. Keys `color` & `size` will become the actual dynamic-index-fields. - - - - -{`class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask { - constructor() { - super(); - - const { createField } = this.mapUtils(); - - this.map("Products", p => { - return { - // Call 'createField' to generate dynamic-index-fields from the attributes object keys - // Using '_' is just a convention. Any other string can be used instead of '_' - - // The actual field name will be the key - // The actual field terms will be derived from p.attributes[key] - _: Object.keys(p.attributes).map(key => createField(key, p.attributes[key], { - indexing: "Search", - storage: false, - termVector: null - })) - }; - }); + New fields added to the object after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the attribute field **key**. + E.g., Keys `color` & `size` will become the actual dynamic-index-fields. + + + ```js + class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { + super(); + + const { createField } = this.mapUtils(); + + this.map("Products", p => { + return { + // Call 'createField' to generate dynamic-index-fields + // from the attributes object keys. + + // Using '_' is just a convention. + // Any other string can be used instead of '_' + + // The actual field name will be the key + // The actual field terms will be derived from p.attributes[key] + _: Object.keys(p.attributes).map(key => createField(key, p.attributes[key])) + }; + }); + } } -} -`} - - - - -**The query**: - -* You can now query the generated dynamic-index fields. - Property `_` is Not queryable, it is only used in the index definition syntax. - -* To get all documents with some 'size' use: - - - - -{`const matchingDocuments = session.query({indexName: 'Products_ByAttributeKey'}) - // 'size' is a dynamic-index-field that was indexed from the attributes object - .whereEquals('size', 42) - .all(); -`} - - - - -{`// 'size' is a dynamic-index-field that was indexed from the attributes object -from index 'Products/ByAttributeKey' where size = 42 -`} - - - - - + ``` + + +* **The query**: + + You can now query the generated dynamic-index fields. + The `_` property is Not queryable but used only in the index definition syntax. + + To get all documents with a specific _size_ use: + + + + ```js + const matchingDocuments = await session + .query({ indexName: "Products/ByAttributeKey/JS" }) + // 'size' is a dynamic-index-field that was indexed from the attributes object + .whereEquals("size", 42) + .all(); + ``` + + + ```sql + // 'size' is a dynamic-index-field that was indexed from the attributes object + from index 'Products/ByAttributeKey/JS' where size = 42 + ``` + + + + + + + +### Example - index every field -#### Example - index any field -The following allows you to: - -* Define an index on a collection **without** needing any common structure between the indexed documents. -* After index is deployed, any new field added to the document will be indexed as well. - +The following example allows you to: + * Define an index on a collection **without** needing any common structure between the indexed documents. + * After the index is deployed, any new field added to any document in the collection will be indexed as well. + + - -Consider if that is a true necessity, as indexing every single field can end up costing time and disk space. - +* Use this approach with care. +* Indexing every field can increase indexing time, memory usage, and disk space, + especially for large or frequently updated documents. -**The document**: - - -{`class Product \{ - constructor(id, firstName, lastName, title) \{ - this.id = id; - - // All KEYS in the document will be dynamically indexed - // Fields added to the document after index creation time will also get indexed - this.firstName = firstName; - this.lastName = lastName; - this.title = title; - // ... - \} -\} -`} - - - - - -{`// Sample document content -\{ - "firstName": "John", - "lastName": "Doe", - "title": "Engineer", - // ... -\} -`} - - -**The index**: +* **The document**: + + + ```js + class Product { + constructor(id, firstName, lastName, title) { + this.id = id; + + // All KEYS in the document will be dynamically indexed + // Fields added to the document after index creation time will also get indexed + this.firstName = firstName; + this.lastName = lastName; + this.title = title; + // ... + } + } + ``` + + + + ```json + // Sample document content + { + "firstName": "John", + "lastName": "Doe", + "title": "Engineer", + // ... + } + ``` + -* The following index will index any field from the document, +* **The index**: + + The following index will index any field from the document, a dynamic-index-field will be created for each field. - New fields added to the document after index creation time will be dynamically indexed as well. - -* The actual dynamic-index-field name on which you can query will be the field **key**. - e.g. Keys `firstName` & `lastName` will become the actual dynamic-index-fields. + New fields added to the document after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the field **key**. + E.g., Keys `firstName` & `lastName` will become the actual dynamic-index-fields. + + + ```js + class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { + super(); + + const { createField } = this.mapUtils(); + + // This will index EVERY FIELD under the top level of the document + this.map("Products", p => { + return { + _: Object.keys(p).map(key => createField(key, p[key])) + }; + }); + } + } + ``` + + +* **The query**: + + To get all documents with a specific _lastName_ use: + + + + ```js + const matchingDocuments = await session + .query({ indexName: "Products/ByAnyField/JS" }) + // 'lastName' is a dynamic-index-field that was indexed from the document + .whereEquals("lastName", "Doe") + .all(); + ``` + + + ```sql + // 'lastName' is a dynamic-index-field that was indexed from the document + from index 'Products/ByAnyField/JS' where lastName = "Doe" + ``` + + + + + + + + + + + +### Example - basic + + +The following example shows: + * Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. + * Documents can then be queried based on those indexed values. + * For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---list) below. + - - - -{`class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask { - constructor () { - super(); +* **The document**: + + + ```js + class Product { + constructor(id, productType, pricePerUnit) { + this.id = id; + + // The VALUE of productType will be dynamically indexed + this.productType = productType; + this.pricePerUnit = pricePerUnit; + } + } + ``` + + + + ```json + // Sample document content + { + "productType": "Electronics", + "pricePerUnit": 23 + } + ``` + + +* **The index**: + + The following index will index the **value** of document field 'productType'. + + This value will be the dynamic-index-field name on which you can query. + E.g., Field value `Electronics` will be the dynamic-index-field. + + + ```js + class Products_ByProductType_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { + super(); + + const { createField } = this.mapUtils(); + + this.map("Products", p => { + return { + // The field name will be the value of document field 'productType' + // The field terms will be derived from document field 'pricePerUnit' + _: createField(p.productType, p.pricePerUnit) + }; + }); + } + } + ``` + + +* **The query**: + + To get all documents of some product type having a specific price per unit use: + + + + ```js + const matchingDocuments = await session + .query({ indexName: "Products/ByProductType/JS" }) + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + .whereEquals("Electronics", 23) + .all(); + ``` + + + ```sql + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + from index 'Products/ByProductType/JS' where Electronics = 23 + ``` + + + + + + + +### Example - list - const { createField } = this.mapUtils(); + +The following example allows you to: + * Index **values** from items in a list + * After index is deployed, any item added to this list in the document will be dynamically indexed as well. + - this.map("Products", p => { - return { - // This will index EVERY FIELD under the top level of the document - _: Object.keys(p).map(key => createField(key, p[key], { - indexing: "Search", - storage: true, - termVector: null - })) - }; - }); +* **The document**: + + + ```js + class Product { + constructor(id, name, attributes) { + this.id = id; + this.name = name; + + // For each element in this list, + // the VALUE of property 'propName' will be dynamically indexed. + // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields. + this.attributes = attributes; + } } -} -`} - - - -**The query**: + class Attribute { + constructor(propName, propValue) { + this.propName = propName; + this.propValue = propValue; + } + } + ``` + + + + ```json + // Sample document content + { + "name": "SomeName", + "attributes": [ + { + "propName": "Color", + "propValue": "Blue" + }, + { + "propName": "Width", + "propValue": "10" + }, + { + "propName": "Length", + "propValue": "20" + }, + ... + ] + } + ``` + + +* **The index**: + + The following index will create a dynamic-index-field per item in the document's `attributes` list. + New items added to the attributes list after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the item's propName **value**. + E.g., 'propName' value `Width` will be a dynamic-index-field. + + + ```js + class Attributes_ByName_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { + super(); + + const { createField } = this.mapUtils(); + + this.map("Products", p => { + return { + // Define the dynamic-index-fields by calling 'createField' + // A dynamic-index-field will be generated for each item in 'attributes' + + // For each item, the field name will be the value of field 'propName' + // The field terms will be derived from field 'propValue' + _: p.attributes.map(item => + createField(item.propName, item.propValue)), + + // A regular index field can be defined as well: + name: p.name + }; + }); + } + } + ``` + + +* **The query**: + + To get all documents matching a specific attribute property use: + + + + ```js + const matchingDocuments = await session + .query({ indexName: "Attributes/ByName/JS" }) + // 'Width' is a dynamic-index-field that was indexed from the attributes list + .whereEquals("Width", 10) + .all(); + ``` + + + ```sql + // 'Width' is a dynamic-index-field that was indexed from the attributes list + from index 'Attributes/ByName/JS' where Width = 10 + ``` + + + + + + + + + + + +Dynamic fields can be indexed for [Full-text search](../../indexes/querying/searching.mdx) by setting `indexing: "Search"` in the `createField` options. + +* **At indexing time**: + At indexing time, RavenDB analyzes the dynamic field value and creates the indexed terms. + The analyzer used at indexing time is resolved from the index definition. + You can configure an analyzer for a specific generated field name, or configure `ALL_FIELDS` as a fallback. + See [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) below. + + * If the generated dynamic field name has an explicit analyzer configuration, that analyzer is used. + * Otherwise, if `ALL_FIELDS` has an analyzer configuration, RavenDB uses it as a fallback. + * If no analyzer is configured for the field, RavenDB uses the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + +* **At querying time**: + For full-text search to return the expected results, the analyzer used at indexing time and the analyzer used for the query term should produce compatible terms. + + When a `search()` query targets a dynamic field that does **not** have an explicit analyzer configuration for its generated field name, + RavenDB uses the [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) to analyze the query term by default. + + Set the [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) + configuration key to `true` to make RavenDB use the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) as the fallback analyzer for the query term instead. + + This configuration can be set at different scopes: server-wide, per database, or per index. + The example below sets it at the index scope, in the index definition. + + For a summary of how RavenDB resolves the analyzer used for the query term, + see [Which analyzer is used for the query term](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) below. -* To get all documents with some 'lastName' use: + + +--- + +The following example indexes localized product descriptions as dynamic fields and queries one of those generated fields using full-text search: + +* **The document**: + + + ```js + class Product { + constructor(id, descriptions) { + this.id = id; + this.descriptions = descriptions; + } + } + ``` + + + + ```json + // Sample document content + { + "descriptions": { + "English": "North wind jacket", + "French": "Veste vent du nord" + } + } + ``` + + +* **The index**: + + The following index creates one dynamic field for each entry in the `descriptions` dictionary. + For example, the key `English` creates the dynamic field `Description_English`. + + Each generated dynamic field is indexed for **full-text search**. + + + ```js + class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { + super(); + + const { createField } = this.mapUtils(); + + const useSearchAnalyzerForDynamicFields = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + this.map("Products", product => { + return { + _: Object.keys(product.descriptions).map(key => + createField( + "Description_" + key, + product.descriptions[key], + { + // Index each generated dynamic field for FTS + indexing: "Search", + storage: false + })) + }; + }); + + this.storeAllFields("No"); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + this.configuration[useSearchAnalyzerForDynamicFields] = "true"; + } + } + ``` + - - - -{`const matchingDocuments = session.query({ indexName: 'Products_ByAnyField_JS' }) - // 'lastName' is a dynamic-index-field that was indexed from the document - .whereEquals('lastName', 'Doe') - .all(); -`} - - - - -{`// 'lastName' is a dynamic-index-field that was indexed from the document -from index 'Products/ByAnyField/JS' where lastName = "Doe" -`} - - - +* **Full-text search query**: -
+ Query the generated dynamic field by its field name. + In this example, the query targets the generated field `Description_English`. + Since `Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery` is set to `true`, + the searched term `"north wind"` is analyzed with the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). -## Indexing documents fields VALUES + + + ```js + const results = await session + .query({ indexName: "Products/ByLocalizedDescription/JS" }) + .search("Description_English", "north wind") + .all(); + ``` + - -#### Example - basic -This example shows: - -* Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. -* Documents can then be queried based on those indexed values. -* For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---index-a-list-of-properties) below. -**The document**: - - -{`class Product \{ - constructor(id, productType, pricePerUnit) \{ - this.id = id; - - // The VALUE of productType will be dynamically indexed - this.productType = productType; - this.pricePerUnit = pricePerUnit; - \} -\} -`} - - + + ```sql + from index "Products/ByLocalizedDescription/JS" + where search(Description_English, "north wind") + ``` + + - - -{`// Sample document content -\{ - "productType": "Electronics", - "pricePerUnit": 23 -\} -`} - - +--- -**The index**: + + +### Configuring analyzers for dynamic fields -* The following index will index the **value** of document field 'productType'. +The `createField` options object does **not** let you specify a custom analyzer directly. +However, you can still configure which analyzer RavenDB will use for dynamic fields in the index definition by either: + +* Configuring an analyzer for a specific generated dynamic field name. +* Configuring `ALL_FIELDS` as a fallback for fields that do not have their own explicit analyzer configuration. + +Use `ALL_FIELDS` with care, since it can affect any field that does not have its own explicit analyzer configuration. -* This value will be the dynamic-index-field name on which you can query. - e.g. Field value `Electronics` will be the dynamic-index-field. +--- - - - -{`class Products_ByProductType extends AbstractCsharpIndexCreationTask { - constructor () { - super(); +#### Configure analyzer for a specific dynamic field - // The field name will be the value of document field 'productType' - // The field terms will be derived from document field 'pricePerUnit' - this.map = "docs.Products.Select(p => new { " + - " _ = this.CreateField(p.productType, p.pricePerUnit) " + - "})"; - } -} -`} - - - - -{`class Products_ByProductType_JS extends AbstractJavaScriptIndexCreationTask { - constructor () { + +```js +class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { super(); const { createField } = this.mapUtils(); - this.map("Products", p => { + this.map("Products", product => { return { - _: [ - // The field name will be the value of document field 'productType' - // The field terms will be derived from document field 'pricePerUnit' - createField(p.productType, p.pricePerUnit, { - indexing: "Search", - storage: false, - termVector: null - }) - ] + _: Object.keys(product.descriptions).map(key => + createField( + "Description_" + key, + product.descriptions[key], + { + indexing: "Search", + storage: false + })) }; }); - } -} -`} - - - - -**The query**: - -* To get all documents of some product type having a specific price per unit use: - - - - -{`const matchingDocuments = session.query({ indexName: 'Products_ByProductType' }) - // 'Electronics' is the dynamic-index-field that was indexed from document field 'productType' - .whereEquals('Electronics', 23) - .all(); -`} - - - - -{`// 'Electronics' is the dynamic-index-field that was indexed from document field 'productType' -from index 'Products/ByProductType' where Electronics = 23 -`} - - - - - - -#### Example - list -The following allows you to: - -* Index **values** from items in a list -* After index is deployed, any item added this list in the document will be dynamically indexed as well. -**The document**: - - -{`class Product \{ - constructor(id, name, attributes) \{ - this.id = id; - this.name = name; - - // For each element in this list, the VALUE of property 'propName' will be dynamically indexed - // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields - this.attributes = attributes; - \} -\} - -class Attribute \{ - constructor(propName, propValue) \{ - this.propName = propName; - this.propValue = propValue; - \} -\} -`} - - - - - -{`// Sample document content -\{ - "name": "SomeName", - "attributes": [ - \{ - "propName": "Color", - "propValue": "Blue" - \}, - \{ - "propName": "Width", - "propValue": "10" - \}, - \{ - "propName": "Length", - "propValue": "20" - \}, - ... - ] -\} -`} - - - -**The index**: - -* The following index will create a dynamic-index-field per item in the document's `attributes` list. - New items added to the attributes list after index creation time will be dynamically indexed as well. - -* The actual dynamic-index-field name on which you can query will be the item's propName **value**. - e.g. 'propName' value `Width` will be a dynamic-index-field. - - - - -{`class Attributes_ByName extends AbstractCsharpIndexCreationTask -{ - constructor () { - super(); + this.storeAllFields("No"); - // For each attribute item, the field name will be the value of field 'propName' - // The field terms will be derived from field 'propValue' - // A regular-index-field (Name) is defined as well - this.map = - "docs.Products.Select(p => new { " + - " _ = p.attributes.Select(item => this.CreateField(item.propName, item.propValue)), " + - " Name = p.name " + - "})"; + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + this.analyze("Description_English", "StopAnalyzer"); } } -`} - +``` - - -{`class Attributes_ByName_JS extends AbstractJavaScriptIndexCreationTask { - constructor () { + +#### Configure a fallback analyzer for all fields + + +```js +class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { + constructor() { super(); const { createField } = this.mapUtils(); - this.map("Products", p => { + this.map("Products", product => { return { - // For each item, the field name will be the value of field 'propName' - // The field terms will be derived from field 'propValue' - _: p.attributes.map(item => createField(item.propName, item.propValue, { - indexing: "Search", - storage: true, - termVector: null - })), - - // A regular-index-field can be defined as well: - Name: p.name + _: Object.keys(product.descriptions).map(key => + createField( + "Description_" + key, + product.descriptions[key], + { + indexing: "Search", + storage: false + })) }; }); - } -} -`} - - - - -**The query**: -* To get all documents matching a specific attribute property use: + this.storeAllFields("No"); - - - -{`const matchingDocuments = session.query({ indexName: 'Attributes/ByName' }) - // 'Width' is a dynamic-index-field that was indexed from the attributes list - .whereEquals('Width', 10) - .all(); -`} - - - - -{`// 'Width' is a dynamic-index-field that was indexed from the attributes list -from index 'Attributes/ByName' where Width = 10 -`} - + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + this.analyze(CONSTANTS.Documents.Indexing.Fields.ALL_FIELDS, "StopAnalyzer"); + } +} +``` - - - + + + + +### Which analyzer is used for the query term? -## CreateField syntax +The following applies when a `search()` query targets a **dynamic field**: -#### Syntax for LINQ-index: +| Index configuration | [Configuration key](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) | Analyzer used for the query term | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| No analyzer is explicitly configured for the generated field name | `false` | The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) | +| No analyzer is explicitly configured for the generated field name | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `false` | The analyzer resolved by the normal field fallback path,
e.g. `WhitespaceAnalyzer` | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| The generated field name is explicitly configured,
e.g. `this.analyze("Description_English", "StopAnalyzer")` | `false` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +| The generated field name is explicitly configured,
e.g. `this.analyze("Description_English", "StopAnalyzer")` | `true` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | - - -{`object CreateField(string name, object value); +
-object CreateField(string name, object value, bool stored, bool analyzed); + -object CreateField(string name, object value, CreateFieldOptions options); -`} - - + -#### Syntax for JavaScript-index: +#### Syntax: - - -{`createField(fieldName, fieldValue, options); // returns object -`} - + +```js +createField(fieldName, fieldValue, options); // returns object +``` -| Parameters | Type | Description | -|----------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **fieldName** | `string` | Name of the dynamic-index-field | -| **fieldValue** | `object` | Value of the dynamic-index-field
The field Terms are derived from this value. | -| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorate.Yes` | -| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing.Default` (default value)
`false` - `FieldIndexing.Exact`
`true` - `FieldIndexing.Search` | -| **options** | `CreateFieldOptions` | Dynamic-index-field options | +| Parameters | Type | Description | +|----------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **fieldName** | `string` | Name of the dynamic-index-field | +| **fieldValue** | `object` | Value of the dynamic-index-field
The field terms are derived from this value. | +| **options** | `object` | Dynamic-index-field options | -| CreateFieldOptions | | | -|--------------------|--------------------|----------------------------------------------------------------------------| -| **Storage** | `FieldStorage?` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | -| **Indexing** | `FieldIndexing?` | Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | -| **TermVector** | `FieldTermVector?` | Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | +| options object | | | +|-----------------|--------------------|-----------------------------------------------------------------------------------| +| **storage** | `boolean` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | +| **indexing** | `FieldIndexing` | Sets the field indexing behavior.
Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | +| **termVector** | `FieldTermVector` | Sets the field term-vector behavior.
Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | @@ -536,21 +713,19 @@ object CreateField(string name, object value, CreateFieldOptions options); However, using `_` is just a convention. Any other string can be used instead. * This property is Not queryable, it is only used in the index definition syntax. - The actual dynamic-index-fields that are generated are defined by the `CreateField` method. + The actual dynamic-index-fields that are generated are defined by the `createField` method. +
+ + - -## Indexed fields & terms view - -The generated dynamic-index-fields and their indexed terms can be viewed in the **Terms View**. -Below are sample index fields & their terms generated from the last example. - +The generated dynamic index fields and their indexed terms can be viewed in the **Terms View** in Studio. +Below are sample index fields & their terms generated from [this example](../../indexes/using-dynamic-fields.mdx#example---list). + ![Figure 1. Go to terms view](../assets/dynamic-index-fields-1.png) - + ![Figure 2. Indexed fields & terms](../assets/dynamic-index-fields-2.png) - - - - + + diff --git a/versioned_docs/version-7.1/indexes/content/_using-dynamic-fields-php.mdx b/versioned_docs/version-7.1/indexes/content/_using-dynamic-fields-php.mdx index 09c58cc9fc..6992310715 100644 --- a/versioned_docs/version-7.1/indexes/content/_using-dynamic-fields-php.mdx +++ b/versioned_docs/version-7.1/indexes/content/_using-dynamic-fields-php.mdx @@ -2,537 +2,969 @@ import Admonition from '@theme/Admonition'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; +import ContentFrame from '@site/src/components/ContentFrame'; +import Panel from '@site/src/components/Panel'; -* In RavenDB different documents can have different shapes. - Documents are schemaless - new fields can be added or removed as needed. +* In RavenDB, different documents can have different shapes. + Documents are schemaless - new fields can be added or removed as needed. + For such dynamic data, you can define indexes with **dynamic-index-fields**. + +* Dynamic-index-fields are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time, + which is very useful when working with highly dynamic systems. + +* Any value type can be indexed, string, number, date, etc. + An index definition can contain both dynamic-index-fields and regular-index-fields. + +* In this article: + * [Indexing field KEYS](../../indexes/using-dynamic-fields.mdx#indexing-field-keys) + * [Example - index every field under an object](../../indexes/using-dynamic-fields.mdx#example---index-every-field-under-an-object) + * [Example - index every field](../../indexes/using-dynamic-fields.mdx#example---index-every-field) + * [Indexing field VALUES](../../indexes/using-dynamic-fields.mdx#indexing-field-values) + * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) + * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) + * [Indexing dynamic fields for full-text search](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + * [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) + * [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + * [`CreateField` syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) + * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields-terms-view) -* For such dynamic data, you can define indexes with **dynamic-index-fields**. - -* This allows querying the index on fields that aren't yet known at index creation time, - which is very useful when working on highly dynamic systems. - -* Any value type can be indexed, string, number, date, etc. - -* An index definition can contain both dynamic-index-fields and regular-index-fields. + -* In this page: + - * [Indexing documents fields KEYS](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-keys) - * [Example - index any field under object](../../indexes/using-dynamic-fields.mdx#example---index-any-field-under-object) - * [Example - index any field](../../indexes/using-dynamic-fields.mdx#example---index-any-field) - * [Indexing documents fields VALUES](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-values) - * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) - * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) - * [CreateField syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) - * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields--terms-view) + + +### Example - index every field under an object + +The following example allows you to: + * Index any field that is under the same object from the document. + * After index is deployed, any new field added to this object will be indexed as well. -## Indexing documents fields KEYS - -## Example - index any field under object +* **The document**: + + + ```php + use Ds\Map as DSMap; - + class Product + { + public ?string $id = null; -* Index any field that is under the some object from the document. -* After index is deployed, any new field added to the this object will be indexed as well. + // The KEYS under the attributes object will be dynamically indexed. + // Fields added to this object after index creation time will also get indexed. + public ?DSMap $attributes = null; - - -* **The document**: - - -{`use Ds\\Map as DSMap; - -class Product -\{ - private ?string $id = null; - - // The KEYS under the Attributes object will be dynamically indexed - // Fields added to this object after index creation time will also get indexed - public ?DSMap $attributes = null; -\} -`} - - - - -{`// Sample document content -\{ - "Attributes": \{ - "Color": "Red", - "Size": 42 - \} -\} -`} - - + // ... getters and setters + } + ``` + + + + ```json + // Sample document content + { + "attributes": { + "Color": "Red", + "Size": 42 + } + } + ``` + * **The index**: - The below index will index any field under the `Attributes` object from the document, + + The following index will index any field under the `attributes` object from the document, a dynamic-index-field will be created for each such field. New fields added to the object after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the attribute field **key**. - E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. + The actual dynamic-index-field name on which you can query will be the attribute field **key**. + E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. - - - -{`class Products_ByAttributeKey extends AbstractIndexCreationTask -{ - public function __construct() + + + ```php + class Products_ByAttributeKey extends AbstractIndexCreationTask { - parent::__construct(); - - $this->map = "from p in docs.Products select new {" . - "_ = p.attributes.Select(item => CreateField(item.Key, item.Value))" . - "}"; + public function __construct() + { + parent::__construct(); + + // Call 'CreateField' to generate dynamic-index-fields + // from the attributes object keys. + // + // Using '_' is just a convention. + // Any other string can be used instead of '_'. + // + // The actual field name will be 'item.Key'. + // The actual field terms will be derived from 'item.Value'. + $this->map = + "from p in docs.Products " . + "select new { " . + " _ = p.attributes.Select(item => CreateField(item.Key, item.Value)) " . + "}"; + } } -} -`} - - - - -{`class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask -{ - public function __construct() + ``` + + + ```php + class Products_ByAttributeKey_JS extends AbstractJavaScriptIndexCreationTask { - parent::__construct(); - - $this->setMaps([ - "map('Products', function (p) { " . - " return { " . - " _: Object.keys(p.attributes).map(key => createField(key, p.attributes[key], " . - " { indexing: 'Search', storage: false, termVector: null })) " . - " }; " . - "}) " - ]); + public function __construct() + { + parent::__construct(); + + $this->setMaps([ + "map('Products', function (p) { + return { + // The field name will be the key + // The field terms will be derived from the corresponding value + _: Object.keys(p.attributes) + .map(key => createField(key, p.attributes[key], null)) + }; + })" + ]); + } } -} -`} - - - - + ``` + + * **The query**: - * You can now query the generated dynamic-index fields. - * To get all documents with some 'size' use: - - - -{`$matchingDocuments = $session - ->advanced() - ->documentQuery(Product::class, Products_ByAttributeKey::class) - // 'Size' is a dynamic-index-field that was indexed from the Attributes object - ->whereEquals("Size", 42) - ->toList(); -`} - - - - -{`// 'Size' is a dynamic-index-field that was indexed from the Attributes object -from index 'Products/ByAttributeKey' where Size = 42 -`} - - - - -## Example - index any field - - + + You can now query the generated dynamic-index fields. + The `_` property is Not queryable but used only in the index definition syntax. + + To get all documents with a specific _Size_ use: + + + + ```php + /** @var array $matchingDocuments */ + $matchingDocuments = $session + ->advanced() + ->documentQuery(Product::class, Products_ByAttributeKey::class) + // 'Size' is a dynamic-index-field that was indexed from the attributes object + ->whereEquals("Size", 42) + ->toList(); + ``` + + + ```sql + // 'Size' is a dynamic-index-field that was indexed from the attributes object + from index 'Products/ByAttributeKey' where Size = 42 + ``` + + + + + Dynamic index fields are queried with _documentQuery_ or _RQL_ because the field names (e.g. `Size`, `Color`) + are generated at indexing time and are not properties on the _Product_ class. + + Use the string-based `whereEquals("Size", 42)` or RQL `where Size = 42` instead. + + + + + + +### Example - index every field + +The following example allows you to: * Define an index on a collection **without** needing any common structure between the indexed documents. - * After index is deployed, any new field added to the document will be indexed as well. - + * After the index is deployed, any new field added to any document in the collection will be indexed as well. - + -Consider whether this is really necessary, as indexing every single field can end up costing time and disk space. +* Use this approach with care. +* Indexing every field can increase indexing time, memory usage, and disk space, + especially for large or frequently updated documents. * **The document**: - - -{`class Product -\{ - private ?string $id = null; - - // All KEYS in the document will be dynamically indexed - // Fields added to the document after index creation time will also get indexed - public ?string $firstName = null; - public ?string $lastName = null; - public ?string $title = null; - - // ... getters and setters -\} -`} - - + + + ```php + class Product + { + public ?string $id = null; - - -{`// Sample document content - \{ - "FirstName": "John", - "LastName": "Doe", - "Title": "Engineer", + // All KEYS in the document will be dynamically indexed. + // Fields added to the document after index creation time will also get indexed. + public ?string $firstName = null; + public ?string $lastName = null; + public ?string $title = null; // ... -\} -`} - - + + // ... getters and setters + } + ``` + + + + ```json + // Sample document content + { + "firstName": "John", + "lastName": "Doe", + "title": "Engineer" + // ... + } + ``` + * **The index**: - The below index will index any field from the document, + + The following index will index any field from the document, a dynamic-index-field will be created for each field. New fields added to the document after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the field **key**. - E.g., Keys `FirstName` & `LastName` will become the actual dynamic-index-fields. - - - - -{`class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask -{ - public function __construct() + The actual dynamic-index-field name on which you can query will be the field **key**. + E.g., Keys `firstName` & `lastName` will become the actual dynamic-index-fields. + + The example uses a JavaScript index because it enumerates all top-level document fields at indexing time with `Object.keys(...)`. + LINQ indexes typically create dynamic fields from a known enumerable structure, such as a dictionary or collection. + + + + ```php + class Products_ByAnyField_JS extends AbstractJavaScriptIndexCreationTask { - parent::__construct(); - - // This will index EVERY FIELD under the top level of the document - $this->setMaps([ - "map('Products', function (p) { - return { - _: Object.keys(p).map(key => createField(key, p[key], - { indexing: 'Search', storage: true, termVector: null })) - } - })" - ]); + public function __construct() + { + parent::__construct(); + + // This will index EVERY FIELD under the top level of the document + $this->setMaps([ + "map('Products', function (p) { + return { + _: Object.keys(p).map(key => createField(key, p[key], null)) + }; + })" + ]); + } } -} -`} - - - + ``` + + * **The query**: - * To get all documents with some 'LastName' use: - - - -{`$matchingDocuments = $session - ->advanced() - ->documentQuery(Product::class, Products_ByAnyField_JS::class) - // 'LastName' is a dynamic-index-field that was indexed from the document - ->whereEquals("LastName", "Doe") - ->toList(); -`} - - - - -{`// 'LastName' is a dynamic-index-field that was indexed from the document -from index 'Products/ByAnyField/JS' where LastName = "Doe" -`} - - - + + To get all documents with a specific _lastName_ use: + + + + ```php + /** @var array $matchingDocuments */ + $matchingDocuments = $session + ->advanced() + ->documentQuery(Product::class, Products_ByAnyField_JS::class) + // 'lastName' is a dynamic-index-field that was indexed from the document + ->whereEquals("lastName", "Doe") + ->toList(); + ``` + + + ```sql + // 'lastName' is a dynamic-index-field that was indexed from the document + from index 'Products/ByAnyField/JS' where lastName = "Doe" + ``` + + + + + + + + + + + +### Example - basic + + +The following example shows: + * Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. + * Documents can then be queried based on those indexed values. + * For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---list) below. + +* **The document**: + + + ```php + class Product + { + public ?string $id = null; + // The VALUE of productType will be dynamically indexed + public ?string $productType = null; + public ?int $pricePerUnit = null; -## Indexing documents fields VALUES + // ... getters and setters + } + ``` + + + + ```json + // Sample document content + { + "productType": "Electronics", + "pricePerUnit": 23 + } + ``` + -## Example - basic +* **The index**: + + The following index will index the **value** of document field 'productType'. + + This value will be the dynamic-index-field name on which you can query. + E.g., Field value `Electronics` will be the dynamic-index-field. - + + + ```php + class Products_ByProductType extends AbstractIndexCreationTask + { + public function __construct() + { + parent::__construct(); + + // Call 'CreateField' to generate the dynamic-index-fields. + // The field name will be the value of document field 'productType'. + // The field terms will be derived from document field 'pricePerUnit'. + $this->map = + "from p in docs.Products " . + "select new { " . + " _ = CreateField(p.productType, p.pricePerUnit) " . + "}"; + } + } + ``` + + + ```php + class Products_ByProductType_JS extends AbstractJavaScriptIndexCreationTask + { + public function __construct() + { + parent::__construct(); + + $this->setMaps([ + "map('Products', function (p) { + return { + // The field name will be the value of document field 'productType' + // The field terms will be derived from document field 'pricePerUnit' + _: createField(p.productType, p.pricePerUnit, null) + }; + })" + ]); + } + } + ``` + + -* Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. -* Documents can then be queried based on those indexed values. +* **The query**: + + To get all documents of some product type having a specific price per unit use: + + + + ```php + /** @var array $matchingDocuments */ + $matchingDocuments = $session + ->advanced() + ->documentQuery(Product::class, Products_ByProductType::class) + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + ->whereEquals("Electronics", 23) + ->toList(); + ``` + + + ```sql + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'productType' + from index 'Products/ByProductType' where Electronics = 23 + ``` + + + + + + + +### Example - list + +The following example allows you to: + * Index **values** from items in a list + * After index is deployed, any item added to this list in the document will be dynamically indexed as well. * **The document**: - - -{`class Product -\{ - public ?string $id = null; - - // The VALUE of ProductType will be dynamically indexed - public ?string $productType = null; - public ?int $pricePerUnit = null; - - // ... getters and setters -\} -`} - - + + + ```php + class Product + { + public ?string $id = null; + public ?string $name = null; - - -{`// Sample document content -\{ - "ProductType": "Electronics", - "PricePerUnit": 23 -\} -`} - - + // For each element in this list, + // the VALUE of property 'propName' will be dynamically indexed. + // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields. + public ?AttributeList $attributes = null; + + // ... getters and setters + } + + class Attribute + { + public ?string $propName = null; + public ?string $propValue = null; + + // ... getters and setters + } + + class AttributeList extends TypedList + { + protected function __construct() + { + parent::__construct(Attribute::class); + } + } + ``` + + + + ```json + // Sample document content + { + "name": "SomeName", + "attributes": [ + { + "propName": "Color", + "propValue": "Blue" + }, + { + "propName": "Width", + "propValue": "10" + }, + { + "propName": "Length", + "propValue": "20" + } + // ... + ] + } + ``` + * **The index**: - The below index will index the **value** of document field 'ProductType'. + + The following index will create a dynamic-index-field per item in the document's `attributes` list. + New items added to the attributes list after index creation time will be dynamically indexed as well. - This value will be the dynamic-index-field name on which you can query. - E.g., Field value `Electronics` will be the dynamic-index-field. + The actual dynamic-index-field name on which you can query will be the item's propName **value**. + E.g., 'propName' value `Width` will be a dynamic-index-field. + + + + ```php + class Attributes_ByName extends AbstractIndexCreationTask + { + public function __construct() + { + parent::__construct(); + + // Define the dynamic-index-fields by calling 'CreateField'. + // A dynamic-index-field will be generated for each item in 'attributes'. + // + // For each item, the field name will be the value of field 'propName'. + // The field terms will be derived from field 'propValue'. + $this->map = + "from p in docs.Products " . + "select new { " . + " _ = p.attributes.Select(item => CreateField(item.propName, item.propValue)), " . + " Name = p.name " . + "}"; + } + } + ``` + + + ```php + class Attributes_ByName_JS extends AbstractJavaScriptIndexCreationTask + { + public function __construct() + { + parent::__construct(); + + $this->setMaps([ + "map('Products', function (p) { + return { + // For each item, + // the field name will be the value of field 'propName' + // The field terms will be derived from field 'propValue' + _: p.attributes.map(item => createField( + item.propName, item.propValue, null)), + + // A regular index field can be defined as well: + Name: p.name + }; + })" + ]); + } + } + ``` + + + +* **The query**: + + To get all documents matching a specific attribute property use: + + + + ```php + /** @var array $matchingDocuments */ + $matchingDocuments = $session + ->advanced() + ->documentQuery(Product::class, Attributes_ByName::class) + // 'Width' is a dynamic-index-field that was indexed from the attributes list + ->whereEquals("Width", 10) + ->toList(); + ``` + + + ```sql + // 'Width' is a dynamic-index-field that was indexed from the attributes list + from index 'Attributes/ByName' where Width = 10 + ``` + + + + + + + + + + + +Dynamic fields can be indexed for [Full-text search](../../indexes/querying/searching.mdx) by setting `FieldIndexing::search()` in `CreateFieldOptions`. + +* **At indexing time**: + At indexing time, RavenDB analyzes the dynamic field value and creates the indexed terms. + The analyzer used at indexing time is resolved from the index definition. + You can configure an analyzer for a specific generated field name, or configure `ALL_FIELDS` as a fallback. + See [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) below. + + * If the generated dynamic field name has an explicit analyzer configuration, that analyzer is used. + * Otherwise, if `ALL_FIELDS` has an analyzer configuration, RavenDB uses it as a fallback. + * If no analyzer is configured for the field, RavenDB uses the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + +* **At querying time**: + For full-text search to return the expected results, the analyzer used at indexing time and the analyzer used for the query term should produce compatible terms. + + When a `search()` query targets a dynamic field that does **not** have an explicit analyzer configuration for its generated field name, + RavenDB uses the [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) to analyze the query term by default. + + Set the [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) + configuration key to `true` to make RavenDB use the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) as the fallback analyzer for the query term instead. + + This configuration can be set at different scopes: server-wide, per database, or per index. + The example below sets it at the index scope, in the index definition. + + For a summary of how RavenDB resolves the analyzer used for the query term, + see [Which analyzer is used for the query term](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) below. + + + +--- + +The following example indexes localized product descriptions as dynamic fields and queries one of those generated fields using full-text search: + +* **The document**: + + + ```php + use Ds\Map as DSMap; + + class Product + { + public ?string $id = null; + public ?DSMap $descriptions = null; + + // ... getters and setters + } + ``` + + + + ```json + // Sample document content + { + "descriptions": { + "English": "North wind jacket", + "French": "Veste vent du nord" + } + } + ``` + + +* **The index**: + + The following index creates one dynamic field for each entry in the `descriptions` dictionary. + For example, the key `English` creates the dynamic field `Description_English`. - + Each generated dynamic field is indexed for **full-text search**. + + + + ```php + class Products_ByLocalizedDescription extends AbstractIndexCreationTask + { + private const USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public function __construct() + { + parent::__construct(); + + $this->map = + "from product in docs.Products " . + "select new { " . + " _ = product.descriptions.Select(x => " . + " CreateField(" . + " \"Description_\" + x.Key, " . + " x.Value, " . + " new CreateFieldOptions { " . + // Index each generated dynamic field for FTS + " Indexing = FieldIndexing.Search, " . + " Storage = FieldStorage.No " . + " })) " . + "}"; + + $this->storeAllFields(FieldStorage::no()); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + $this->getConfiguration()[self::USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS] = "true"; + } + } + ``` + + + ```php + class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask + { + private const USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery"; + + public function __construct() + { + parent::__construct(); + + $this->setMaps([ + "map('Products', function (product) { + return { + _: Object.keys(product.descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.descriptions[key], + { + // Index each generated dynamic field for FTS + indexing: 'Search', + storage: 'No' + }); + }) + }; + })" + ]); + + // Apply a storage default to every generated field + $fields = new IndexFieldOptionsArray(); + $allFieldsOptions = new IndexFieldOptions(); + $allFieldsOptions->setStorage(FieldStorage::no()); + $fields->offsetSet(DocumentsIndexingFields::ALL_FIELDS, $allFieldsOptions); + $this->setFields($fields); + + // Set this configuration key to true so that search() queries on dynamic fields + // will use the 'Default Search Analyzer' when the generated field name + // has no explicit analyzer configuration. + $this->getConfiguration()[self::USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS] = "true"; + } + } + ``` + + + +* **Full-text search query**: + + Query the generated dynamic field by its field name. + + In this example, the query targets the generated field `Description_English`. + + Since `Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery` is set to `true`, + the searched term `"north wind"` is analyzed with the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + + + + ```php + /** @var array $results */ + $results = $session->advanced() + ->documentQuery(Product::class, Products_ByLocalizedDescription::class) + ->search("Description_English", "north wind") + ->toList(); + ``` + + + + ```sql + from index "Products/ByLocalizedDescription" + where search(Description_English, "north wind") + ``` + + + +--- + + + +### Configuring analyzers for dynamic fields + +`CreateFieldOptions` does **not** let you specify a custom analyzer directly. +However, you can still configure which analyzer RavenDB will use for dynamic fields in the index definition by either: + +* Configuring an analyzer for a specific generated dynamic field name. +* Configuring `ALL_FIELDS` as a fallback for fields that do not have their own explicit analyzer configuration. + +Use `ALL_FIELDS` with care, since it can affect any field that does not have its own explicit analyzer configuration. + +--- + +#### Configure analyzer for a specific dynamic field + + - -{`class Products_ByProductType extends AbstractIndexCreationTask +```php +class Products_ByLocalizedDescription extends AbstractIndexCreationTask { public function __construct() { parent::__construct(); - // Call 'CreateField' to generate the dynamic-index-fields - // The field name will be the value of document field 'ProductType' - // The field terms will be derived from document field 'PricePerUnit' - $this->map = "docs.Products.Select(p => new { " . - " _ = this.CreateField(p.productType, p.pricePerUnit) " . - "})"; + $this->map = + "from product in docs.Products " . + "select new { " . + " _ = product.descriptions.Select(x => " . + " CreateField(" . + " \"Description_\" + x.Key, " . + " x.Value, " . + " new CreateFieldOptions { " . + " Indexing = FieldIndexing.Search, " . + " Storage = FieldStorage.No " . + " })) " . + "}"; + + $this->storeAllFields(FieldStorage::no()); + + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + $this->analyze("Description_English", "StopAnalyzer"); } } -`} - +``` - - -{`class Products_ByProductType_JS extends AbstractJavaScriptIndexCreationTask + +```php +class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { public function __construct() { parent::__construct(); $this->setMaps([ - "map('Products', function (p) { + "map('Products', function (product) { return { - _: createField(p.ProductType, p.PricePerUnit, - { indexing: 'Search', storage: true, termVector: null }) + _: Object.keys(product.descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; })" ]); - } -} -`} - - - - -* **The query**: - * To get all documents of some product type having a specific price per unit use: - - - -{`$matchingDocuments = $session - ->advanced() - ->documentQuery(Product::class, Products_ByProductType::class) -// 'Electronics' is the dynamic-index-field that was indexed from document field 'ProductType' -->whereEquals("Electronics", 23) -->toList(); -`} - - - - -{`// 'Electronics' is the dynamic-index-field that was indexed from document field 'ProductType' -from index 'Products/ByProductType' where Electronics = 23 -`} - - - - -## Example - list - - -* Index **values** from items in a list -* After index is deployed, any item added this list in the document will be dynamically indexed as well. + // Apply a storage default to every generated field + + // Configure an analyzer for a known generated dynamic field name + // ============================================================== + $fields = new IndexFieldOptionsArray(); - + $allFieldsOptions = new IndexFieldOptions(); + $allFieldsOptions->setStorage(FieldStorage::no()); + $fields->offsetSet(DocumentsIndexingFields::ALL_FIELDS, $allFieldsOptions); -* **The document**: - - -{`class Product -\{ - public ?string $id = null; - public ?string $name = null; - - // For each element in this list, the VALUE of property 'PropName' will be dynamically indexed - // e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields - public ?AttributeList $attributes = null; - - // ... getters and setters -\} - -class Attribute -\{ - public ?string $propName = null; - public ?string $propValue = null; - - // ... getters and setters -\} - -class AttributeList extends TypedList -\{ - protected function __construct() - \{ - parent::__construct(Attribute::class); - \} -\} -`} - - + $englishFieldOptions = new IndexFieldOptions(); + $englishFieldOptions->setAnalyzer("StopAnalyzer"); + $fields->offsetSet("Description_English", $englishFieldOptions); - - -{`// Sample document content -\{ -Name": "SomeName", -Attributes": [ - \{ - "PropName": "Color", - "PropValue": "Blue" - \}, - \{ - "PropName": "Width", - "PropValue": "10" - \}, - \{ - "PropName": "Length", - "PropValue": "20" - \}, - ... - -\} -`} - + $this->setFields($fields); + } +} +``` + -* **The index**: - The below index will create a dynamic-index-field per item in the document's `Attributes` list. - New items added to the Attributes list after index creation time will be dynamically indexed as well. - - The actual dynamic-index-field name on which you can query will be the item's PropName **value**. - E.g., 'PropName' value `width` will be a dynamic-index-field. +#### Configure a fallback analyzer for all fields - + - -{`class Attributes_ByName extends AbstractIndexCreationTask +```php +class Products_ByLocalizedDescription extends AbstractIndexCreationTask { public function __construct() { parent::__construct(); - // Define the dynamic-index-fields by calling CreateField - // A dynamic-index-field will be generated for each item in the Attributes list + $this->map = + "from product in docs.Products " . + "select new { " . + " _ = product.descriptions.Select(x => " . + " CreateField(" . + " \"Description_\" + x.Key, " . + " x.Value, " . + " new CreateFieldOptions { " . + " Indexing = FieldIndexing.Search, " . + " Storage = FieldStorage.No " . + " })) " . + "}"; - // For each item, the field name will be the value of field 'PropName' - // The field terms will be derived from field 'PropValue' + $this->storeAllFields(FieldStorage::no()); - $this->map = - "docs.Products.Select(p => new { " . - " _ = p.attributes.Select(item => this.CreateField(item.propName, item.propValue)), " . - " Name = p.name " . - "})"; + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + $this->analyze(DocumentsIndexingFields::ALL_FIELDS, "StopAnalyzer"); } } -`} - +``` - - -{`class Attributes_ByName_JS extends AbstractJavaScriptIndexCreationTask + +```php +class Products_ByLocalizedDescription_JS extends AbstractJavaScriptIndexCreationTask { public function __construct() { parent::__construct(); $this->setMaps([ - "map('Products', function (p) { + "map('Products', function (product) { return { - _: p.Attributes.map(item => createField(item.PropName, item.PropValue, - { indexing: 'Search', storage: true, termVector: null })), - Name: p.Name + _: Object.keys(product.descriptions) + .map(function (key) { + return createField( + 'Description_' + key, + product.descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; })" ]); + + // Use this as a fallback for fields that do not have + // their own explicit analyzer configuration + // ================================================== + $fields = new IndexFieldOptionsArray(); + $allFieldsOptions = new IndexFieldOptions(); + $allFieldsOptions->setStorage(FieldStorage::no()); + $allFieldsOptions->setAnalyzer("StopAnalyzer"); + $fields->offsetSet(DocumentsIndexingFields::ALL_FIELDS, $allFieldsOptions); + $this->setFields($fields); } } -`} - - - - -* **The query**: - To get all documents matching a specific attribute property use: - - - -{`/** @var array $matchingDocuments */ -$matchingDocuments = $session - ->advanced() - ->documentQuery(Product::class, Attributes_ByName::class) - // 'Width' is a dynamic-index-field that was indexed from the Attributes list - ->whereEquals("Width", 10) - ->toList(); -`} - - - - -{`// 'Width' is a dynamic-index-field that was indexed from the Attributes list -from index 'Attributes/ByName' where Width = 10 -`} - +``` + + + + +### Which analyzer is used for the query term? +The following applies when a `search()` query targets a **dynamic field**: -## CreateField syntax +| Index configuration | [Configuration key](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) | Analyzer used for the query term | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| No analyzer is explicitly configured for the generated field name | `false` | The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) | +| No analyzer is explicitly configured for the generated field name | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `false` | The analyzer resolved by the normal field fallback path,
e.g. `WhitespaceAnalyzer` | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| The generated field name is explicitly configured,
e.g. `$this->analyze("Description_English", "StopAnalyzer")` | `false` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +| The generated field name is explicitly configured,
e.g. `$this->analyze("Description_English", "StopAnalyzer")` | `true` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | - - -{`object CreateField(string name, object value); +
+ +
+ + + +#### Syntax for LINQ-index: + + +```php +object CreateField(string name, object value); object CreateField(string name, object value, bool stored, bool analyzed); object CreateField(string name, object value, CreateFieldOptions options); -`} - +``` -| Parameters | Type | Description | -|------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **name** | `string` | Name of the dynamic-index-field | -| **value** | `object` | Value of the dynamic-index-field
The field Terms are derived from this value. | -| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`False` - will set `FieldStorage.NO` (default value)
`True` - will set `FieldStorate.YES` | -| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`None` - `FieldIndexing.Default` (default value)
`False` - `FieldIndexing.Exact`
`True` - `FieldIndexing.Search` | -| **options** | `CreateFieldOptions` | Dynamic-index-field options | +#### Syntax for JavaScript-index: -| CreateFieldOptions | | | -|--------------------|--------------------|----------------------------------------------------------------------------| -| **Storage** | `FieldStorage` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | -| **Indexing** | `FieldIndexing` | | -| **TermVector** | `FieldTermVector` | | + +```js +createField(fieldName, fieldValue, options); // returns object +``` + + +| Parameters | Type | Description | +|----------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **fieldName** | `string` | Name of the dynamic-index-field | +| **fieldValue** | `object` | Value of the dynamic-index-field
The field terms are derived from this value. | +| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage::no()` (default value)
`true` - will set `FieldStorage::yes()` | +| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing::default()` (default value)
`false` - `FieldIndexing::exact()`
`true` - `FieldIndexing::search()` | +| **options** | `CreateFieldOptions` | Dynamic-index-field options | + +| CreateFieldOptions | | | +|--------------------|--------------------|-----------------------------------------------------------------------------------| +| **Storage** | `FieldStorage?` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | +| **Indexing** | `FieldIndexing?` | Sets the field indexing behavior.
Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | +| **TermVector** | `FieldTermVector?` | Sets the field term-vector behavior.
Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | @@ -544,17 +976,15 @@ object CreateField(string name, object value, CreateFieldOptions options); +
+ + - -## Indexed fields & terms view - -The generated dynamic-index-fields and their indexed terms can be viewed in the **Terms View**. -Below are sample index fields & their terms generated from the last example. - +The generated dynamic index fields and their indexed terms can be viewed in the **Terms View** in Studio. +Below are sample index fields & their terms generated from [this example](../../indexes/using-dynamic-fields.mdx#example---list). + ![Figure 1. Go to terms view](../assets/dynamic-index-fields-1.png) - + ![Figure 2. Indexed fields & terms](../assets/dynamic-index-fields-2.png) - - - - + + diff --git a/versioned_docs/version-7.1/indexes/content/_using-dynamic-fields-python.mdx b/versioned_docs/version-7.1/indexes/content/_using-dynamic-fields-python.mdx index 69514a29e2..6862d18067 100644 --- a/versioned_docs/version-7.1/indexes/content/_using-dynamic-fields-python.mdx +++ b/versioned_docs/version-7.1/indexes/content/_using-dynamic-fields-python.mdx @@ -1,490 +1,886 @@ import Admonition from '@theme/Admonition'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -import CodeBlock from '@theme/CodeBlock'; +import ContentFrame from '@site/src/components/ContentFrame'; +import Panel from '@site/src/components/Panel'; -* In RavenDB different documents can have different shapes. - Documents are schemaless - new fields can be added or removed as needed. - -* For such dynamic data, you can define indexes with **dynamic-index-fields**. - -* This allows querying the index on fields that aren't yet known at index creation time, - which is very useful when working on highly dynamic systems. - -* Any value type can be indexed, string, number, date, etc. - -* An index definition can contain both dynamic-index-fields and regular-index-fields. - -* In this page: - - * [Indexing documents fields KEYS](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-keys) - * [Example - index any field under object](../../indexes/using-dynamic-fields.mdx#example---index-any-field-under-object) - * [Example - index any field](../../indexes/using-dynamic-fields.mdx#example---index-any-field) - * [Indexing documents fields VALUES](../../indexes/using-dynamic-fields.mdx#indexing-documents-fields-values) - * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) - * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) - * [CreateField syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) - * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields--terms-view) +* In RavenDB, different documents can have different shapes. + Documents are schemaless - new fields can be added or removed as needed. + For such dynamic data, you can define indexes with **dynamic-index-fields**. + +* Dynamic-index-fields are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time, + which is very useful when working with highly dynamic systems. + +* Any value type can be indexed, string, number, date, etc. + An index definition can contain both dynamic-index-fields and regular-index-fields. + +* In this article: + * [Indexing field KEYS](../../indexes/using-dynamic-fields.mdx#indexing-field-keys) + * [Example - index every field under an object](../../indexes/using-dynamic-fields.mdx#example---index-every-field-under-an-object) + * [Example - index every field](../../indexes/using-dynamic-fields.mdx#example---index-every-field) + * [Indexing field VALUES](../../indexes/using-dynamic-fields.mdx#indexing-field-values) + * [Example - basic](../../indexes/using-dynamic-fields.mdx#example---basic) + * [Example - list](../../indexes/using-dynamic-fields.mdx#example---list) + * [Indexing dynamic fields for full-text search](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + * [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) + * [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + * [`CreateField` syntax](../../indexes/using-dynamic-fields.mdx#createfield-syntax) + * [Indexed fields & terms view](../../indexes/using-dynamic-fields.mdx#indexed-fields-terms-view) -## Indexing documents fields KEYS - -## Example - index any field under object + - - -* Index any field that is under the some object from the document. -* After index is deployed, any new field added to the this object will be indexed as well. + + +### Example - index every field under an object + +The following example allows you to: + * Index any field that is under the same object from the document. + * After index is deployed, any new field added to this object will be indexed as well. * **The document**: - - -{`class Product: - def __init__(self, Id: str = None, attributes: Dict[str, object] = None): - self.Id = Id - - # The KEYS under the Attributes object will be dynamically indexed - # Fields added to this object after index creation time will also get indexed - self.attributes = attributes -`} - - - - -{`// Sample document content -\{ - "Attributes": \{ - "Color": "Red", - "Size": 42 - \} -\} -`} - - + + + ```python + class Product: + def __init__(self, Id: str = None, Attributes: Dict[str, object] = None): + self.Id = Id + + # The KEYS under the Attributes object will be dynamically indexed + # Fields added to this object after index creation time will also get indexed + self.Attributes = Attributes + ``` + + + + ```json + // Sample document content + { + "Attributes": { + "Color": "Red", + "Size": 42 + } + } + ``` + * **The index**: - The below index will index any field under the `Attributes` object from the document, + + The following index will index any field under the `Attributes` object from the document, a dynamic-index-field will be created for each such field. New fields added to the object after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the attribute field **key**. - E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. - - - - -{`class Products_ByAttributeKey(AbstractIndexCreationTask): - def __init__(self): - super().__init__() - self.map = ( - "from p in docs.Products select new {" - "_ = p.attributes.Select(item => CreateField(item.Key, item.Value))" - "}" - ) -`} - - - - -{`class Products_ByAttributeKey_JS(AbstractJavaScriptIndexCreationTask): - def __init__(self): - super().__init__() - self.maps = { - """ - map('Products', function (p) { - return { - _: Object.keys(p.attributes).map(key => createField(key, p.attributes[key], - { indexing: 'Search', storage: true, termVector: null })) - }; - }) - """ - } -`} - - - - + The actual dynamic-index-field name on which you can query will be the attribute field **key**. + E.g., Keys `Color` & `Size` will become the actual dynamic-index-fields. + + + + ```python + class Products_ByAttributeKey(AbstractIndexCreationTask): + def __init__(self): + super().__init__() + self.map = ( + "from p in docs.Products select new { " + # Call 'CreateField' to generate dynamic-index-fields + # from the Attributes object keys. + + # Using '_' is just a convention. + # Any other string can be used instead of '_' + + # The actual field name will be 'item.Key' + # The actual field terms will be derived from 'item.Value' + "_ = p.Attributes.Select(item => CreateField(item.Key, item.Value)) " + "}" + ) + ``` + + + ```python + class Products_ByAttributeKey_JS(AbstractJavaScriptIndexCreationTask): + def __init__(self): + super().__init__() + self.maps = [ + """ + map('Products', function (p) { + return { + // The field name will be the key + // The field terms will be derived from the corresponding value + _: Object.keys(p.Attributes) + .map(key => createField(key, p.Attributes[key])) + }; + }) + """ + ] + ``` + + * **The query**: - * You can now query the generated dynamic-index fields. - * To get all documents with some 'size' use: - - - -{`matching_documents = list( - session.query_index_type(Products_ByAttributeKey, Product) - # 'size' is a dynamic-index-field that was indexed from the attributes object - .where_equals("size", 42) -) -`} - - - - -{`// 'Size' is a dynamic-index-field that was indexed from the Attributes object -from index 'Products/ByAttributeKey' where Size = 42 -`} - - - - -## Example - index any field - - + + You can now query the generated dynamic-index fields. + The `_` property is Not queryable but used only in the index definition syntax. + + To get all documents with a specific _Size_ use: + + + + ```python + matching_documents = list( + session.advanced + .document_query_from_index_type(Products_ByAttributeKey, Product) + # 'Size' is a dynamic-index-field that was indexed from the Attributes object + .where_equals("Size", 42) + ) + ``` + + + ```sql + // 'Size' is a dynamic-index-field that was indexed from the Attributes object + from index 'Products/ByAttributeKey' where Size = 42 + ``` + + + + + Dynamic index fields are queried with _document_query_ or _RQL_ because the field names (e.g. `Size`, `Color`) + are generated at indexing time and are not properties on the _Product_ class. + + Use the string-based `where_equals("Size", 42)` or RQL `where Size = 42`. + + + + + + +### Example - index every field + +The following example allows you to: * Define an index on a collection **without** needing any common structure between the indexed documents. - * After index is deployed, any new field added to the document will be indexed as well. - + * After the index is deployed, any new field added to any document in the collection will be indexed as well. - + -Consider whether this is really necessary, as indexing every single field can end up costing time and disk space. +* Use this approach with care. +* Indexing every field can increase indexing time, memory usage, and disk space, + especially for large or frequently updated documents. * **The document**: - - -{`class Product: - def __init__(self, Id: str = None, first_name: str = None, last_name: str = None, title: str = None): - self.Id = Id - - # All KEYS in the document will be dynamically indexes - # Fields added to the document after index creation time wil also get indexed - self.first_name = first_name - self.last_name = last_name - self.title = title - # ... -`} - - - - - -{`// Sample document content - \{ + + + ```python + class Product: + def __init__( + self, + Id: str = None, + FirstName: str = None, + LastName: str = None, + Title: str = None, + ): + self.Id = Id + + # All KEYS in the document will be dynamically indexed + # Fields added to the document after index creation time will also get indexed + self.FirstName = FirstName + self.LastName = LastName + self.Title = Title + # ... + ``` + + + + ```json + // Sample document content + { "FirstName": "John", "LastName": "Doe", "Title": "Engineer", // ... -\} -`} - - + } + ``` + * **The index**: - The below index will index any field from the document, + + The following index will index any field from the document, a dynamic-index-field will be created for each field. New fields added to the document after index creation time will be dynamically indexed as well. - The actual dynamic-index-field name on which you can query will be the field **key**. - E.g., Keys `FirstName` & `LastName` will become the actual dynamic-index-fields. - - - - -{`class Products_ByAnyField_JS(AbstractJavaScriptIndexCreationTask): - def __init__(self): - super().__init__() - # This will index EVERY FIELD under the top level of the document - self.maps = { - """ - map('Products', function (p) { - return { - _: Object.keys(p).map(key => createField(key, p[key], - { indexing: 'Search', storage: true, termVector: null })) - } - }) - """ - } -`} - - - + The actual dynamic-index-field name on which you can query will be the field **key**. + E.g., Keys `FirstName` & `LastName` will become the actual dynamic-index-fields. + + The example uses a JavaScript index because it enumerates all top-level document fields at indexing time with `Object.keys(...)`. + LINQ indexes typically create dynamic fields from a known enumerable structure, such as a dictionary or collection. + + + + ```python + class Products_ByAnyField_JS(AbstractJavaScriptIndexCreationTask): + def __init__(self): + super().__init__() + # This will index EVERY FIELD under the top level of the document + self.maps = [ + """ + map('Products', function (p) { + return { + _: Object.keys(p).map(key => createField(key, p[key])) + }; + }) + """ + ] + ``` + + * **The query**: - * To get all documents with some 'LastName' use: - - - -{`# 'last_name' is a dynamic-index-field that was indexed from the document -matching_documents = list( - session.query_index_type(Products_ByAnyField_JS, Product).where_equals("last_name", "Doe") -) -`} - - - - -{`// 'LastName' is a dynamic-index-field that was indexed from the document -from index 'Products/ByAnyField/JS' where LastName = "Doe" -`} - - - + + To get all documents with a specific _LastName_ use: + + + + ```python + matching_documents = list( + session.advanced + .document_query_from_index_type(Products_ByAnyField_JS, Product) + # 'LastName' is a dynamic-index-field that was indexed from the document + .where_equals("LastName", "Doe") + ) + ``` + + + ```sql + // 'LastName' is a dynamic-index-field that was indexed from the document + from index 'Products/ByAnyField/JS' where LastName = "Doe" + ``` + + + + + + + + + + + +### Example - basic + + +The following example shows: + * Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. + * Documents can then be queried based on those indexed values. + * For a more practical usage see the [Example](../../indexes/using-dynamic-fields.mdx#example---list) below. + + +* **The document**: + + + ```python + class Product: + def __init__(self, Id: str = None, ProductType: str = None, PricePerUnit: int = None): + self.Id = Id + + # The VALUE of ProductType will be dynamically indexed + self.ProductType = ProductType + self.PricePerUnit = PricePerUnit + ``` + + + + ```json + // Sample document content + { + "ProductType": "Electronics", + "PricePerUnit": 23 + } + ``` + + +* **The index**: + + The following index will index the **value** of document field 'ProductType'. + + This value will be the dynamic-index-field name on which you can query. + E.g., Field value `Electronics` will be the dynamic-index-field. + + + + ```python + class Products_ByProductType(AbstractIndexCreationTask): + def __init__(self): + super().__init__() + self.map = ( + "from p in docs.Products select new { " + # Call 'CreateField' to generate the dynamic-index-fields + # The field name will be the value of document field 'ProductType' + # The field terms will be derived from document field 'PricePerUnit' + "_ = CreateField(p.ProductType, p.PricePerUnit) " + "}" + ) + ``` + + + ```python + class Products_ByProductType_JS(AbstractJavaScriptIndexCreationTask): + def __init__(self): + super().__init__() + self.maps = [ + """ + map('Products', function (p) { + return { + // The field name will be the value of document field 'ProductType' + // The field terms will be derived from document field 'PricePerUnit' + _: createField(p.ProductType, p.PricePerUnit) + }; + }) + """ + ] + ``` + + + +* **The query**: + + To get all documents of some product type having a specific price per unit use: + + + + ```python + matching_documents = list( + session.advanced + .document_query_from_index_type(Products_ByProductType, Product) + # 'Electronics' is the dynamic-index-field that was indexed + # from document field 'ProductType' + .where_equals("Electronics", 23) + ) + ``` + + + ```sql + // 'Electronics' is the dynamic-index-field that was indexed + // from document field 'ProductType' + from index 'Products/ByProductType' where Electronics = 23 + ``` + + + + + + + +### Example - list + + +The following example allows you to: + * Index **values** from items in a list + * After index is deployed, any item added to this list in the document will be dynamically indexed as well. + + +* **The document**: + + + ```python + class Attribute: + def __init__(self, PropName: str = None, PropValue: str = None): + self.PropName = PropName + self.PropValue = PropValue + + + class Product: + def __init__(self, Id: str = None, Name: str = None, Attributes: List[Attribute] = None): + self.Id = Id + self.Name = Name + + # For each element in this list, + # the VALUE of property 'PropName' will be dynamically indexed. + # e.g. Color, Width, Length (in ex. below) will become dynamic-index-fields. + self.Attributes = Attributes + ``` + + + + ```json + // Sample document content + { + "Name": "SomeName", + "Attributes": [ + { + "PropName": "Color", + "PropValue": "Blue" + }, + { + "PropName": "Width", + "PropValue": "10" + }, + { + "PropName": "Length", + "PropValue": "20" + }, + // ... + ] + } + ``` + + +* **The index**: + + The following index will create a dynamic-index-field per item in the document's `Attributes` list. + New items added to the Attributes list after index creation time will be dynamically indexed as well. + + The actual dynamic-index-field name on which you can query will be the item's PropName **value**. + E.g., 'PropName' value `Width` will be a dynamic-index-field. + + + + ```python + class Attributes_ByName(AbstractIndexCreationTask): + def __init__(self): + super().__init__() + self.map = ( + "from a in docs.Products select new { " + # Define the dynamic-index-fields by calling 'CreateField' + # A dynamic-index-field will be generated for each item in 'Attributes' + + # For each item, the field name will be the value of field 'PropName' + # The field terms will be derived from field 'PropValue' + "_ = a.Attributes.Select(item => CreateField(item.PropName, item.PropValue)), " + + # A regular index field can be defined as well: + "Name = a.Name " + "}" + ) + ``` + + + ```python + class Attributes_ByName_JS(AbstractJavaScriptIndexCreationTask): + def __init__(self): + super().__init__() + self.maps = [ + """ + map('Products', function (p) { + return { + // For each item, + // the field name will be the value of field 'PropName' + // The field terms will be derived from field 'PropValue' + _: p.Attributes.map(item => createField(item.PropName, item.PropValue)), + + // A regular index field can be defined as well: + Name: p.Name + }; + }) + """ + ] + ``` + + + +* **The query**: + + To get all documents matching a specific attribute property use: + + + + ```python + matching_documents = list( + session.advanced + .document_query_from_index_type(Attributes_ByName, Product) + # 'Width' is a dynamic-index-field that was indexed from the Attributes list + .where_equals("Width", 10) + ) + ``` + + + ```sql + // 'Width' is a dynamic-index-field that was indexed from the Attributes list + from index 'Attributes/ByName' where Width = 10 + ``` + + + + + + + + + + + +Dynamic fields can be indexed for [Full-text search](../../indexes/querying/searching.mdx) by setting `FieldIndexing.Search` in `CreateFieldOptions`. +* **At indexing time**: + At indexing time, RavenDB analyzes the dynamic field value and creates the indexed terms. + The analyzer used at indexing time is resolved from the index definition. + You can configure an analyzer for a specific generated field name, or configure `ALL_FIELDS` as a fallback. + See [Configuring analyzers for dynamic fields](../../indexes/using-dynamic-fields.mdx#configuring-analyzers-for-dynamic-fields) below. + * If the generated dynamic field name has an explicit analyzer configuration, that analyzer is used. + * Otherwise, if `ALL_FIELDS` has an analyzer configuration, RavenDB uses it as a fallback. + * If no analyzer is configured for the field, RavenDB uses the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). -## Indexing documents fields VALUES +* **At querying time**: + For full-text search to return the expected results, the analyzer used at indexing time and the analyzer used for the query term should produce compatible terms. -## Example - basic + When a `search()` query targets a dynamic field that does **not** have an explicit analyzer configuration for its generated field name, + RavenDB uses the [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) to analyze the query term by default. - + Set the [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) + configuration key to `true` to make RavenDB use the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) as the fallback analyzer for the query term instead. -* Only the **basic concept** of creating a dynamic-index-field from the **value** of a document field. -* Documents can then be queried based on those indexed values. + This configuration can be set at different scopes: server-wide, per database, or per index. + The example below sets it at the index scope, in the index definition. + + For a summary of how RavenDB resolves the analyzer used for the query term, + see [Which analyzer is used for the query term](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) below. + +--- + +The following example indexes localized product descriptions as dynamic fields and queries one of those generated fields using full-text search: + +* **The document**: + + + ```python + class Product: + def __init__(self, Id: str = None, Descriptions: Dict[str, str] = None): + self.Id = Id + self.Descriptions = Descriptions + ``` + + + + ```json + // Sample document content + { + "Descriptions": { + "English": "North wind jacket", + "French": "Veste vent du nord" + } + } + ``` + -* **The document**: - - -{`class Product: - def __init__(self, Id: str = None, product_type: str = None, price_per_unit: float = None): - self.Id = Id - - # The VALUE of ProductType will be dynamically indexed - self.product_type = product_type - self.price_per_unit = price_per_unit -`} - - +* **The index**: - - -{`// Sample document content -\{ - "ProductType": "Electronics", - "PricePerUnit": 23 -\} -`} - - + The following index creates one dynamic field for each entry in the `Descriptions` dictionary. + For example, the key `English` creates the dynamic field `Description_English`. -* **The index**: - The below index will index the **value** of document field 'ProductType'. - - This value will be the dynamic-index-field name on which you can query. - E.g., Field value `Electronics` will be the dynamic-index-field. + Each generated dynamic field is indexed for **full-text search**. + + + + ```python + class Products_ByLocalizedDescription(AbstractIndexCreationTask): + USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = ( + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery" + ) + + def __init__(self): + super().__init__() + self.map = ( + "from product in docs.Products select new { " + " _ = product.Descriptions.Select(x => " + " CreateField(" + " \"Description_\" + x.Key, " + " x.Value, " + # Index each generated dynamic field for FTS + " new CreateFieldOptions { " + " Indexing = FieldIndexing.Search, " + " Storage = FieldStorage.No " + " })) " + "}" + ) + + self._store_all_fields(FieldStorage.NO) + + # Set this configuration key to true so that search() queries on dynamic fields + # will use the 'Default Search Analyzer' when the generated field name + # has no explicit analyzer configuration. + self.configuration = { + Products_ByLocalizedDescription.USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS: "true" + } + ``` + + + ```python + class Products_ByLocalizedDescription_JS(AbstractJavaScriptIndexCreationTask): + USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS = ( + "Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery" + ) + + def __init__(self): + super().__init__() + self.maps = [ + """ + map('Products', function (product) { + return { + _: Object.keys(product.Descriptions).map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + // Index each generated dynamic field for FTS + indexing: 'Search', + storage: 'No' + }); + }) + }; + }) + """ + ] + + # Apply a storage default to every generated field + self.fields = { + constants.Documents.Indexing.Fields.ALL_FIELDS: + IndexFieldOptions(storage=FieldStorage.NO) + } + + # Set this configuration key to true so that search() queries on dynamic fields + # will use the 'Default Search Analyzer' when the generated field name + # has no explicit analyzer configuration. + self.configuration = { + Products_ByLocalizedDescription_JS.USE_SEARCH_ANALYZER_FOR_DYNAMIC_FIELDS: "true" + } + ``` + + + +* **Full-text search query**: + + Query the generated dynamic field by its field name. + + In this example, the query targets the generated field `Description_English`. + + Since `Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery` is set to `true`, + the searched term `"north wind"` is analyzed with the [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer). + + + + ```python + results = list( + session.advanced + .document_query_from_index_type(Products_ByLocalizedDescription, Product) + .search("Description_English", "north wind") + ) + ``` + + + + ```sql + from index "Products/ByLocalizedDescription" + where search(Description_English, "north wind") + ``` + + + +--- + + + +### Configuring analyzers for dynamic fields + +`CreateFieldOptions` does **not** let you specify a custom analyzer directly. +However, you can still configure which analyzer RavenDB will use for dynamic fields in the index definition by either: + +* Configuring an analyzer for a specific generated dynamic field name. +* Configuring `ALL_FIELDS` as a fallback for fields that do not have their own explicit analyzer configuration. + +Use `ALL_FIELDS` with care, since it can affect any field that does not have its own explicit analyzer configuration. + +--- + +#### Configure analyzer for a specific dynamic field - -{`class Products_ByProductType(AbstractIndexCreationTask): +```python +class Products_ByLocalizedDescription(AbstractIndexCreationTask): def __init__(self): super().__init__() + self.map = ( + "from product in docs.Products select new { " + " _ = product.Descriptions.Select(x => " + " CreateField(" + " \"Description_\" + x.Key, " + " x.Value, " + " new CreateFieldOptions { " + " Indexing = FieldIndexing.Search, " + " Storage = FieldStorage.No " + " })) " + "}" + ) - # Call 'CreateField' to generate the dynamic-index-fields - # The field name will be the value of document field 'product_type' - # The field terms will be derived from document field 'price_per_unit' - self.map = "from p in docs.Products select new { _ = CreateField(p.product_type, p.price_per_unit)}" -`} - + self._store_all_fields(FieldStorage.NO) + + # Configure an analyzer for a known generated dynamic field name + # ============================================================== + self._analyze("Description_English", "StopAnalyzer") +``` - - -{`class Products_ByProductType_JS(AbstractJavaScriptIndexCreationTask): + +```python +class Products_ByLocalizedDescription_JS(AbstractJavaScriptIndexCreationTask): def __init__(self): super().__init__() - self.maps = { + self.maps = [ """ - map('Products', function (p) { + map('Products', function (product) { return { - _: createField(p.product_type, p.price_per_unit, - { indexing: 'Search', storage: true, termVector: null }) + _: Object.keys(product.Descriptions).map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; }) """ + ] + + # Configure an analyzer for a known generated dynamic field name + # ============================================================== + self.fields = { + constants.Documents.Indexing.Fields.ALL_FIELDS: + IndexFieldOptions(storage=FieldStorage.NO), + "Description_English": + IndexFieldOptions(analyzer="StopAnalyzer") } -`} - - - - -* **The query**: - * To get all documents of some product type having a specific price per unit use: - - - -{`# 'electronics' is the dynamic-index-field that was indexed from the document 'product_type' -matching_documents = list( - session.advanced.document_query_from_index_type(Products_ByProductType, Product).where_equals( - "electronics", 23 - ) -) -`} - - - - -{`// 'Electronics' is the dynamic-index-field that was indexed from document field 'ProductType' -from index 'Products/ByProductType' where Electronics = 23 -`} - +``` -## Example - list - - - -* Index **values** from items in a list -* After index is deployed, any item added this list in the document will be dynamically indexed as well. - - - -* **The document**: - - -{`class Attribute: - def __init__(self, prop_name: str = None, prop_value: str = None): - self.prop_name = prop_name - self.prop_value = prop_value - - -class Product: - def __init__(self, Id: str = None, name: str = None, attributes: List[Attribute] = None): - self.Id = Id - self.name = name - # For each element in this list, the VALUE of property 'prop_name' will be dynamically indexed - # e.g. color, width, length (in ex. below) will become dynamic-index-field - self.attributes = attributes -`} - - - - - -{`// Sample document content -\{ -Name": "SomeName", -Attributes": [ - \{ - "PropName": "Color", - "PropValue": "Blue" - \}, - \{ - "PropName": "Width", - "PropValue": "10" - \}, - \{ - "PropName": "Length", - "PropValue": "20" - \}, - ... - -\} -`} - - - -* **The index**: - The below index will create a dynamic-index-field per item in the document's `Attributes` list. - New items added to the Attributes list after index creation time will be dynamically indexed as well. - - The actual dynamic-index-field name on which you can query will be the item's PropName **value**. - E.g., 'PropName' value `width` will be a dynamic-index-field. +#### Configure a fallback analyzer for all fields - -{`class Attributes_ByName(AbstractIndexCreationTask): +```python +class Products_ByLocalizedDescription(AbstractIndexCreationTask): def __init__(self): super().__init__() self.map = ( - "from a in docs.Products select new " - "{ _ = a.attributes.Select( item => CreateField(item.prop_name, item.prop_value)), name = a.name " + "from product in docs.Products select new { " + " _ = product.Descriptions.Select(x => " + " CreateField(" + " \"Description_\" + x.Key, " + " x.Value, " + " new CreateFieldOptions { " + " Indexing = FieldIndexing.Search, " + " Storage = FieldStorage.No " + " })) " "}" ) -`} - + + self._store_all_fields(FieldStorage.NO) + + # Use this as a fallback for fields that do not have + # their own explicit analyzer configuration + # ================================================== + self._analyze(constants.Documents.Indexing.Fields.ALL_FIELDS, "StopAnalyzer") +``` - - -{`class Attributes_ByName_JS(AbstractJavaScriptIndexCreationTask): + +```python +class Products_ByLocalizedDescription_JS(AbstractJavaScriptIndexCreationTask): def __init__(self): super().__init__() - self.maps = { + self.maps = [ """ - map('Products', function (p) { + map('Products', function (product) { return { - _: p.Attributes.map(item => createField(item.PropName, item.PropValue, - { indexing: 'Search', storage: true, termVector: null })), - Name: p.Name + _: Object.keys(product.Descriptions).map(function (key) { + return createField( + 'Description_' + key, + product.Descriptions[key], + { + indexing: 'Search', + storage: 'No' + }); + }) }; }) """ + ] + + # Use this as a fallback for fields that do not have + # their own explicit analyzer configuration + # ================================================== + self.fields = { + constants.Documents.Indexing.Fields.ALL_FIELDS: + IndexFieldOptions(storage=FieldStorage.NO, analyzer="StopAnalyzer") } -`} - - - - -* **The query**: - To get all documents matching a specific attribute property use: - - - -{`matching_documents = list( - session.advanced.document_query_from_index_type(Attributes_ByName, Product).where_equals( - "width", 10 - ) -) -`} - - - - -{`// 'Width' is a dynamic-index-field that was indexed from the Attributes list -from index 'Attributes/ByName' where Width = 10 -`} - +``` + + + + +### Which analyzer is used for the query term? + +The following applies when a `search()` query targets a **dynamic field**: + +| Index configuration | [Configuration key](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) | Analyzer used for the query term | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| No analyzer is explicitly configured for the generated field name | `false` | The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) | +| No analyzer is explicitly configured for the generated field name | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `false` | The analyzer resolved by the normal field fallback path,
e.g. `WhitespaceAnalyzer` | +| `ALL_FIELDS` is configured with a specific analyzer,
e.g. `WhitespaceAnalyzer` | `true` | The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) | +| The generated field name is explicitly configured,
e.g. `self._analyze("Description_English", "StopAnalyzer")` | `false` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | +| The generated field name is explicitly configured,
e.g. `self._analyze("Description_English", "StopAnalyzer")` | `true` | The explicitly configured analyzer,
e.g. `StopAnalyzer` | + +
+
-## CreateField syntax + #### Syntax for Index: - - -{`object CreateField(string name, object value); + +```python +CreateField(name, value) -object CreateField(string name, object value, bool stored, bool analyzed); +CreateField(name, value, stored, analyzed) -object CreateField(string name, object value, CreateFieldOptions options); -`} - +CreateField(name, value, CreateFieldOptions) +``` #### Syntax for JavaScript-index: - - -{`createField(fieldName, fieldValue, options); // returns object -`} - + +```js +createField(fieldName, fieldValue, options); // returns object +``` -| Parameters | Type | Description | -|------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **name** | `string` | Name of the dynamic-index-field | -| **value** | `object` | Value of the dynamic-index-field
The field Terms are derived from this value. | -| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`False` - will set `FieldStorage.NO` (default value)
`True` - will set `FieldStorate.YES` | -| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`None` - `FieldIndexing.Default` (default value)
`False` - `FieldIndexing.Exact`
`True` - `FieldIndexing.Search` | -| **options** | `CreateFieldOptions` | Dynamic-index-field options | +| Parameters | Type | Description | +|----------------|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **fieldName** | `string` | Name of the dynamic-index-field | +| **fieldValue** | `object` | Value of the dynamic-index-field
The field terms are derived from this value. | +| **stored** | `bool` | Sets [FieldStorage](../../indexes/storing-data-in-index.mdx)

`false` - will set `FieldStorage.No` (default value)
`true` - will set `FieldStorage.Yes` | +| **analyzed** | `bool` | Sets [FieldIndexing](../../indexes/using-analyzers.mdx)

`null` - `FieldIndexing.Default` (default value)
`false` - `FieldIndexing.Exact`
`true` - `FieldIndexing.Search` | +| **options** | `CreateFieldOptions` | Dynamic-index-field options | -| CreateFieldOptions | | | -|--------------------|--------------------|----------------------------------------------------------------------------| +| CreateFieldOptions | | | +|--------------------|--------------------|-----------------------------------------------------------------------------------| | **Storage** | `FieldStorage?` | Learn about [storing data](../../indexes/storing-data-in-index.mdx) in the index. | -| **Indexing** | `FieldIndexing?` | Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | -| **TermVector** | `FieldTermVector?` | Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | +| **Indexing** | `FieldIndexing?` | Sets the field indexing behavior.
Learn about [using analyzers](../../indexes/using-analyzers.mdx) in the index. | +| **TermVector** | `FieldTermVector?` | Sets the field term-vector behavior.
Learn about [term vectors](../../indexes/using-term-vectors.mdx) in the index. | @@ -496,17 +892,15 @@ object CreateField(string name, object value, CreateFieldOptions options); +
+ + - -## Indexed fields & terms view - -The generated dynamic-index-fields and their indexed terms can be viewed in the **Terms View**. -Below are sample index fields & their terms generated from the last example. +The generated dynamic index fields and their indexed terms can be viewed in the **Terms View** in Studio. +Below are sample index fields & their terms generated from [this example](../../indexes/using-dynamic-fields.mdx#example---list). ![Figure 1. Go to terms view](../assets/dynamic-index-fields-1.png) ![Figure 2. Indexed fields & terms](../assets/dynamic-index-fields-2.png) - - - - + + diff --git a/versioned_docs/version-7.1/indexes/querying/content/_searching-csharp.mdx b/versioned_docs/version-7.1/indexes/querying/content/_searching-csharp.mdx index d20ef5a65e..f0be0e6ffd 100644 --- a/versioned_docs/version-7.1/indexes/querying/content/_searching-csharp.mdx +++ b/versioned_docs/version-7.1/indexes/querying/content/_searching-csharp.mdx @@ -16,11 +16,13 @@ import CodeBlock from '@theme/CodeBlock'; See examples below. * You can configure which analyzer will be used to tokenize this field. - See [selecting an analyzer](../../../indexes/using-analyzers.mdx#selecting-an-analyzer-for-a-field). + Learn more in [Setting analyzer for index-field](../../../indexes/using-analyzers.mdx#setting-analyzer-for-index-field). + * In this article: * [Indexing single field for FTS](../../../indexes/querying/searching.mdx#indexing-single-field-for-fts) * [Indexing multiple fields for FTS](../../../indexes/querying/searching.mdx#indexing-multiple-fields-for-fts) - * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-(using-asjson)) + * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-using-asjson) + * [Indexing dynamic fields for FTS](../../../indexes/querying/searching.mdx#indexing-dynamic-fields-for-fts) * [Boosting search results](../../../indexes/querying/searching.mdx#boosting-search-results) * [Searching with wildcards](../../../indexes/querying/searching.mdx#searching-with-wildcards) * [When using RavenStandardAnalyzer or StandardAnalyzer or NGramAnalyzer](../../../indexes/querying/searching.mdx#when-usingoror) @@ -28,6 +30,7 @@ import CodeBlock from '@theme/CodeBlock'; * [When using the Exact analyzer](../../../indexes/querying/searching.mdx#when-using-the-exact-analyzer) + ## Indexing single field for FTS #### The index: @@ -262,7 +265,7 @@ where (search(EmployeeData, "Manager") or search(EmployeeData, "French Spanish", ## Indexing all fields for FTS (using AsJson) -* To search across ALL fields in a document without defining each one explicitly, +* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method in the _Map_ function to extract all property values and index them in a single searchable field. * This approach makes the index robust to changes in the document schema. @@ -377,7 +380,14 @@ where search(AllValues, "tofu") +## Indexing dynamic fields for FTS + +* [Dynamic index fields](../../../indexes/using-dynamic-fields.mdx) are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time. +* Dynamic index fields can be configured for full-text search just like regular index fields. + Learn more in [Indexing dynamic fields for full-text search](../../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search). ## Boosting search results diff --git a/versioned_docs/version-7.1/indexes/querying/content/_searching-java.mdx b/versioned_docs/version-7.1/indexes/querying/content/_searching-java.mdx index 7d85246ced..4accf3f512 100644 --- a/versioned_docs/version-7.1/indexes/querying/content/_searching-java.mdx +++ b/versioned_docs/version-7.1/indexes/querying/content/_searching-java.mdx @@ -388,4 +388,11 @@ where search(allValues, "tofu") +## Indexing dynamic fields for FTS +* [Dynamic index fields](../../../indexes/using-dynamic-fields.mdx) are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time. + +* Dynamic index fields can be configured for full-text search just like regular index fields. + Learn more in [Indexing dynamic fields for full-text search](../../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search). \ No newline at end of file diff --git a/versioned_docs/version-7.1/indexes/querying/content/_searching-nodejs.mdx b/versioned_docs/version-7.1/indexes/querying/content/_searching-nodejs.mdx index 4ca66b5877..cb73bfd7c0 100644 --- a/versioned_docs/version-7.1/indexes/querying/content/_searching-nodejs.mdx +++ b/versioned_docs/version-7.1/indexes/querying/content/_searching-nodejs.mdx @@ -5,7 +5,7 @@ import CodeBlock from '@theme/CodeBlock'; -* Prior to reading this article, please refer to [full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) +* Prior to reading this article, please refer to [Full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) to learn about the `search` method. * **All capabilities** provided by `search` with a dynamic query can also be used when querying a static-index. @@ -17,11 +17,13 @@ import CodeBlock from '@theme/CodeBlock'; See examples below. * You can configure which analyzer will be used to tokenize this field. - See [selecting an analyzer](../../../indexes/using-analyzers.mdx#selecting-an-analyzer-for-a-field). + Learn more in [Setting analyzer for index-field](../../../indexes/using-analyzers.mdx#setting-analyzer-for-index-field). + * In this article: * [Indexing single field for FTS](../../../indexes/querying/searching.mdx#indexing-single-field-for-fts) * [Indexing multiple fields for FTS](../../../indexes/querying/searching.mdx#indexing-multiple-fields-for-fts) - * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-(using-asjson)) + * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-using-asjson) + * [Indexing dynamic fields for FTS](../../../indexes/querying/searching.mdx#indexing-dynamic-fields-for-fts) * [Boosting search results](../../../indexes/querying/searching.mdx#boosting-search-results) * [Searching with wildcards](../../../indexes/querying/searching.mdx#searching-with-wildcards) * [When using RavenStandardAnalyzer or StandardAnalyzer or NGramAnalyzer](../../../indexes/querying/searching.mdx#when-usingoror) @@ -29,6 +31,7 @@ import CodeBlock from '@theme/CodeBlock'; * [When using the Exact analyzer](../../../indexes/querying/searching.mdx#when-using-the-exact-analyzer) + ## Indexing single field for FTS #### The index: @@ -163,7 +166,7 @@ where (search(employeeData, "Manager") or search(employeeData, "French Spanish", ## Indexing all fields for FTS (using AsJson) -* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, +* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, which is available when using **a C# LINQ string** that is assigned to the `map` property in the Node.js index class, as shown in the example below. @@ -229,6 +232,14 @@ where search(AllValues, "tofu") +## Indexing dynamic fields for FTS + +* [Dynamic index fields](../../../indexes/using-dynamic-fields.mdx) are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time. + +* Dynamic index fields can be configured for full-text search just like regular index fields. + Learn more in [Indexing dynamic fields for full-text search](../../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search). ## Boosting search results diff --git a/versioned_docs/version-7.1/indexes/querying/content/_searching-php.mdx b/versioned_docs/version-7.1/indexes/querying/content/_searching-php.mdx index 55261ce19f..510a6f8752 100644 --- a/versioned_docs/version-7.1/indexes/querying/content/_searching-php.mdx +++ b/versioned_docs/version-7.1/indexes/querying/content/_searching-php.mdx @@ -5,7 +5,7 @@ import CodeBlock from '@theme/CodeBlock'; -* Prior to reading this article, please refer to [full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) +* Prior to reading this article, please refer to [Full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) to learn about the `search` method. * **All capabilities** provided by `search` with a dynamic query can also be used when querying a static-index. @@ -17,14 +17,17 @@ import CodeBlock from '@theme/CodeBlock'; See examples below. * You can configure which analyzer will be used to tokenize this field. - See [selecting an analyzer](../../../indexes/using-analyzers.mdx#selecting-an-analyzer-for-a-field). + Learn more in [Setting analyzer for index-field](../../../indexes/using-analyzers.mdx#setting-analyzer-for-index-field). + * In this article: * [Indexing single field for FTS](../../../indexes/querying/searching.mdx#indexing-single-field-for-fts) * [Indexing multiple fields for FTS](../../../indexes/querying/searching.mdx#indexing-multiple-fields-for-fts) - * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-(using-asjson)) + * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-using-asjson) + * [Indexing dynamic fields for FTS](../../../indexes/querying/searching.mdx#indexing-dynamic-fields-for-fts) * [Boosting search results](../../../indexes/querying/searching.mdx#boosting-search-results) + ## Indexing single field for FTS #### The index: @@ -253,7 +256,7 @@ where (search(EmployeeData, "Manager") or search(EmployeeData, "French Spanish", ## Indexing all fields for FTS (using AsJson) -* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, +* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, which is available in the **C# LINQ string** that is assigned to the `map` property in the PHP index class, as shown in the example below. @@ -332,6 +335,15 @@ where search(allValues, "tofu") +## Indexing dynamic fields for FTS + +* [Dynamic index fields](../../../indexes/using-dynamic-fields.mdx) are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time. + +* Dynamic index fields can be configured for full-text search just like regular index fields. + Learn more in [Indexing dynamic fields for full-text search](../../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search). + ## Boosting search results diff --git a/versioned_docs/version-7.1/indexes/querying/content/_searching-python.mdx b/versioned_docs/version-7.1/indexes/querying/content/_searching-python.mdx index f06c29d94f..88c6aa452f 100644 --- a/versioned_docs/version-7.1/indexes/querying/content/_searching-python.mdx +++ b/versioned_docs/version-7.1/indexes/querying/content/_searching-python.mdx @@ -5,7 +5,7 @@ import CodeBlock from '@theme/CodeBlock'; -* Prior to reading this article, please refer to [full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) +* Prior to reading this article, please refer to [Full-Text search with dynamic queries](../../../client-api/session/querying/text-search/full-text-search.mdx) to learn about the `search` method. * **All capabilities** provided by `search` with a dynamic query can also be used when querying a static-index. @@ -17,14 +17,17 @@ import CodeBlock from '@theme/CodeBlock'; See examples below. * You can configure which analyzer will be used to tokenize this field. - See [selecting an analyzer](../../../indexes/using-analyzers.mdx#selecting-an-analyzer-for-a-field). + Learn more in [Setting analyzer for index-field](../../../indexes/using-analyzers.mdx#setting-analyzer-for-index-field). + * In this article: * [Indexing single field for FTS](../../../indexes/querying/searching.mdx#indexing-single-field-for-fts) * [Indexing multiple fields for FTS](../../../indexes/querying/searching.mdx#indexing-multiple-fields-for-fts) - * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-(using-asjson)) + * [Indexing all fields for FTS (using AsJson)](../../../indexes/querying/searching.mdx#indexing-all-fields-for-fts-using-asjson) + * [Indexing dynamic fields for FTS](../../../indexes/querying/searching.mdx#indexing-dynamic-fields-for-fts) * [Boosting search results](../../../indexes/querying/searching.mdx#boosting-search-results) + ## Indexing single field for FTS #### The index: @@ -167,7 +170,7 @@ where (search(EmployeeData, "Manager") or search(EmployeeData, "French Spanish", ## Indexing all fields for FTS (using AsJson) -* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, +* To search across ALL fields in a document without defining each one explicitly, use the `AsJson` method, which is available in the **C# LINQ string** that is assigned to the `map` property in the Python index class, as shown in the example below. @@ -238,6 +241,14 @@ where search(all_values, "tofu") +## Indexing dynamic fields for FTS + +* [Dynamic index fields](../../../indexes/using-dynamic-fields.mdx) are created at indexing time, with names and values derived from document content, + rather than being predefined as fixed index fields in the index definition. + This allows you to query the index by fields that are not known at index definition or deployment time. + +* Dynamic index fields can be configured for full-text search just like regular index fields. + Learn more in [Indexing dynamic fields for full-text search](../../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search). ## Boosting search results diff --git a/versioned_docs/version-7.1/indexes/using-dynamic-fields.mdx b/versioned_docs/version-7.1/indexes/using-dynamic-fields.mdx index 7e6fe3a7ca..e465ba5431 100644 --- a/versioned_docs/version-7.1/indexes/using-dynamic-fields.mdx +++ b/versioned_docs/version-7.1/indexes/using-dynamic-fields.mdx @@ -1,6 +1,7 @@ --- -title: "Indexes: Dynamic Index Fields" -sidebar_label: Dynamic Fields +title: "Dynamic Index Fields" +sidebar_label: "Dynamic Index Fields" +description: "Index document fields dynamically in RavenDB when field names are unknown at index definition time, using the CreateField method." sidebar_position: 27 supported_languages: ["csharp", "java", "python", "php", "nodejs"] see_also: @@ -31,7 +32,6 @@ import UsingDynamicFieldsPython from './content/_using-dynamic-fields-python.mdx import UsingDynamicFieldsPhp from './content/_using-dynamic-fields-php.mdx'; import UsingDynamicFieldsNodejs from './content/_using-dynamic-fields-nodejs.mdx'; - diff --git a/versioned_docs/version-7.1/server/configuration/indexing-configuration.mdx b/versioned_docs/version-7.1/server/configuration/indexing-configuration.mdx index 2e1f67f6a6..3ff0ff574f 100644 --- a/versioned_docs/version-7.1/server/configuration/indexing-configuration.mdx +++ b/versioned_docs/version-7.1/server/configuration/indexing-configuration.mdx @@ -103,6 +103,7 @@ import LanguageContent from "@site/src/components/LanguageContent"; [Indexing.OrderByTicksAutomaticallyWhenDatesAreInvolved](../../server/configuration/indexing-configuration.mdx#indexingorderbyticksautomaticallywhendatesareinvolved) [Indexing.QueryClauseCache.Disabled](../../server/configuration/indexing-configuration.mdx#indexingqueryclausecachedisabled) [Indexing.QueryClauseCache.RepeatedQueriesTimeFrameInSec](../../server/configuration/indexing-configuration.mdx#indexingqueryclausecacherepeatedqueriestimeframeinsec) + [Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery](../../server/configuration/indexing-configuration.mdx#indexingqueryingusesearchanalyzerfordynamicfieldsifnotsetexplicitlyinsearchquery) [Indexing.ScratchSpaceLimitInMb](../../server/configuration/indexing-configuration.mdx#indexingscratchspacelimitinmb) [Indexing.Static.SearchEngineType](../../server/configuration/indexing-configuration.mdx#indexingstaticsearchenginetype) [Indexing.Throttling.TimeIntervalInMs](../../server/configuration/indexing-configuration.mdx#indexingthrottlingtimeintervalinms) @@ -1008,6 +1009,33 @@ Queries that repeat within this time frame will be considered worth caching. +## Indexing.Querying.UseSearchAnalyzerForDynamicFieldsIfNotSetExplicitlyInSearchQuery + +* This setting controls which analyzer is used for the **query term** when a **`search()` query** targets a [Dynamic index field](../../indexes/using-dynamic-fields.mdx#indexing-dynamic-fields-for-full-text-search) + that was indexed for full-text search, and no analyzer is explicitly configured for that field in the index definition. + + * `false` (default): + The [Default analyzer](../../indexes/using-analyzers.mdx#using-the-default-analyzer) is used to analyze the **query term**. + This analyzer can be configured via the [Indexing.Analyzers.Default](../../server/configuration/indexing-configuration.mdx#indexinganalyzersdefault) configuration key. + + * `true`: + The [Default search analyzer](../../indexes/using-analyzers.mdx#using-the-default-search-analyzer) is used to analyze the **query term**. + This analyzer can be configured via the [Indexing.Analyzers.Search.Default](../../server/configuration/indexing-configuration.mdx#indexinganalyzerssearchdefault) configuration key. + +* See the table in [Which analyzer is used for the query term?](../../indexes/using-dynamic-fields.mdx#which-analyzer-is-used-for-the-query-term) + for the complete analyzer-selection rules based on this configuration key and the analyzer configured for the dynamic index field. + +* This configuration applies to both the Lucene and Corax search engines. + The default is `false` to preserve backward compatibility. + +--- + +- **Type**: `bool` +- **Default**: `false` +- **Scope**: Server-wide, or per database, or per index + + + ## Indexing.ScratchSpaceLimitInMb * Amount of scratch space in megabytes that we allow to use for the index storage.