Couchbase Lite Android 1.4.0:按值排序Map/Reduce并获取特定患者数据
Got it, let's break this down step by step since you're sticking with Couchbase Lite Android 1.4.0 and can't upgrade to 2.x yet. I'll cover the Map/Reduce view design, filtering logic, query implementation, and solve the value-sorting challenge.
First, we need a view that identifies valid, open visits, then maps them to their associated patients. We'll use a Map function to filter qualifying visits, and a Reduce function to aggregate patients with at least one matching visit.
Map Function
This function processes only VISIT documents, checks the validity and open status, then emits the associated patient ID as the key (I'm assuming your VISIT docs have a patientId field linking to the patient's _id; adjust if your association works the other way).
function(doc) { // Target only VISIT documents if (doc.type === 'VISIT') { // Check if the visit is active (inactive is not true, or missing) const isActive = !doc.inactive; // Check if the visit is open (endDateTime is null or missing) const isOpen = doc.endDateTime == null; if (isActive && isOpen) { // Emit patient ID as key, 1 as value for aggregation emit(doc.patientId, 1); } } }
Reduce Function
Use the built-in _count reducer to tally how many valid open visits each patient has. We only care about patients with a count ≥ 1.
_count
First, register the view in your Android code, then query it to get valid patient IDs, then fetch the corresponding patient docs with only the fields you need.
Step 1: Register the View
// Get your database instance Database db = getDatabase(); // Replace with your actual database access logic // Create or retrieve the view View activeOpenVisitsView = db.getView("active_open_visits_by_patient"); activeOpenVisitsView.setMapReduce(new MapReduce() { @Override public void map(Map<String, Object> doc, Emitter emitter) { // Translate the JS map logic to Java if ("VISIT".equals(doc.get("type"))) { Boolean inactive = (Boolean) doc.get("inactive"); boolean isActive = inactive == null || !inactive; Object endDateTime = doc.get("endDateTime"); boolean isOpen = endDateTime == null; if (isActive && isOpen) { String patientId = (String) doc.get("patientId"); if (patientId != null) { emitter.emit(patientId, 1); } } } } @Override public Object reduce(List<Object> values, boolean rereduce) { // Implement count aggregation return values.size(); } }, "1.0"); // Increment version if you update the view logic
Step 2: Get Valid Patient IDs
// Query the view with grouping to get patient-specific counts Query visitQuery = activeOpenVisitsView.createQuery(); visitQuery.setReduce(true); visitQuery.setGroupLevel(1); // Group results by patient ID Set<String> validPatientIds = new HashSet<>(); QueryEnumerator visitEnumerator = visitQuery.run(); try { while (visitEnumerator.hasNext()) { QueryRow row = visitEnumerator.next(); Integer visitCount = (Integer) row.getValue(); // Keep patients with at least one valid open visit if (visitCount >= 1) { validPatientIds.add((String) row.getKey()); } } } finally { visitEnumerator.close(); }
Step 3: Fetch Patients with Required Fields & Sort
Now query the PATIENT documents, filter to only valid IDs, project just id and name, and sort as needed.
// Build patient query Query patientQuery = db.createQuery(); patientQuery.setDocType("PATIENT"); // Filter to only patients with valid visits patientQuery.where(Expression.property("_id").in(validPatientIds)); // Project only the fields you need: _id (as "id") and name patientQuery.select(Expression.property("_id"), Expression.property("name")); // Sort by name (adjust to your desired sort field) patientQuery.orderBy(Sort.property("name").ascending()); // Execute and collect results List<Map<String, Object>> patientResults = new ArrayList<>(); QueryEnumerator patientEnumerator = patientQuery.run(); try { while (patientEnumerator.hasNext()) { QueryRow row = patientEnumerator.next(); Map<String, Object> patient = new HashMap<>(); patient.put("id", row.getKey()); patient.put("name", row.get("name")); patientResults.add(patient); } } finally { patientEnumerator.close(); } // patientResults now contains your filtered, sorted patient list
Couchbase Lite 1.4's Map/Reduce views sort results by key by default. To sort by the Reduce value (e.g., number of valid visits), you'll need to sort the results client-side after fetching them:
// Fetch all Reduce results first List<QueryRow> reduceRows = new ArrayList<>(); while (visitEnumerator.hasNext()) { reduceRows.add(visitEnumerator.next()); } // Sort by visit count (descending order) Collections.sort(reduceRows, new Comparator<QueryRow>() { @Override public int compare(QueryRow o1, QueryRow o2) { Integer count1 = (Integer) o1.getValue(); Integer count2 = (Integer) o2.getValue(); return count2.compareTo(count1); // Reverse for descending; use compareTo for ascending } }); // Now extract patient IDs from the sorted rows and fetch patients as before Set<String> sortedPatientIds = new LinkedHashSet<>(); // Preserve sort order for (QueryRow row : reduceRows) { sortedPatientIds.add((String) row.getKey()); } // Use sortedPatientIds in your patient query to maintain the order
Notes to Adjust for Your Schema
- If your
PATIENTdocs usevisitIdto link toVISITdocs (instead ofVISIThavingpatientId), swap the logic: filter validVISITdocs, emit their_idas key, then fetchPATIENTdocs wherevisitIdis in the valid visit IDs. - If
inactiveis a string (e.g.,"true"/"false") instead of a boolean, update the validity check to!"true".equals(doc.get("inactive")).
内容的提问来源于stack exchange,提问作者MHogge

