1
use bonsaidb::core::document::{CollectionDocument, Emit};
2
use bonsaidb::core::schema::{
3
    Collection, CollectionMapReduce, ReduceResult, SerializedCollection, SerializedView, View,
4
    ViewMapResult, ViewMappedValue, ViewSchema,
5
};
6
use bonsaidb::local::config::{Builder, StorageConfiguration};
7
use bonsaidb::local::Database;
8
use serde::{Deserialize, Serialize};
9

            
10
// begin rustme snippet: snippet-a
11
72
#[derive(Debug, Serialize, Deserialize, Collection)]
12
#[collection(name = "shapes", views = [ShapesByNumberOfSides])]
13
struct Shape {
14
    pub sides: u32,
15
}
16

            
17
74
#[derive(Debug, Clone, View, ViewSchema)]
18
#[view(collection = Shape, key = u32, value = usize, name = "by-number-of-sides")]
19
struct ShapesByNumberOfSides;
20

            
21
impl CollectionMapReduce for ShapesByNumberOfSides {
22
21
    fn map<'doc>(&self, document: CollectionDocument<Shape>) -> ViewMapResult<'doc, Self::View> {
23
21
        document
24
21
            .header
25
21
            .emit_key_and_value(document.contents.sides, 1)
26
21
    }
27

            
28
20
    fn reduce(
29
20
        &self,
30
20
        mappings: &[ViewMappedValue<'_, Self>],
31
20
        _rereduce: bool,
32
20
    ) -> ReduceResult<Self::View> {
33
33
        Ok(mappings.iter().map(|m| m.value).sum())
34
20
    }
35
}
36
// end rustme snippet
37

            
38
1
fn main() -> Result<(), bonsaidb::core::Error> {
39
    // begin rustme snippet: snippet-b
40
1
    let db = Database::open::<Shape>(StorageConfiguration::new("view-examples.bonsaidb"))?;
41

            
42
    // Insert a new document into the Shape collection.
43
1
    Shape { sides: 3 }.push_into(&db)?;
44
    // end rustme snippet
45

            
46
    // Views in BonsaiDb are written using a Map/Reduce approach. In this
47
    // example, we take a look at how document mapping can be used to filter and
48
    // retrieve data
49
    //
50
    // Let's start by seeding the database with some shapes of various sizes:
51
19
    for sides in 3..=20 {
52
18
        Shape { sides }.push_into(&db)?;
53
    }
54

            
55
    // And, let's add a few shapes with the same number of sides
56
1
    Shape { sides: 3 }.push_into(&db)?;
57
1
    Shape { sides: 4 }.push_into(&db)?;
58

            
59
    // At this point, our database should have 3 triangles:
60
    // begin rustme snippet: snippet-c
61
1
    let triangles = ShapesByNumberOfSides::entries(&db).with_key(&3).query()?;
62
1
    println!("Number of triangles: {}", triangles.len());
63
1
    // end rustme snippet
64
1

            
65
1
    // What is returned is a list of entries containing the document id
66
1
    // (source), the key of the entry, and the value of the entry:
67
1
    println!("Triangles: {triangles:#?}");
68

            
69
    // If you want the associated documents, use query_with_collection_docs:
70
3
    for entry in &ShapesByNumberOfSides::entries(&db)
71
1
        .with_key(&3)
72
1
        .query_with_collection_docs()?
73
3
    {
74
3
        println!(
75
3
            "Shape ID {} has {} sides",
76
3
            entry.document.header.id, entry.document.contents.sides
77
3
        );
78
3
    }
79

            
80
    // The reduce() function takes the "values" emitted during the map()
81
    // function, and reduces a list down to a single value. In this example, the
82
    // reduce function is acting as a count. So, if you want to query for the
83
    // number of shapes, we don't need to fetch all the records, we can just
84
    // retrieve the result of the calculation directly.
85
    //
86
    // So, here we're using reduce() to count the number of shapes with 4 sides.
87
1
    println!(
88
1
        "Number of quads: {} (expected 2)",
89
1
        ShapesByNumberOfSides::entries(&db).with_key(&4).reduce()?
90
    );
91

            
92
    // Or, 5 shapes that are triangles or quads
93
1
    println!(
94
1
        "Number of quads and triangles: {} (expected 5)",
95
1
        ShapesByNumberOfSides::entries(&db)
96
1
            .with_keys(&[3, 4])
97
1
            .reduce()?
98
    );
99

            
100
    // And, 10 shapes that have more than 10 sides
101
1
    println!(
102
1
        "Number of shapes with more than 10 sides: {} (expected 10)",
103
1
        ShapesByNumberOfSides::entries(&db)
104
1
            .with_key_range(11..)
105
1
            .reduce()?
106
    );
107

            
108
1
    Ok(())
109
1
}
110

            
111
1
#[test]
112
1
fn runs() {
113
1
    drop(std::fs::remove_dir_all("view-examples.bonsaidb"));
114
1
    main().unwrap()
115
1
}