Skip to content

Commit c7ef472

Browse files
authored
Merge pull request #19 from mleduque/n1ql-query
Allow n1ql queries
2 parents 825e893 + 05951ae commit c7ef472

File tree

5 files changed

+138
-4
lines changed

5 files changed

+138
-4
lines changed
Submodule couchbase-lite-core updated 61 files

couchbase-lite/src/fl_slice.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@ impl AsFlSlice for FlSliceOwner {
8282
}
8383
}
8484

85+
impl AsFlSlice for FLSliceResult {
86+
fn as_flslice(&self) -> FLSlice {
87+
FLSlice {
88+
buf: self.buf,
89+
size: self.size,
90+
}
91+
}
92+
}
93+
8594
#[inline]
8695
pub(crate) unsafe fn fl_slice_to_str_unchecked<'a>(s: FLSlice) -> &'a str {
8796
let bytes: &[u8] = slice::from_raw_parts(s.buf as *const u8, s.size);

couchbase-lite/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,10 @@ impl Database {
204204
pub fn query(&self, query_json: &str) -> Result<Query> {
205205
Query::new(self, query_json)
206206
}
207+
/// Compiles a query from an expression given as N1QL.
208+
pub fn n1ql_query(&self, query: &str) -> Result<Query> {
209+
Query::new_n1ql(self, query)
210+
}
207211

208212
/// Creates an enumerator ordered by docID.
209213
pub fn enumerate_all_docs(&self, flags: DocEnumeratorFlags) -> Result<DocEnumerator> {

couchbase-lite/src/query.rs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
use crate::{
22
error::{c4error_init, Error},
33
ffi::{
4-
c4query_free, c4query_new, c4query_run, c4queryenum_free, c4queryenum_next,
5-
kC4DefaultQueryOptions, C4Query, C4QueryEnumerator, FLArrayIterator_GetCount,
6-
FLArrayIterator_GetValueAt,
4+
c4query_free, c4query_new, c4query_new2, c4query_run, c4query_setParameters,
5+
c4queryenum_free, c4queryenum_next, kC4DefaultQueryOptions, kC4N1QLQuery, C4Query,
6+
C4QueryEnumerator, FLArrayIterator_GetCount, FLArrayIterator_GetValueAt,
77
},
88
fl_slice::{fl_slice_empty, AsFlSlice},
99
value::{FromValueRef, ValueRef},
1010
Database, Result,
1111
};
1212
use fallible_streaming_iterator::FallibleStreamingIterator;
13+
use serde::Serialize;
1314
use std::ptr::NonNull;
1415

1516
pub struct Query<'db> {
@@ -39,6 +40,36 @@ impl Query<'_> {
3940
.ok_or_else(|| c4err.into())
4041
}
4142

43+
pub(crate) fn new_n1ql<'a, 'b>(db: &'a Database, query_n1ql: &'b str) -> Result<Query<'a>> {
44+
let mut c4err = c4error_init();
45+
let mut out_error_pos: std::os::raw::c_int = -1;
46+
let query = unsafe {
47+
c4query_new2(
48+
db.inner.0.as_ptr(),
49+
kC4N1QLQuery,
50+
query_n1ql.as_bytes().as_flslice(),
51+
&mut out_error_pos,
52+
&mut c4err,
53+
)
54+
};
55+
56+
NonNull::new(query)
57+
.map(|inner| Query { _db: db, inner })
58+
.ok_or_else(|| c4err.into())
59+
}
60+
61+
pub fn set_parameters<T>(&self, parameters: &T) -> Result<()>
62+
where
63+
T: Serialize,
64+
{
65+
let param_string = serde_json::to_string(parameters)?;
66+
let param_slice = param_string.as_bytes().as_flslice();
67+
unsafe {
68+
c4query_setParameters(self.inner.as_ptr(), param_slice);
69+
}
70+
Ok(())
71+
}
72+
4273
pub fn run(&self) -> Result<Enumerator> {
4374
let mut c4err = c4error_init();
4475
let it = unsafe {

couchbase-lite/tests/smoke_tests.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,3 +499,93 @@ fn test_like_performance() {
499499
}
500500
tmp_dir.close().expect("Can not close tmp_dir");
501501
}
502+
503+
#[test]
504+
fn test_n1ql_query() {
505+
let _ = env_logger::try_init();
506+
let tmp_dir = tempdir().expect("Can not create tmp directory");
507+
println!("we create tempdir at {}", tmp_dir.path().display());
508+
let db_path = tmp_dir.path().join("a.cblite2");
509+
{
510+
let mut db = Database::open(&db_path, DatabaseConfig::default()).unwrap();
511+
let mut trans = db.transaction().unwrap();
512+
for i in 0..10_000 {
513+
let foo = Foo {
514+
i,
515+
s: format!("Hello {}", i),
516+
};
517+
let mut doc = Document::new(&foo).unwrap();
518+
trans.save(&mut doc).unwrap();
519+
}
520+
trans.commit().unwrap();
521+
522+
let query = db.n1ql_query("SELECT s WHERE s LIKE '%555'").unwrap();
523+
let expected = vec![
524+
"Hello 1555",
525+
"Hello 2555",
526+
"Hello 3555",
527+
"Hello 4555",
528+
"Hello 555",
529+
"Hello 5555",
530+
"Hello 6555",
531+
"Hello 7555",
532+
"Hello 8555",
533+
"Hello 9555",
534+
];
535+
536+
let mut iter = query.run().unwrap();
537+
let mut query_ret = Vec::with_capacity(10);
538+
while let Some(item) = iter.next().unwrap() {
539+
let val = item.get_raw_checked(0).unwrap();
540+
let val = val.as_str().unwrap();
541+
query_ret.push(val.to_string());
542+
}
543+
query_ret.sort();
544+
545+
assert_eq!(expected, query_ret);
546+
}
547+
tmp_dir.close().expect("Can not close tmp_dir");
548+
}
549+
550+
#[test]
551+
fn test_n1ql_query_with_parameter() {
552+
let _ = env_logger::try_init();
553+
let tmp_dir = tempdir().expect("Can not create tmp directory");
554+
println!("we create tempdir at {}", tmp_dir.path().display());
555+
let db_path = tmp_dir.path().join("a.cblite2");
556+
{
557+
let mut db = Database::open(&db_path, DatabaseConfig::default()).unwrap();
558+
let mut trans = db.transaction().unwrap();
559+
for i in 0..10_000 {
560+
let foo = Foo {
561+
i,
562+
s: format!("Hello {}", i),
563+
};
564+
let mut doc = Document::new(&foo).unwrap();
565+
trans.save(&mut doc).unwrap();
566+
}
567+
trans.commit().unwrap();
568+
569+
let query = db
570+
.n1ql_query("SELECT s WHERE s LIKE $pattern ORDER BY s LIMIT 2 OFFSET 1")
571+
.unwrap();
572+
query
573+
.set_parameters(&serde_json::json!({
574+
"pattern": "%555",
575+
}))
576+
.unwrap();
577+
let expected = vec!["Hello 2555", "Hello 3555"];
578+
579+
let mut iter = query.run().unwrap();
580+
let mut query_ret = Vec::with_capacity(10);
581+
while let Some(item) = iter.next().unwrap() {
582+
let val = item.get_raw_checked(0).unwrap();
583+
let val = val.as_str().unwrap();
584+
query_ret.push(val.to_string());
585+
}
586+
query_ret.sort();
587+
588+
assert_eq!(expected, query_ret);
589+
}
590+
tmp_dir.close().expect("Can not close tmp_dir");
591+
}

0 commit comments

Comments
 (0)