# als-mongo-list

`als-mongo-list` is a Node.js library designed to help you quickly build search, filter, sort, and pagination logic for Mongoose-based MongoDB queries. It simplifies building complex query interfaces by providing a structured way to:

- Filter documents by various fields, including support for ranges, enums, dates, booleans, ObjectIds, and array fields.
- Search text fields using regular expressions with enhanced configuration options.
- Sort by specified keys, including strings, numbers, booleans, and dates.
- Paginate results efficiently, including total count and navigation controls.
- Dynamically generate input fields for building UI filters and sorting selectors.

## ChangeLog for Version 2.2.0

**Added Features:**

1. Boolean Fields Input Update:
    - Boolean fields now use a `<select>` input instead of a `<input type="checkbox">`.
    - Added an additional option "all" to include both true and false results (by default).
2. Improved Search Logic:
    - If only one searchable field is configured, the search applies directly to that field.
    - If multiple searchable fields are configured:
      - A search input (type="search") is generated for the query text.
      - Corresponding checkbox inputs for each field are provided to enable or disable specific fields in the search query.
3. Array Field Filtering: 
    - Added support for filtering array fields within documents.
    - The library now generates filters for array fields, allowing regex-based or equality-based filtering.
4. For `searchIn`,`orderBy`,`populate`,`select` if key not in model's tree, console.warn will warn for it

## Installation

Install via npm:

```bash
npm install als-mongo-list
```

## Usage

First, define your Mongoose model and schema as usual:

```js
const mongoose = require("mongoose");

const userSchema = new mongoose.Schema({
  name: { type: String },
  age: { type: Number, min: 18, max: 99 },
  isActive: { type: Boolean },
  userId: { type: mongoose.Types.ObjectId },
  createdAt: { type: Date, default: Date.now },
  status: { type: String, enum: ["active", "inactive", "pending"] },
  tags: [{ type: String }], // Example array field
});

const UserModel = mongoose.model("User", userSchema);
```

Then, initialize `als-mongo-list` with your model:

```js
const List = require("als-mongo-list");

const list = new List(UserModel)
  .searchIn("name", "tags") // Fields to include in search/filter.
  .orderBy("age") // Fields allowed for sorting.
  .populate("userId") // Fields to populate.
  .select("name", "age", "status"); // Fields to select.
```

### Executing Queries

Once configured, you can execute queries by passing in query parameters and, optionally, an initial `search` object. Query parameters may include filters (e.g., `age_gte`, `age_lte`), sorting (`orderBy`), pagination (`currentPage`, `pageSize`), and full-text search (`search`).

**Example:**

```js
(async () => {
  const query = {
    search: "John",
    age_gte: "25",
    orderBy: "age",
    currentPage: "0",
    pageSize: "10",
  };

  const result = await list.exec(query, { userId: "someuserid" });

  if (result.error) {
    console.error("Query error:", result.error);
  } else {
    console.log(result.items); // Paginated array of documents
    console.log(result.total); // Total number of documents matching the filters
    console.log(result.inputs); // Input definitions for building a UI
    console.log(result.query); // Query parameters (with normalized pagination)
  }
})();
```

### Updated Features

#### Boolean Fields

- **New Input Type:** `Boolean` fields now use a `<select>` with the following options:
  - `"all"`: No filtering (default).
  - `"true"`: Includes documents where the field is `true`.
  - `"false"`: Includes documents where the field is `false`.

**Example Input Definition:**

```json
{
  "isActive": {
    "tag": "select",
    "name": "isActive",
    "options": [
      { "value": "", "text": "all" },
      { "value": "true", "text": "true" },
      { "value": "false", "text": "false" }
    ]
  }
}
```

---

#### Enhanced Search Logic

1. **Single Search Field:**

   - If only one field is included in the search configuration, the query applies directly to that field without `$or`.

2. **Multiple Search Fields:**
   - If multiple fields are configured, a `search` input is generated alongside checkboxes for each field. Users can enable or disable specific fields for inclusion in the search query.

**Example Input Definition:**

```json
{
  "search": {
    "tag": "input",
    "type": "search",
    "name": "search",
    "keys": [
      { "tag": "input", "type": "checkbox", "name": "name", "checked": true },
      { "tag": "input", "type": "checkbox", "name": "tags", "checked": true }
    ]
  }
}
```

---

#### Array Field Filtering

- Array fields are now supported for filtering. You can search for documents where any element in the array matches a given value (or regex).

**Example:**
Given a schema with an array field:

```js
tags: [{ type: String }];
```

You can perform a search:

```js
const query = { tags: "technology" };
const result = await list.exec(query);
```

The library will filter documents where the `tags` array contains `"technology"`.

---

### Dynamic Input Definitions

`als-mongo-list` generates metadata (`inputs`) about possible filters and sorts. These can be used to build dynamic UIs. For example, `result.inputs.filter` and `result.inputs.sort` provide configuration for creating input elements (like `<input>`, `<select>`, etc.) that match the filter and sort capabilities of the current configuration.

**Updated Example:** Boolean, search, and array fields are now supported dynamically:

```js
// Suppose `list.exec(query)` returns `result.inputs` as follows:
console.log(result.inputs);
/*
{
  filter: {
    search: { 
      tag: 'input', 
      type: 'search', 
      name: 'search', 
      keys: [
        { tag: 'input', type: 'checkbox', name: 'name', checked: true },
        { tag: 'input', type: 'checkbox', name: 'tags', checked: true }
      ]
    },
    isActive: { 
      tag: 'select', 
      name: 'isActive', 
      options: [
        { value: "", text: "all" },
        { value: "true", text: "true" },
        { value: "false", text: "false" }
      ]
    },
    tags: { tag: 'input', type: 'search', name: 'tags' },
    age_gte: { tag: 'input', type: 'number', name: 'age_gte', min: 18, max: 99 },
    age_lte: { tag: 'input', type: 'number', name: 'age_lte', min: 18, max: 99 },
  },
  sort: {
    tag: 'select',
    name: 'orderBy',
    options: [
      { value: '-age', text: 'small_big', key: 'age' },
      { value: 'age', text: 'big_small', key: 'age' },
      // ... other sort options
    ]
  },
  pagination: { /* pagination info from als-pagination *-/ }
}
*/
```
