1
1
use bonsaidb::{
2
    core::{
3
        connection::AsyncConnection,
4
        document::{CollectionDocument, Emit},
5
        schema::{
6
            view::CollectionViewSchema, Collection, ReduceResult, SerializedCollection, View,
7
            ViewMapResult, ViewMappedValue,
8
        },
9
    },
10
    local::{
11
        config::{Builder, StorageConfiguration},
12
        AsyncDatabase,
13
    },
14
};
15
use serde::{Deserialize, Serialize};
16

            
17
// begin rustme snippet: snippet-a
18
72
#[derive(Debug, Serialize, Deserialize, Collection)]
19
#[collection(name = "shapes", views = [ShapesByNumberOfSides])]
20
struct Shape {
21
    pub sides: u32,
22
}
23

            
24
74
#[derive(Debug, Clone, View)]
25
#[view(collection = Shape, key = u32, value = usize, name = "by-number-of-sides")]
26
struct ShapesByNumberOfSides;
27

            
28
impl CollectionViewSchema for ShapesByNumberOfSides {
29
    type View = Self;
30

            
31
21
    fn map(&self, document: CollectionDocument<Shape>) -> ViewMapResult<Self::View> {
32
21
        document
33
21
            .header
34
21
            .emit_key_and_value(document.contents.sides, 1)
35
21
    }
36

            
37
20
    fn reduce(
38
20
        &self,
39
20
        mappings: &[ViewMappedValue<Self>],
40
20
        _rereduce: bool,
41
20
    ) -> ReduceResult<Self::View> {
42
33
        Ok(mappings.iter().map(|m| m.value).sum())
43
20
    }
44
}
45
// end rustme snippet
46

            
47
#[tokio::main]
48
1
async fn main() -> Result<(), bonsaidb::core::Error> {
49
    // begin rustme snippet: snippet-b
50
1
    let db =
51
1
        AsyncDatabase::open::<Shape>(StorageConfiguration::new("view-examples.bonsaidb")).await?;
52

            
53
    // Insert a new document into the Shape collection.
54
1
    Shape { sides: 3 }.push_into_async(&db).await?;
55
    // end rustme snippet
56

            
57
    // Views in BonsaiDb are written using a Map/Reduce approach. In this
58
    // example, we take a look at how document mapping can be used to filter and
59
    // retrieve data
60
    //
61
    // Let's start by seeding the database with some shapes of various sizes:
62
19
    for sides in 3..=20 {
63
18
        Shape { sides }.push_into_async(&db).await?;
64
    }
65

            
66
    // And, let's add a few shapes with the same number of sides
67
1
    Shape { sides: 3 }.push_into_async(&db).await?;
68
1
    Shape { sides: 4 }.push_into_async(&db).await?;
69

            
70
    // At this point, our database should have 3 triangles:
71
    // begin rustme snippet: snippet-c
72
1
    let triangles = db
73
1
        .view::<ShapesByNumberOfSides>()
74
1
        .with_key(3)
75
1
        .query()
76
1
        .await?;
77
1
    println!("Number of triangles: {}", triangles.len());
78
1
    // end rustme snippet
79
1

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

            
84
    // If you want the associated documents, use query_with_collection_docs:
85
3
    for entry in &db
86
1
        .view::<ShapesByNumberOfSides>()
87
1
        .with_key(3)
88
2
        .query_with_collection_docs()
89
2
        .await?
90
3
    {
91
3
        println!(
92
3
            "Shape ID {} has {} sides",
93
3
            entry.document.header.id, entry.document.contents.sides
94
3
        );
95
3
    }
96

            
97
    // The reduce() function takes the "values" emitted during the map()
98
    // function, and reduces a list down to a single value. In this example, the
99
    // reduce function is acting as a count. So, if you want to query for the
100
    // number of shapes, we don't need to fetch all the records, we can just
101
    // retrieve the result of the calculation directly.
102
    //
103
    // So, here we're using reduce() to count the number of shapes with 4 sides.
104
1
    println!(
105
1
        "Number of quads: {} (expected 2)",
106
1
        db.view::<ShapesByNumberOfSides>()
107
1
            .with_key(4)
108
1
            .reduce()
109
1
            .await?
110
    );
111

            
112
    // Or, 5 shapes that are triangles or quads
113
1
    println!(
114
1
        "Number of quads and triangles: {} (expected 5)",
115
1
        db.view::<ShapesByNumberOfSides>()
116
1
            .with_keys([3, 4])
117
1
            .reduce()
118
1
            .await?
119
    );
120

            
121
    // And, 10 shapes that have more than 10 sides
122
1
    println!(
123
1
        "Number of shapes with more than 10 sides: {} (expected 10)",
124
1
        db.view::<ShapesByNumberOfSides>()
125
1
            .with_key_range(11..u32::MAX)
126
1
            .reduce()
127
1
            .await?
128
    );
129

            
130
1
    Ok(())
131
1
}
132

            
133
1
#[test]
134
1
fn runs() {
135
1
    drop(std::fs::remove_dir_all("view-examples.bonsaidb"));
136
1
    main().unwrap()
137
1
}