weaviate_community/collections/
query.rs

1/// GraphQL related structures.
2///
3/// Contains the ability to generate the following queries:
4/// - Get
5/// - Aggregate
6/// - Explore
7///
8/// Also contains the ability to create a raw query from a string.
9///
10/// This is currently incomplete - most of the data types for the different options are Strings,
11/// which I want to enforce to the expected values a little better. Mainly just getting the
12/// structure and ability to run completed now.
13///
14/// There are also some places I need to return an error from which I am yet to do.
15///
16/// I've also not had a chance to test a lot of the functionality, so lots will be broken like the
17/// near_text or near_image as I have not implemented the `encoding` functionality yet.
18use serde::{Deserialize, Serialize};
19use uuid::Uuid;
20
21/// RawQuery struct to hold a custom `raw` query.
22#[derive(Serialize, Deserialize, Debug)]
23pub struct RawQuery {
24    pub query: String,
25}
26
27impl RawQuery {
28    /// Retrieve a raw GraphQL query.
29    ///
30    /// # Example
31    /// ```
32    /// use weaviate_community::collections::query::RawQuery;
33    ///
34    /// let my_query_str = "{
35    ///   Get {
36    ///     JeopardyQuestion {
37    ///       question
38    ///       answer
39    ///       points
40    ///     }
41    ///   }
42    /// }";
43    ///
44    /// let query = RawQuery::new(my_query_str);
45    /// ```
46    pub fn new(query: &str) -> Self {
47        RawQuery {
48            query: query.into(),
49        }
50    }
51}
52
53/// AggregatorQuery struct to hold an Aggregate query.
54#[derive(Serialize, Deserialize, Debug)]
55pub struct AggregateQuery {
56    pub query: String,
57}
58
59impl AggregateQuery {
60    /// Create a new `AggregateBuilder` for the GraphQL `AggregateQuery`.
61    ///
62    /// This is the same as `AggregateBuilder::new()`.
63    ///
64    /// # Example
65    /// ```
66    /// use weaviate_community::collections::query::AggregateQuery;
67    ///
68    /// let query = AggregateQuery::builder("Article");
69    /// ```
70    pub fn builder(class_name: &str) -> AggregateBuilder {
71        AggregateBuilder::new(class_name)
72    }
73}
74
75/// The builder for the `AggregateQuery`.
76#[derive(Serialize, Deserialize, Debug)]
77pub struct AggregateBuilder {
78    pub class_name: String,
79    pub object_limit: Option<u32>,
80    pub meta_count: Option<bool>,
81    pub fields: Option<Vec<String>>,
82    pub where_clause: Option<String>,
83    pub group_by: Option<String>,
84    pub near: Option<String>,
85    pub tenant: Option<String>,
86    pub limit: Option<u32>,
87}
88
89impl AggregateBuilder {
90    /// Create a new AggregateBuilder item.
91    ///
92    /// This is the same as `AggregateQuery::builder()`.
93    ///
94    /// # Example
95    /// ```
96    /// use weaviate_community::collections::query::AggregateBuilder;
97    ///
98    /// let query_builder = AggregateBuilder::new("Article");
99    /// ```
100    pub fn new(class_name: &str) -> Self {
101        AggregateBuilder {
102            class_name: class_name.into(),
103            object_limit: None,
104            meta_count: None,
105            fields: None,
106            where_clause: None,
107            group_by: None,
108            near: None,
109            tenant: None,
110            limit: None,
111        }
112    }
113
114    /// Set the `objectLimit: <value>` as a filter to the query to limit the vector search results
115    /// used within the aggregation.
116    ///
117    /// Should only be set in conjunction when used in conjunction with a `near` filter (for
118    /// example, `with_near_text()`
119    ///
120    /// # Example
121    /// ```
122    /// use weaviate_community::collections::query::AggregateBuilder;
123    ///
124    /// let query_builder = AggregateBuilder::new("Article")
125    ///     .with_object_limit(1);
126    /// ```
127    pub fn with_object_limit(mut self, value: u32) -> AggregateBuilder {
128        self.object_limit = Some(value);
129        self
130    }
131
132    /// Add `meta{count}` to the body of the query when called.
133    ///
134    /// # Example
135    /// ```
136    /// use weaviate_community::collections::query::AggregateBuilder;
137    ///
138    /// let query_builder = AggregateBuilder::new("Article")
139    ///     .with_meta_count();
140    /// ```
141    pub fn with_meta_count(mut self) -> AggregateBuilder {
142        self.meta_count = Some(true);
143        self
144    }
145
146    /// Appends the specified fields in the aggregate query body.
147    ///
148    /// # Example
149    /// ```
150    /// use weaviate_community::collections::query::AggregateBuilder;
151    ///
152    /// let query_builder = AggregateBuilder::new("Article")
153    ///     .with_fields(vec!["wordCount { mean }"]);
154    /// ```
155    pub fn with_fields(mut self, fields: Vec<&str>) -> AggregateBuilder {
156        let fields = fields.iter().map(|field| field.to_string()).collect();
157        self.fields = Some(fields);
158        self
159    }
160
161    /// Set the `where` filter in the aggregate query.
162    ///
163    /// # Example -> todo
164    /// ```
165    /// ```
166    pub fn with_where(mut self, where_clause: &str) -> AggregateBuilder {
167        self.where_clause = Some(where_clause.into());
168        self
169    }
170
171    /// Set the `group_by` filter in the aggregate query.
172    ///
173    /// This may also require `groupedBy {...}` to be specified in with_fields().
174    ///
175    /// # Example
176    /// ```
177    /// use weaviate_community::collections::query::AggregateBuilder;
178    ///
179    /// let query_builder = AggregateBuilder::new("Article")
180    ///     .with_group_by_filter("[\"inPublication\"]")
181    ///     .with_fields(vec!["groupedBy {value path}"]);
182    /// ```
183    pub fn with_group_by_filter(mut self, group_by: &str) -> AggregateBuilder {
184        self.group_by = Some(group_by.into());
185        self
186    }
187
188    /// Set the `nearText` filter in the aggregate query. This filter can be used with text modules
189    /// (text2vec).
190    ///
191    /// Note that the `autocorrect` field is only available with the `text-spellcheck` Weaviate
192    /// module.
193    ///
194    /// # Example
195    /// ```
196    /// ```
197    pub fn with_near_text(mut self, near_text: &str) -> AggregateBuilder {
198        if self.near.is_some() {
199            // raise an error here, can only have one near filter
200        }
201        self.near = Some(near_text.into());
202        self
203    }
204
205    /// Set the `nearVector` filter in the aggregate query.
206    ///
207    /// # Example
208    /// ```
209    /// ```
210    pub fn with_near_vector(mut self, near_vector: &str) -> AggregateBuilder {
211        if self.near.is_some() {
212            // raise an error here, can only have one near filter
213        }
214        self.near = Some(near_vector.into());
215        self
216    }
217
218    /// Set the `nearObject` filter in the aggregate query.
219    ///
220    /// # Example
221    /// ```
222    /// ```
223    pub fn with_near_object(mut self, near_object: &str) -> AggregateBuilder {
224        if self.near.is_some() {
225            // raise an error here, can only have one near filter
226        }
227        self.near = Some(near_object.into());
228        self
229    }
230
231    /// Set the `nearImage` filter in the aggregate query.
232    ///
233    /// # Example
234    /// ```
235    /// ```
236    pub fn with_near_image(mut self, near_image: &str) -> AggregateBuilder {
237        if self.near.is_some() {
238            // raise an error here, can only have one near filter
239        }
240        self.near = Some(near_image.into());
241        self
242    }
243
244    /// Set the `nearAudio` filter in the aggregate query.
245    ///
246    /// # Example
247    /// ```
248    /// ```
249    pub fn with_near_audio(mut self, near_audio: &str) -> AggregateBuilder {
250        if self.near.is_some() {
251            // raise an error here, can only have one near filter
252        }
253        self.near = Some(near_audio.into());
254        self
255    }
256
257    /// Set the `nearVideo` filter in the aggregate query.
258    ///
259    /// # Example
260    /// ```
261    /// ```
262    pub fn with_near_video(mut self, near_video: &str) -> AggregateBuilder {
263        if self.near.is_some() {
264            // raise an error here, can only have one near filter
265        }
266        self.near = Some(near_video.into());
267        self
268    }
269
270    /// Set the `nearDepth` filter in the aggregate query.
271    ///
272    /// # Example
273    /// ```
274    /// ```
275    pub fn with_near_depth(mut self, near_depth: &str) -> AggregateBuilder {
276        if self.near.is_some() {
277            // raise an error here, can only have one near filter
278        }
279        self.near = Some(near_depth.into());
280        self
281    }
282
283    /// Set the `nearThermal` filter in the aggregate query.
284    ///
285    /// # Example
286    /// ```
287    /// ```
288    pub fn with_near_thermal(mut self, near_thermal: &str) -> AggregateBuilder {
289        if self.near.is_some() {
290            // raise an error here, can only have one near filter
291        }
292        self.near = Some(near_thermal.into());
293        self
294    }
295
296    /// Set the `nearIMU` filter in the aggregate query.
297    ///
298    /// # Example
299    /// ```
300    /// ```
301    pub fn with_near_imu(mut self, near_imu: &str) -> AggregateBuilder {
302        if self.near.is_some() {
303            // raise an error here, can only have one near filter
304        }
305        self.near = Some(near_imu.into());
306        self
307    }
308
309    /// Set the `tenant` filter in the aggregate query.
310    ///
311    /// # Example
312    /// ```
313    /// ```
314    pub fn with_tenant(mut self, tenant: &str) -> AggregateBuilder {
315        self.tenant = Some(tenant.into());
316        self
317    }
318
319    /// Set the `limit` filter in the aggregate query.
320    ///
321    /// Limits the number of results that are returned.
322    ///
323    /// # Example
324    /// ```
325    /// use weaviate_community::collections::query::AggregateBuilder;
326    ///
327    /// let query_builder = AggregateBuilder::new("Article")
328    ///     .with_limit(1);
329    /// ```
330    pub fn with_limit(mut self, limit: u32) -> AggregateBuilder {
331        self.limit = Some(limit);
332        self
333    }
334
335    /// Build the `AggregateQuery` to use within within a GraphQL Aggregate request.
336    ///
337    /// # Example
338    /// ```
339    /// use weaviate_community::collections::query::AggregateBuilder;
340    ///
341    /// let query = AggregateBuilder::new("Article")
342    ///     .with_fields(vec!["wordCount {count maximum mean median minimum mode sum type}"])
343    ///     .build();
344    /// ```
345    ///
346    /// ```
347    /// use weaviate_community::collections::query::AggregateQuery;
348    ///
349    /// let query = AggregateQuery::builder("Article")
350    ///     .with_meta_count()
351    ///     .with_fields(vec!["wordCount {count maximum mean median minimum mode sum type}"])
352    ///     .build();
353    /// ```
354    ///
355    /// Both examples will create the following AggregateQuery:
356    /// ```text
357    /// AggregateQuery {
358    ///   query: "{
359    ///     Aggregate {
360    ///       Article
361    ///       {
362    ///         meta {count}
363    ///         wordCount {count maximum mean median minimum mode sum type}
364    ///       }
365    ///     }
366    ///   }"
367    /// }
368    /// ```
369    pub fn build(&self) -> AggregateQuery {
370        // Path
371        let mut query = String::from("{\n");
372        query.push_str("  Aggregate {\n");
373        query.push_str(format!("    {} \n", self.class_name).as_str());
374
375        // Filters
376        if self.contains_filter() {
377            query.push_str("    (\n");
378            if let Some(where_clause) = &self.where_clause {
379                query.push_str(format!("      where: {}\n", where_clause).as_str());
380            }
381            if let Some(group_by) = &self.group_by {
382                query.push_str(format!("      groupBy: {}\n", group_by).as_str());
383            }
384            if let Some(near) = &self.where_clause {
385                query.push_str(format!("      near: {}\n", near).as_str());
386            }
387            if let Some(object_limit) = &self.object_limit {
388                query.push_str(format!("      objectLimit: {}\n", object_limit).as_str());
389            }
390            if let Some(tenant) = &self.tenant {
391                query.push_str(format!("      tenant: {}\n", tenant).as_str());
392            }
393            if let Some(limit) = &self.limit {
394                query.push_str(format!("      limit: {}\n", limit).as_str());
395            }
396            query.push_str("    )\n");
397        }
398
399        // Body
400        query.push_str("    {\n");
401        if let Some(_) = &self.meta_count {
402            query.push_str("      meta{count}\n");
403        }
404
405        if let Some(fields) = &self.fields {
406            query.push_str(format!("      {}\n", fields.join(" ")).as_str());
407        }
408        query.push_str("    }\n");
409        query.push_str("  }\n");
410        query.push_str("}");
411        AggregateQuery { query }
412    }
413
414    /// Check if the query contains a filter.
415    fn contains_filter(&self) -> bool {
416        match self.where_clause.is_some()
417            || self.group_by.is_some()
418            || self.near.is_some()
419            || self.object_limit.is_some()
420            || self.tenant.is_some()
421            || self.limit.is_some()
422        {
423            true => true,
424            false => false,
425        }
426    }
427}
428
429/// ExploreQuery struct to hold an Explore query.
430#[derive(Serialize, Deserialize, Debug)]
431pub struct ExploreQuery {
432    pub query: String,
433}
434
435impl ExploreQuery {
436    /// Create a new `ExploreBuilder` for the GraphQL `ExploreQuery`.
437    ///
438    /// This is the same as `ExploreBuilder::new()`.
439    ///
440    /// # Example
441    /// ```
442    /// use weaviate_community::collections::query::ExploreQuery;
443    ///
444    /// let query = ExploreQuery::builder();
445    /// ```
446    pub fn builder() -> ExploreBuilder {
447        ExploreBuilder::new()
448    }
449}
450
451/// The builder for the `ExploreQuery`
452#[derive(Serialize, Deserialize, Debug)]
453pub struct ExploreBuilder {
454    limit: Option<u32>,
455    near_text: Option<String>,
456    near_vector: Option<String>,
457    fields: Option<Vec<String>>,
458}
459
460impl ExploreBuilder {
461    /// Create a new ExploreBuilder item.
462    ///
463    /// This is the same as `ExploreQuery::builder()`.
464    ///
465    /// # Example
466    /// ```
467    /// use weaviate_community::collections::query::ExploreBuilder;
468    ///
469    /// let query_builder = ExploreBuilder::new();
470    /// ```
471    pub fn new() -> Self {
472        ExploreBuilder {
473            limit: None,
474            near_text: None,
475            near_vector: None,
476            fields: None,
477        }
478    }
479
480    /// Appends the specified fields in the explore query body.
481    ///
482    /// # Example
483    /// ```
484    /// use weaviate_community::collections::query::ExploreBuilder;
485    ///
486    /// let query_builder = ExploreBuilder::new()
487    ///     .with_fields(vec!["beacon", "certainty", "className"]);
488    /// ```
489    pub fn with_fields(mut self, fields: Vec<&str>) -> ExploreBuilder {
490        let fields = fields.iter().map(|field| field.to_string()).collect();
491        self.fields = Some(fields);
492        self
493    }
494
495    /// Sets the `limit` in the explore query filters.
496    ///
497    /// # Example
498    /// ```
499    /// use weaviate_community::collections::query::ExploreBuilder;
500    ///
501    /// let query_builder = ExploreBuilder::new()
502    ///     .with_limit(1);
503    /// ```
504    pub fn with_limit(mut self, limit: u32) -> ExploreBuilder {
505        self.limit = Some(limit);
506        self
507    }
508
509    /// Sets the `nearText` value in the explore query filters.
510    ///
511    /// One of either `with_near_text` or `with_near_vector` must be set in the query at point of
512    /// build.
513    ///
514    /// # Example
515    /// ```
516    /// ```
517    pub fn with_near_text(mut self, near_text: &str) -> ExploreBuilder {
518        self.near_text = Some(near_text.into());
519        self
520    }
521
522    /// Sets the `nearVector` value in the explore query filters.
523    ///
524    /// One of either `with_near_text` or `with_near_vector` must be set in the query at point of
525    /// build.
526    ///
527    /// # Example
528    /// ```
529    /// ```
530    pub fn with_near_vector(mut self, near_vector: &str) -> ExploreBuilder {
531        self.near_vector = Some(near_vector.into());
532        self
533    }
534
535    /// Build the `ExploreQuery` to use within within a GraphQL Explore request.
536    ///
537    /// # Examples -> todo: need to add a nearVector or nearText
538    /// ```no_run
539    /// use weaviate_community::collections::query::ExploreBuilder;
540    ///
541    /// let query = ExploreBuilder::new().build();
542    /// ```
543    ///
544    /// ```no_run
545    /// use weaviate_community::collections::query::ExploreQuery;
546    ///
547    /// let query = ExploreQuery::builder().build();
548    /// ```
549    ///
550    /// Both examples will create the following ExploreQuery:
551    /// ```text
552    /// ```
553    pub fn build(&self) -> ExploreQuery {
554        if self.near_text.is_none() && self.near_vector.is_none() {
555            // raise an error, one is required. TBD if other near fields can be used
556        }
557
558        // Path
559        let mut query = String::from("{\n");
560        query.push_str("  Explore\n");
561
562        // Filters
563        query.push_str("  (\n");
564        if let Some(limit) = &self.limit {
565            query.push_str(format!("    limit: {}\n", limit).as_str());
566        }
567        if let Some(near_text) = &self.near_text {
568            query.push_str(format!("    nearText: {}\n", near_text).as_str());
569        }
570        if let Some(near_vector) = &self.near_vector {
571            query.push_str(format!("    nearVector: {}\n", near_vector).as_str());
572        }
573        query.push_str("  )\n");
574
575        // Body
576        query.push_str("  {\n");
577        if let Some(fields) = &self.fields {
578            query.push_str(format!("    {}\n", fields.join(" ")).as_str());
579        }
580        query.push_str("  }\n");
581        query.push_str("}");
582
583        ExploreQuery { query }
584    }
585}
586
587/// GetQuery struct to hold a Get query.
588#[derive(Serialize, Deserialize, Debug)]
589pub struct GetQuery {
590    pub query: String,
591}
592
593impl GetQuery {
594    /// Create a new `GetBuilder` for the GraphQL `GetQuery`.
595    ///
596    /// This is the same as `GetBuilder::new()`.
597    ///
598    /// # Example
599    /// ```
600    /// use weaviate_community::collections::query::GetQuery;
601    ///
602    /// let query = GetQuery::builder("Article", vec!["author"]);
603    /// ```
604    pub fn builder(class_name: &str, properties: Vec<&str>) -> GetBuilder {
605        GetBuilder::new(class_name, properties)
606    }
607}
608
609/// The builder for the `GetQuery`
610#[derive(Serialize, Deserialize, Debug)]
611pub struct GetBuilder {
612    pub class_name: String,
613    pub properties: Vec<String>,
614    pub additional: Option<Vec<String>>,
615    pub where_clause: Option<String>,
616    pub limit: Option<u32>,
617    pub offset: Option<u32>,
618    pub after: Option<Uuid>, // cant use with where, near<media>, bm25, hybrid, etc
619    pub near_text: Option<String>,
620    pub near_vector: Option<String>,
621    pub near_image: Option<String>,
622    pub near_object: Option<String>,
623    pub near_video: Option<String>,
624    pub near_audio: Option<String>,
625    pub near_thermal: Option<String>,
626    pub near_imu: Option<String>,
627    pub near_depth: Option<String>,
628    pub sort: Option<String>,
629    pub bm25: Option<String>,
630    pub hybrid: Option<String>,
631    pub group_by: Option<String>,
632    pub tenant: Option<String>,
633    pub autocut: Option<u32>,
634    pub ask: Option<String>,
635}
636
637impl GetBuilder {
638    /// Create a new GetBuilder item.
639    ///
640    /// This is the same as `GetQuery::builder()`.
641    ///
642    /// # Example
643    /// ```
644    /// use weaviate_community::collections::query::GetBuilder;
645    ///
646    /// let query_builder = GetBuilder::new(
647    ///     "JeopardyQuestion",
648    ///     vec!["question", "answer", "points"]
649    /// );
650    /// ```
651    pub fn new(class_name: &str, properties: Vec<&str>) -> GetBuilder {
652        GetBuilder {
653            class_name: class_name.into(),
654            properties: properties.iter().map(|prop| prop.to_string()).collect(),
655            limit: None,
656            offset: None,
657            additional: None,
658            tenant: None,
659            autocut: None,
660            after: None,
661            sort: None,
662            where_clause: None,
663            near_text: None,
664            near_vector: None,
665            near_image: None,
666            near_object: None,
667            near_video: None,
668            near_audio: None,
669            near_thermal: None,
670            near_imu: None,
671            near_depth: None,
672            hybrid: None,
673            bm25: None,
674            ask: None,
675            group_by: None,
676        }
677    }
678
679    /// Sets the `limit` in the get query filters.
680    ///
681    /// # Example
682    /// ```
683    /// use weaviate_community::collections::query::GetBuilder;
684    ///
685    /// let query_builder = GetBuilder::new(
686    ///     "JeopardyQuestion",
687    ///     vec!["question", "answer", "points"]
688    /// ).with_limit(1);
689    /// ```
690    pub fn with_limit(mut self, limit: u32) -> GetBuilder {
691        self.limit = Some(limit);
692        self
693    }
694
695    /// Set the `offset` in the get query filters.
696    ///
697    /// # Example
698    /// ```
699    /// use weaviate_community::collections::query::GetBuilder;
700    ///
701    /// let query_builder = GetBuilder::new(
702    ///     "JeopardyQuestion",
703    ///     vec!["question", "answer", "points"]
704    /// )
705    ///     .with_limit(1)
706    ///     .with_offset(1);
707    /// ```
708    pub fn with_offset(mut self, offset: u32) -> GetBuilder {
709        self.offset = Some(offset);
710        self
711    }
712
713    /// Specify the `_additional` properties to retrieve in the query result.
714    ///
715    /// Note that the additional properties are properties that cannot be specified in the regular
716    /// properties field, such as the `vector`, or the object UUID (`id`). More `_additional`
717    /// properties are described [here](https://weaviate.io/developers/weaviate/api/graphql/additional-properties).
718    ///
719    /// Cross referenced properties should be specified in the regular properties field (in the
720    /// `new` method).
721    ///
722    /// # Example
723    /// ```
724    /// use weaviate_community::collections::query::GetBuilder;
725    ///
726    /// let query_builder = GetBuilder::new("JeopardyQuestion", vec![])
727    ///     .with_additional(vec!["vector"]);
728    /// ```
729    pub fn with_additional(mut self, additional: Vec<&str>) -> GetBuilder {
730        let additional = additional.iter().map(|item| item.to_string()).collect();
731        self.additional = Some(additional);
732        self
733    }
734
735    /// Specify the `tenant` in the get query filter.
736    ///
737    /// For classes that have multi-tenancy enabled, the tenant parameter must be specified in each
738    /// query.
739    ///
740    /// # Example
741    /// ```
742    /// use weaviate_community::collections::query::GetBuilder;
743    ///
744    /// let query_builder = GetBuilder::new("JeopardyQuestion", vec!["answer"])
745    ///     .with_tenant("tenantA");
746    /// ```
747    pub fn with_tenant(mut self, tenant: &str) -> GetBuilder {
748        self.tenant = Some(tenant.into());
749        self
750    }
751
752    /// Specify the `autocut` search filter in the get query.
753    ///
754    /// The `autocut` filter is an argument that can be added to class objects retrieved by the
755    /// `near<media>`, `bm25`, and `hybrid` operators.
756    ///
757    /// More information on `autocut` can be found [here](https://weaviate.io/developers/weaviate/api/graphql/additional-operators#autocut)
758    ///
759    /// # Example
760    /// ```
761    /// use weaviate_community::collections::query::GetBuilder;
762    ///
763    /// let query_builder = GetBuilder::new("JeopardyQuestion", vec!["question", "answer"])
764    ///     .with_hybrid("{query: \"food\"}")
765    ///     .with_autocut(1)
766    ///     .build();
767    /// ```
768    pub fn with_autocut(mut self, autocut: u32) -> GetBuilder {
769        self.autocut = Some(autocut);
770        self
771    }
772
773    /// Specify the `after` search filter in the get query.
774    ///
775    /// The `after` operator can be used to sequentially retrieve class objects from Weaviate.
776    ///
777    /// More information on `after` can be found [here](https://weaviate.io/developers/weaviate/api/graphql/additional-operators#cursor-with-after)
778    ///
779    /// # Example
780    /// ```
781    /// ```
782    pub fn with_after(mut self, after: Uuid) -> GetBuilder {
783        self.after = Some(after);
784        self
785    }
786
787    /// Specify the `sort` search filter in the get query.
788    ///
789    /// Any primitive property types can be sorted, such as `text`, `string`, `number`, or `int`.
790    ///
791    /// When a query has a natural order (e.g. because of a near<media> vector search), adding a
792    /// sort operator will override that order.
793    ///
794    /// More on sorting in Weaviate can be found [here](https://weaviate.io/developers/weaviate/api/graphql/additional-operators#sorting)
795    pub fn with_sort(mut self, sort: &str) -> GetBuilder {
796        self.sort = Some(sort.into());
797        self
798    }
799
800    /// Specify conditionals to add to the `where` search filter in the get query.
801    ///
802    /// More information on conditionals can be found [here](https://weaviate.io/developers/weaviate/api/graphql/filters)
803    ///
804    /// # Example
805    /// ```
806    /// ```
807    pub fn with_where(mut self, where_clause: &str) -> GetBuilder {
808        self.where_clause = Some(where_clause.into());
809        self
810    }
811
812    /// Set the `nearText` filter in the get query.
813    ///
814    /// # Example
815    /// ```
816    /// ```
817    pub fn with_near_text(mut self, near_text: &str) -> GetBuilder {
818        self.near_text = Some(near_text.into());
819        self
820    }
821
822    /// Set the `nearVector` filter in the get query.
823    ///
824    /// # Example
825    /// ```
826    /// ```
827    pub fn with_near_vector(mut self, near_vector: &str) -> GetBuilder {
828        self.near_vector = Some(near_vector.into());
829        self
830    }
831
832    /// Set the `nearObject` filter in the get query.
833    ///
834    /// # Example
835    /// ```
836    /// ```
837    pub fn with_near_object(mut self, near_object: &str) -> GetBuilder {
838        self.near_object = Some(near_object.into());
839        self
840    }
841
842    /// Set the `nearImage` filter in the get query.
843    ///
844    /// # Example
845    /// ```
846    /// ```
847    pub fn with_near_image(mut self, near_image: &str) -> GetBuilder {
848        self.near_image = Some(near_image.into());
849        self
850    }
851
852    /// Set the `nearVideo` filter in the get query.
853    ///
854    /// # Example
855    /// ```
856    /// ```
857    pub fn with_near_video(mut self, near_video: &str) -> GetBuilder {
858        self.near_video = Some(near_video.into());
859        self
860    }
861
862    /// Set the `nearAudio` filter in the get query.
863    ///
864    /// # Example
865    /// ```
866    /// ```
867    pub fn with_near_audio(mut self, near_audio: &str) -> GetBuilder {
868        self.near_audio = Some(near_audio.into());
869        self
870    }
871
872    /// Set the `nearThermal` filter in the get query.
873    ///
874    /// # Example
875    /// ```
876    /// ```
877    pub fn with_near_thermal(mut self, near_thermal: &str) -> GetBuilder {
878        self.near_thermal = Some(near_thermal.into());
879        self
880    }
881
882    /// Set the `nearIMU` filter in the get query.
883    ///
884    /// # Example
885    /// ```
886    /// ```
887    pub fn with_near_imu(mut self, near_imu: &str) -> GetBuilder {
888        self.near_imu = Some(near_imu.into());
889        self
890    }
891
892    /// Set the `nearDepth` filter in the get query.
893    ///
894    /// # Example
895    /// ```
896    /// ```
897    pub fn with_near_depth(mut self, near_depth: &str) -> GetBuilder {
898        self.near_depth = Some(near_depth.into());
899        self
900    }
901
902    /// Specify the `hybrid` search filter in the get query.
903    ///
904    /// The `hybrid` operator produces results based on a weighted combination of results from a
905    /// keyword (bm25) search and a vector (near<media>) search.
906    ///
907    /// # Example
908    /// ```
909    /// use weaviate_community::collections::query::GetBuilder;
910    ///
911    /// let query_builder = GetBuilder::new("JeopardyQuestion", vec!["question", "answer"])
912    ///     .with_hybrid("{query: \"food\"}")
913    ///     .with_limit(3)
914    ///     .build();
915    /// ```
916    ///
917    /// This will generate the following GetQuery:
918    /// ```text
919    /// GetQuery {
920    ///   query: "{
921    ///     Get {
922    ///       JeopardyQuestion
923    ///       (
924    ///         limit: 3
925    ///         hybrid: {query: "food"]
926    ///       )
927    ///       {
928    ///         question
929    ///         answer
930    ///       }
931    ///     }
932    ///   }
933    /// }
934    /// ```
935    pub fn with_hybrid(mut self, hybrid: &str) -> GetBuilder {
936        self.hybrid = Some(hybrid.into());
937        self
938    }
939
940    /// Specify the `bm25` search filter in the get query.
941    ///
942    /// To use BM25 search, you must provide a search string as a minimum.
943    ///
944    /// More on Keyword (BM25) search can be found [here](https://weaviate.io/developers/weaviate/search/bm25)
945    ///
946    /// # Example
947    /// ```
948    /// use weaviate_community::collections::query::GetBuilder;
949    ///
950    /// let query_builder = GetBuilder::new("JeopardyQuestion", vec!["question", "answer"])
951    ///     .with_bm25("{query: \"food\"}")
952    ///     .with_limit(3)
953    ///     .build();
954    /// ```
955    ///
956    /// This will generate the following GetQuery:
957    /// ```text
958    /// GetQuery {
959    ///   query: "{
960    ///     Get {
961    ///       JeopardyQuestion
962    ///       (
963    ///         limit: 3
964    ///         bm25: {query: "food"]
965    ///       )
966    ///       {
967    ///         question
968    ///         answer
969    ///       }
970    ///     }
971    ///   }
972    /// }
973    /// ```
974    /// and would look for objects containing the keyword `food` anywhere in the object if ran.
975    pub fn with_bm25(mut self, bm25: &str) -> GetBuilder {
976        self.bm25 = Some(bm25.into());
977        self
978    }
979
980    /// Specify the `groupBy` in the get query filters.
981    ///
982    /// To use `groupBy`:
983    /// - Provide the property by which the results should be grouped,
984    /// - The maximum number of groups, and
985    /// - The maximum number of objects per group
986    ///
987    /// # Example
988    /// ```
989    /// ```
990    pub fn with_group_by(mut self, group_by: &str) -> GetBuilder {
991        self.group_by = Some(group_by.into());
992        self
993    }
994
995    ///
996    pub fn with_ask(mut self, ask: &str) -> GetBuilder {
997        self.ask = Some(ask.into());
998        self
999    }
1000
1001    /// Build the `GetQuery` to use within within a GraphQL Get request.
1002    ///
1003    /// # Example
1004    /// ```
1005    /// use weaviate_community::collections::query::GetBuilder;
1006    ///
1007    /// let query = GetBuilder::new(
1008    ///     "JeopardyQuestion",
1009    ///     vec!["question", "answer", "points"]
1010    /// ).build();
1011    /// ```
1012    ///
1013    /// ```
1014    /// use weaviate_community::collections::query::GetQuery;
1015    ///
1016    /// let query = GetQuery::builder(
1017    ///     "JeopardyQuestion",
1018    ///     vec!["question", "answer", "points"]
1019    /// ).build();
1020    /// ```
1021    ///
1022    /// Both examples will create the following GetQuery:
1023    /// ```text
1024    /// GetQuery {
1025    ///   query: "{
1026    ///     Get {
1027    ///       JeopardyQuestion
1028    ///       {
1029    ///         question
1030    ///         answer
1031    ///         points
1032    ///       }
1033    ///     }
1034    ///   }"
1035    /// }
1036    /// ```
1037    pub fn build(&self) -> GetQuery {
1038        // Path
1039        let mut query = String::from("{\n");
1040        query.push_str("  Get {\n");
1041        query.push_str(format!("    {} \n", self.class_name).as_str());
1042
1043        // Filters
1044        if self.contains_filter() {
1045            query.push_str("    (\n");
1046            if let Some(where_clause) = &self.where_clause {
1047                query.push_str(format!("      where: {}\n", where_clause).as_str());
1048            }
1049            if let Some(limit) = &self.limit {
1050                query.push_str(format!("      limit: {}\n", limit).as_str());
1051            }
1052            if let Some(offset) = &self.offset {
1053                query.push_str(format!("      offset: {}\n", offset).as_str());
1054            }
1055            if let Some(near_text) = &self.near_text {
1056                query.push_str(format!("      nearText: {}\n", near_text).as_str());
1057            }
1058            if let Some(near_vector) = &self.near_vector {
1059                query.push_str(format!("      nearVector: {}\n", near_vector).as_str());
1060            }
1061            if let Some(near_object) = &self.near_object {
1062                query.push_str(format!("      nearObject: {}\n", near_object).as_str());
1063            }
1064            if let Some(near_image) = &self.near_image {
1065                query.push_str(format!("      nearImage: {}\n", near_image).as_str());
1066            }
1067            if let Some(near_audio) = &self.near_audio {
1068                query.push_str(format!("      nearAudio: {}\n", near_audio).as_str());
1069            }
1070            if let Some(near_video) = &self.near_video {
1071                query.push_str(format!("      nearVideo: {}\n", near_video).as_str());
1072            }
1073            if let Some(near_thermal) = &self.near_thermal {
1074                query.push_str(format!("      nearThermal: {}\n", near_thermal).as_str());
1075            }
1076            if let Some(near_imu) = &self.near_imu {
1077                query.push_str(format!("      nearIMU: {}\n", near_imu).as_str());
1078            }
1079            if let Some(near_depth) = &self.near_depth {
1080                query.push_str(format!("      nearDepth: {}\n", near_depth).as_str());
1081            }
1082            if let Some(bm25) = &self.bm25 {
1083                query.push_str(format!("      bm25: {}\n", bm25).as_str());
1084            }
1085            if let Some(hybrid) = &self.hybrid {
1086                query.push_str(format!("      hybrid: {}\n", hybrid).as_str());
1087            }
1088            if let Some(group_by) = &self.group_by {
1089                query.push_str(format!("      group_by: {}\n", group_by).as_str());
1090            }
1091            if let Some(after) = &self.after {
1092                query.push_str(format!("      after: {}\n", after).as_str());
1093            }
1094            if let Some(tenant) = &self.tenant {
1095                query.push_str(format!("      tenant: {}\n", tenant).as_str());
1096            }
1097            if let Some(autocut) = &self.autocut {
1098                query.push_str(format!("      autocut: {}\n", autocut).as_str());
1099            }
1100
1101            if let Some(sort) = &self.sort {
1102                query.push_str(format!("      sort: {}\n", sort).as_str());
1103            }
1104            if let Some(ask) = &self.ask {
1105                query.push_str(format!("      ask: {}\n", ask).as_str());
1106            }
1107            query.push_str("    )\n");
1108        }
1109
1110        // Body
1111        query.push_str("    {\n");
1112        query.push_str(format!("      {}\n", self.properties.join(" ")).as_str());
1113
1114        if let Some(additional) = &self.additional {
1115            query.push_str("      _additional {\n");
1116            query.push_str(format!("        {}\n", additional.join(" ")).as_str());
1117            query.push_str("      }\n");
1118        }
1119        query.push_str("    }\n");
1120        query.push_str("  }\n");
1121        query.push_str("}");
1122        GetQuery { query }
1123    }
1124
1125    /// Check if the query contains a filter.
1126    fn contains_filter(&self) -> bool {
1127        match self.limit.is_some()
1128            || self.offset.is_some()
1129            || self.after.is_some()
1130            || self.autocut.is_some()
1131            || self.tenant.is_some()
1132            || self.where_clause.is_some()
1133            || self.near_text.is_some()
1134            || self.near_vector.is_some()
1135            || self.near_image.is_some()
1136            || self.near_object.is_some()
1137            || self.hybrid.is_some()
1138            || self.bm25.is_some()
1139            || self.sort.is_some()
1140            || self.ask.is_some()
1141        {
1142            true => true,
1143            false => false,
1144        }
1145    }
1146}
1147
1148#[cfg(test)]
1149mod tests {
1150    //use super::GetBuilder;
1151
1152    #[test]
1153    fn test_get_query_builder() {
1154        //let query = GetBuilder::new(
1155        //    "JeopardyQuestion",
1156        //    vec![
1157        //        "question".into(),
1158        //        "answer".into(),
1159        //        "points".into(),
1160        //        "hasCategory { ... on JeopardyCategory { title }}".into(),
1161        //    ],
1162        //)
1163        //.with_limit(1)
1164        //.with_offset(1);
1165        //println!("{}", query.build());
1166    }
1167}