Querying Fields (Rust)¶
Learn to use MRRC's Query DSL for complex field searching.
Basic Field Access¶
use mrrc::MarcReader;
use std::fs::File;
fn main() -> mrrc::Result<()> {
let file = File::open("records.mrc")?;
let mut reader = MarcReader::new(file);
while let Some(record) = reader.read_record()? {
// Get all fields with a tag
for field in record.fields_by_tag("650") {
if let Some(subject) = field.get_subfield('a') {
println!("{}", subject);
}
}
}
Ok(())
}
FieldQuery (Tag Matching)¶
use mrrc::FieldQuery;
// Find fields by tag
let query = FieldQuery::new().tag("245");
for field in record.fields_matching(&query) {
println!("{:?}", field.get_subfield('a'));
}
Tag Range Query¶
Find fields within a tag range:
use mrrc::FieldQuery;
// Find all subject fields (600-699)
let query = FieldQuery::new().tag_range("600", "699");
for field in record.fields_matching_range(&query) {
println!("{}: {:?}", field.tag, field.get_subfield('a'));
}
Filtering by Indicators¶
// Manual filtering by indicators
let lcsh_subjects: Vec<_> = record
.fields_by_tag("650")
.filter(|f| f.indicator2 == '0') // LCSH
.collect();
for field in lcsh_subjects {
if let Some(subject) = field.get_subfield('a') {
println!("LCSH: {}", subject);
}
}
Subfield Pattern Query¶
SubfieldPatternQuery matches a subfield against a regular expression, so you
don't have to wire up regex yourself. Construction returns a Result (the
error is a regex::Error) because the pattern is compiled up front:
use mrrc::SubfieldPatternQuery;
// Find ISBN-13s: the $a of an 020 field starting with 978 or 979
let query = SubfieldPatternQuery::new("020", 'a', r"^97[89]")
.expect("valid regex");
for field in record.fields_matching_pattern(&query) {
if let Some(isbn) = field.get_subfield('a') {
println!("ISBN-13: {}", isbn);
}
}
Subfield Value Query¶
SubfieldValueQuery matches a subfield's value directly. Use new for an exact
match and partial for a substring (both case-sensitive):
use mrrc::SubfieldValueQuery;
// Exact match: 650 $a equal to "History"
let exact = SubfieldValueQuery::new("650", 'a', "History");
for field in record.fields_matching_value(&exact) {
println!("Exact: {:?}", field.get_subfield('a'));
}
// Partial match: 650 $a containing "History"
let partial = SubfieldValueQuery::partial("650", 'a', "History");
for field in record.fields_matching_value(&partial) {
println!("Partial: {:?}", field.get_subfield('a'));
}
To test a single field instead of scanning the whole record, call
query.matches(&field), which returns a bool.
Combining Queries¶
use mrrc::Record;
fn find_lcsh_subjects_with_subdivision(record: &Record) -> Vec<String> {
let mut results = Vec::new();
for field in record.fields_by_tag("650") {
// Must be LCSH (indicator2 = 0)
if field.indicator2 != '0' {
continue;
}
// Must have both $a and $x
let main_subject = field.get_subfield('a');
let subdivision = field.get_subfield('x');
if let (Some(main), Some(sub)) = (main_subject, subdivision) {
results.push(format!("{} -- {}", main, sub));
}
}
results
}
Complete Example¶
use mrrc::{MarcReader, Record, RecordHelpers};
use std::fs::File;
fn find_records_about(path: &str, topic: &str) -> mrrc::Result<Vec<String>> {
let file = File::open(path)?;
let mut reader = MarcReader::new(file);
let mut results = Vec::new();
let topic_lower = topic.to_lowercase();
while let Some(record) = reader.read_record()? {
// Check LCSH subjects
for field in record.fields_by_tag("650") {
if field.indicator2 != '0' {
continue;
}
if let Some(subject) = field.get_subfield('a') {
if subject.to_lowercase().contains(&topic_lower) {
if let Some(title) = record.title() {
results.push(format!("{}: {}", title, subject));
}
break;
}
}
}
}
Ok(results)
}
fn main() -> mrrc::Result<()> {
let results = find_records_about("library.mrc", "computers")?;
for result in results {
println!("{}", result);
}
Ok(())
}
Performance Tips¶
- Use specific tags when possible (faster than ranges)
- Filter by tag first, then by indicator, then by content
- For large datasets, consider parallel processing with Rayon
Next Steps¶
- Reading Records - Basic record access
- Concurrency - Parallel processing
- Rust API Reference - Full API documentation