// # Json field example // // This example shows how the json field can be used // to make tantivy partially schemaless by setting it as // default query parser field. use tantivy::collector::{Count, TopDocs}; use tantivy::query::QueryParser; use tantivy::schema::{Schema, FAST, STORED, STRING, TEXT}; use tantivy::{Index, IndexWriter, TantivyDocument}; fn main() -> tantivy::Result<()> { // # Defining the schema let mut schema_builder = Schema::builder(); schema_builder.add_date_field("timestamp", FAST | STORED); let event_type = schema_builder.add_text_field("event_type", STRING | STORED); let attributes = schema_builder.add_json_field("attributes", STORED | TEXT); let schema = schema_builder.build(); // # Indexing documents let index = Index::create_in_ram(schema.clone()); let mut index_writer: IndexWriter = index.writer(50_000_000)?; let doc = TantivyDocument::parse_json( &schema, r#"{ "timestamp": "2022-02-22T23:20:50.53Z", "event_type": "click", "attributes": { "target": "submit-button", "cart": {"product_id": 103}, "description": "the best vacuum cleaner ever" } }"#, )?; index_writer.add_document(doc)?; let doc = TantivyDocument::parse_json( &schema, r#"{ "timestamp": "2022-02-22T23:20:51.53Z", "event_type": "click", "attributes": { "target": "submit-button", "cart": {"product_id": 133}, "description": "das keyboard", "event_type": "holiday-sale" } }"#, )?; index_writer.add_document(doc)?; index_writer.commit()?; let reader = index.reader()?; let searcher = reader.searcher(); // # Default fields: event_type and attributes // By setting attributes as a default field it allows omitting attributes itself, e.g. "target", // instead of "attributes.target" let query_parser = QueryParser::for_index(&index, vec![event_type, attributes]); { let query = query_parser.parse_query("target:submit-button")?; let count_docs = searcher.search(&*query, &TopDocs::with_limit(2))?; assert_eq!(count_docs.len(), 2); } { let query = query_parser.parse_query("target:submit")?; let count_docs = searcher.search(&*query, &TopDocs::with_limit(2))?; assert_eq!(count_docs.len(), 2); } { let query = query_parser.parse_query("cart.product_id:103")?; let count_docs = searcher.search(&*query, &Count)?; assert_eq!(count_docs, 1); } { let query = query_parser.parse_query("click AND cart.product_id:133")?; let hits = searcher.search(&*query, &TopDocs::with_limit(2))?; assert_eq!(hits.len(), 1); } { // The sub-fields in the json field marked as default field still need to be explicitly // addressed let query = query_parser.parse_query("click AND 133")?; let hits = searcher.search(&*query, &TopDocs::with_limit(2))?; assert_eq!(hits.len(), 0); } { // Default json fields are ignored if they collide with the schema let query = query_parser.parse_query("event_type:holiday-sale")?; let hits = searcher.search(&*query, &TopDocs::with_limit(2))?; assert_eq!(hits.len(), 0); } // # Query via full attribute path { // This only searches in our schema's `event_type` field let query = query_parser.parse_query("event_type:click")?; let hits = searcher.search(&*query, &TopDocs::with_limit(2))?; assert_eq!(hits.len(), 2); } { // Default json fields can still be accessed by full path let query = query_parser.parse_query("attributes.event_type:holiday-sale")?; let hits = searcher.search(&*query, &TopDocs::with_limit(2))?; assert_eq!(hits.len(), 1); } Ok(()) }