diff --git a/src/query/query_parser/query_parser.rs b/src/query/query_parser/query_parser.rs index 6fcd9df68..632d9f692 100644 --- a/src/query/query_parser/query_parser.rs +++ b/src/query/query_parser/query_parser.rs @@ -404,9 +404,10 @@ impl QueryParser { let bytes = base64::decode(phrase).map_err(QueryParserError::ExpectedBase64)?; Ok(Term::from_field_bytes(field, &bytes)) } - FieldType::IpAddr(_) => Err(QueryParserError::UnsupportedQuery( - "Range query are not supported on ip field.".to_string(), - )), + FieldType::IpAddr(_) => { + let ip_v6 = IpAddr::from_str(phrase)?.into_ipv6_addr(); + Ok(Term::from_field_ip_addr(field, ip_v6)) + } } } diff --git a/src/query/range_query.rs b/src/query/range_query.rs index 0340eca79..e198618c8 100644 --- a/src/query/range_query.rs +++ b/src/query/range_query.rs @@ -328,13 +328,15 @@ impl Weight for RangeWeight { #[cfg(test)] mod tests { + use std::net::IpAddr; use std::ops::Bound; + use std::str::FromStr; use super::RangeQuery; use crate::collector::{Count, TopDocs}; use crate::query::QueryParser; - use crate::schema::{Document, Field, Schema, INDEXED, TEXT}; - use crate::Index; + use crate::schema::{Document, Field, IntoIpv6Addr, Schema, INDEXED, STORED, TEXT}; + use crate::{doc, Index}; #[test] fn test_range_query_simple() -> crate::Result<()> { @@ -506,4 +508,69 @@ mod tests { assert_eq!(top_docs.len(), 1); Ok(()) } + + #[test] + fn search_ip_range_test() { + let mut schema_builder = Schema::builder(); + let ip_field = schema_builder.add_ip_addr_field("ip", INDEXED | STORED); + let schema = schema_builder.build(); + let index = Index::create_in_ram(schema); + let ip_addr_1 = IpAddr::from_str("127.0.0.10").unwrap().into_ipv6_addr(); + let ip_addr_2 = IpAddr::from_str("127.0.0.20").unwrap().into_ipv6_addr(); + + { + let mut index_writer = index.writer(3_000_000).unwrap(); + index_writer + .add_document(doc!( + ip_field => ip_addr_1 + )) + .unwrap(); + index_writer + .add_document(doc!( + ip_field => ip_addr_2 + )) + .unwrap(); + + index_writer.commit().unwrap(); + } + let reader = index.reader().unwrap(); + let searcher = reader.searcher(); + + let get_num_hits = |query| { + let (_top_docs, count) = searcher + .search(&query, &(TopDocs::with_limit(10), Count)) + .unwrap(); + count + }; + let query_from_text = |text: &str| { + QueryParser::for_index(&index, vec![ip_field]) + .parse_query(text) + .unwrap() + }; + + assert_eq!( + get_num_hits(query_from_text("ip:[127.0.0.1 TO 127.0.0.20]")), + 2 + ); + + assert_eq!( + get_num_hits(query_from_text("ip:[127.0.0.10 TO 127.0.0.20]")), + 2 + ); + + assert_eq!( + get_num_hits(query_from_text("ip:[127.0.0.11 TO 127.0.0.20]")), + 1 + ); + + assert_eq!( + get_num_hits(query_from_text("ip:[127.0.0.11 TO 127.0.0.19]")), + 0 + ); + + assert_eq!(get_num_hits(query_from_text("ip:[127.0.0.11 TO *]")), 1); + assert_eq!(get_num_hits(query_from_text("ip:[127.0.0.21 TO *]")), 0); + assert_eq!(get_num_hits(query_from_text("ip:[* TO 127.0.0.9]")), 0); + assert_eq!(get_num_hits(query_from_text("ip:[* TO 127.0.0.10]")), 1); + } }