aerospike/query/
statement.rs

1// Copyright 2015-2018 Aerospike, Inc.
2//
3// Portions may be licensed to Aerospike, Inc. under one or more contributor
4// license agreements.
5//
6// Licensed under the Apache License, Version 2.0 (the "License"); you may not
7// use this file except in compliance with the License. You may obtain a copy of
8// the License at http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13// License for the specific language governing permissions and limitations under
14// the License.
15
16use crate::errors::{ErrorKind, Result};
17use crate::query::Filter;
18use crate::Bins;
19use crate::Value;
20
21#[derive(Clone)]
22pub struct Aggregation {
23    pub package_name: String,
24    pub function_name: String,
25    pub function_args: Option<Vec<Value>>,
26}
27
28/// Query statement parameters.
29pub struct Statement {
30    /// Namespace
31    pub namespace: String,
32
33    /// Set name
34    pub set_name: String,
35
36    /// Optional index name
37    pub index_name: Option<String>,
38
39    /// Optional list of bin names to return in query.
40    pub bins: Bins,
41
42    /// Optional list of query filters. Currently, only one filter is allowed by the server on a
43    /// secondary index lookup.
44    pub filters: Option<Vec<Filter>>,
45
46    /// Optional Lua aggregation function parameters.
47    pub aggregation: Option<Aggregation>,
48}
49
50impl Statement {
51    /// Create a new query statement with the given namespace, set name and optional list of bin
52    /// names.
53    ///
54    /// # Examples
55    ///
56    /// Create a new statement to query the namespace "foo" and set "bar" and return the "name" and
57    /// "age" bins for each matching record.
58    ///
59    /// ```rust
60    /// # use aerospike::*;
61    ///
62    /// let stmt = Statement::new("foo", "bar", Bins::from(["name", "age"]));
63    /// ```
64    pub fn new(namespace: &str, set_name: &str, bins: Bins) -> Self {
65        Statement {
66            namespace: namespace.to_owned(),
67            set_name: set_name.to_owned(),
68            bins,
69            index_name: None,
70            aggregation: None,
71            filters: None,
72        }
73    }
74
75    /// Add a query filter to the statement. Currently, only one filter is allowed by the server on
76    /// a secondary index lookup.
77    ///
78    /// # Example
79    ///
80    /// This example uses a numeric index on bin _baz_ in namespace _foo_ within set _bar_ to find
81    /// all records using a filter with the range 0 to 100 inclusive:
82    ///
83    /// ```rust
84    /// # use aerospike::*;
85    ///
86    /// let mut stmt = Statement::new("foo", "bar", Bins::from(["name", "age"]));
87    /// stmt.add_filter(as_range!("baz", 0, 100));
88    /// ```
89    pub fn add_filter(&mut self, filter: Filter) {
90        if let Some(ref mut filters) = self.filters {
91            filters.push(filter);
92        } else {
93            let mut filters = vec![];
94            filters.push(filter);
95            self.filters = Some(filters);
96        }
97    }
98
99    /// Set Lua aggregation function parameters.
100    pub fn set_aggregate_function(
101        &mut self,
102        package_name: &str,
103        function_name: &str,
104        function_args: Option<&[Value]>,
105    ) {
106        let agg = Aggregation {
107            package_name: package_name.to_owned(),
108            function_name: function_name.to_owned(),
109            function_args: match function_args {
110                Some(args) => Some(args.to_vec()),
111                None => None,
112            },
113        };
114        self.aggregation = Some(agg);
115    }
116
117    #[doc(hidden)]
118    pub fn is_scan(&self) -> bool {
119        match self.filters {
120            Some(ref filters) => filters.is_empty(),
121            None => true,
122        }
123    }
124
125    #[doc(hidden)]
126    pub fn validate(&self) -> Result<()> {
127        if let Some(ref filters) = self.filters {
128            if filters.len() > 1 {
129                bail!(ErrorKind::InvalidArgument(
130                    "Too many filter expressions".to_string()
131                ));
132            }
133        }
134
135        if self.set_name.is_empty() {
136            bail!(ErrorKind::InvalidArgument("Empty set name".to_string()));
137        }
138
139        if let Some(ref index_name) = self.index_name {
140            if index_name.is_empty() {
141                bail!(ErrorKind::InvalidArgument("Empty index name".to_string()));
142            }
143        }
144
145        if let Some(ref agg) = self.aggregation {
146            if agg.package_name.is_empty() {
147                bail!(ErrorKind::InvalidArgument(
148                    "Empty UDF package name".to_string()
149                ));
150            }
151
152            if agg.function_name.is_empty() {
153                bail!(ErrorKind::InvalidArgument(
154                    "Empty UDF function name".to_string()
155                ));
156            }
157        }
158
159        Ok(())
160    }
161}