0

我有以下文档

{
  "_id": "products:info",
  "_rev": "3-47add0ce8ecbcb2c4c7dfefae83c02cd",
  "created": "01-26-2022",
  "products": [
    {
      "brand": "MSI",
      "title": "Summit e16",
      "sku": "1000121"
    },
    {
      "brand": "MSI",
      "title": "Summit e13",
      "sku": "1000120"
    }
  ]
}

我想按 desc 顺序products按属性对数组进行排序sku并获得第一个产品 sku。

到目前为止,我尝试了以下操作。

指数

{
 "type": "json",
 "partitioned": true,
 "def": {
  "fields": [
   {
    "products[].sku": "asc"
   }
  ]
 }
}

询问

{
   "selector": {
      "products": {
         "$elemMatch": {
            "sku": {
               "$ne": null
            }
         }
      }
   },
   "fields": [
      "_id",
      "products.0.sku"
   ],
   "sort": [
      {
         "products.sku": "desc"
      }
   ]
}

投掷错误

Error running query. Reason: (no_usable_index) No global index exists for this sort, try indexing by the sort fields.

也尝试了以下

{"products.sku": "desc"}
{"products.[].sku": "desc"}
4

1 回答 1

1

如评论

'sort' 根据排序字段对文档(满足选择器)进行排序。但是只有一个文档,所以没有什么可以排序的。 排序不是数组排序

因此,使用选择器无法达到预期的结果_find。既然如此,我将提供一个建议和替代方案。

首先,将所有产品放在一个文件中似乎是个坏主意 - 非规范化变得疯狂?如果这是一个真实世界的应用程序,请注意这$elemMatch是一个内存操作,并且可能会导致非常大的产品列表的结果不佳。

解决方案很容易通过视图来实现。考虑这个map函数

function (doc) {
  if(doc._id === "products:info" && doc.products) {    
    doc.products.forEach((product) => { 
      emit(product.sku,{brand: product.brand, title: product.title}); 
    });
  }
}

给定来自 OP 的示例文档,视图索引看起来像这样

ID 钥匙 价值
产品:信息 1000120 {“品牌”:“MSI”,“标题”:“Summit e13”}
产品:信息 1000121 {“品牌”:“MSI”,“标题”:“Summit e16”}

可能还有其他要求,但是这个视图涵盖了很多

  • 按降序或升序获取skus;使用限制来获取第一个/最后一个/块的 sku
  • 利用该value字段无需加载实际文档以获取特定信息
  • Reduce =_count免费提供一些关于(非)空 sku 的好信息

在下面的代码片段中,产品列表是

"products": [{
    "brand": "MSI",
    "title": "Summit e16",
    "sku": "1000121"
  },
  {
    "brand": "Asus",
    "title": "L510",
    "sku": null
  },
  {
    "brand": "MSI",
    "title": "Summit e13",
    "sku": "1000120"
  },
  {
    "brand": "Asus",
    "title": "VivoBook 15 K513",
    "sku": null
  },
  {
    "brand": "MSI",
    "title": "GT72S Dominator Pro",
    "sku": "1000122"
  },
]

结果视图索引看起来像这样

ID 钥匙 价值
产品:信息 无效的 {“品牌”:“华硕”,“标题”:“L510”}
产品:信息 无效的 {“品牌”:“华硕”,“标题”:“VivoBook 15 K513”}
产品:信息 1000120 {“品牌”:“MSI”,“标题”:“Summit e13”}
产品:信息 1000121 {“品牌”:“MSI”,“标题”:“Summit e16”}
产品:信息 1000122 {“品牌”:“MSI”,“标题”:“GT72S Dominator Pro”}

执行视图时片段的默认状态解决了 OP 要求。

const getDocsToInstall = () => {
  return [{
      "_id": "_design/products:info",
      "views": {
        "skus": {
          "reduce": "_count",
          "map": "function (doc) {\n  if(doc._id === \"products:info\" && doc.products) {\n    doc.products.forEach((product) => { emit(product.sku,{brand: product.brand, title: product.title}); });\n  }\n}"
        }
      }
    },
    {
      "_id": "products:info",
      "created": "01-26-2022",
      "products": [{
          "brand": "MSI",
          "title": "Summit e16",
          "sku": "1000121"
        },
        {
          "brand": "Asus",
          "title": "L510",
          "sku": null
        },
        {
          "brand": "MSI",
          "title": "Summit e13",
          "sku": "1000120"
        },
        {
          "brand": "Asus",
          "title": "VivoBook 15 K513",
          "sku": null
        },
        {
          "brand": "MSI",
          "title": "GT72S Dominator Pro",
          "sku": "1000122"
        },
      ]
    }
  ]
}

let db;
const gel = (id) => document.getElementById(id);

const initDb = async() => {
  db = new PouchDB('test', {
    adapter: 'memory'
  });
  await db.bulkDocs(getDocsToInstall());
}

async function query(params) {
  console.info(params)
  const results = await db.query("products:info/skus", params);
  gel('results').innerText = JSON.stringify(results, undefined, 2);
}

async function reduce(opt) {
  const params = {
    reduce: true
  }
  if (opt === null) {
    params.startkey = null;
    params.endkey = "0";
  } else if (opt === undefined) {
    params.startkey = "0"
  }
  return query(params);
}

async function view() {
  // sanity
  if (!parseInt(gel('limit').value)) {
    gel('limit').value = 1
  }
  const params = {
    reduce: false,
    include_docs: gel('include_docs').checked,
    limit: parseInt(gel('limit').value),
    descending: gel('descending').checked
  }
  // adjust keys
  if (gel('null_skus').checked) {
    params.startkey = params.descending ? "0" : null;
    params.endkey = params.descending ? null : "0";
  } else if (params.descending) {
    params.endkey = null;
  } else {
    params.startkey = "0";
  }


  return query(params);
}

(async() => {
  await initDb();
  gel('controls').classList.remove('hide');
})();
.hide {
  display: none
}

.vgap {
  margin-top: 1em;
}
<script src="https://cdn.jsdelivr.net/npm/pouchdb@7.1.1/dist/pouchdb.min.js"></script>
<script src="https://github.com/pouchdb/pouchdb/releases/download/7.1.1/pouchdb.memory.min.js"></script>
<pre>
<h3>Map/Reduce Alternative</h3>
</pre>
<div id="controls" class="hide">
  <label for="limit">Limit&nbsp;</label><input id="limit" type="number" min="1" value="1" /><br/>
  <input id="descending" type="checkbox" class='vgap' /><label for="descending">&nbsp;Descending</label>
  <input id="include_docs" type="checkbox" /><label for="descending">&nbsp;Include Docs</label>
  <input id="null_skus" type="checkbox" /><label for="null_skus">&nbsp;Null skus</label>
  <br/>
  <button onclick="view()" class="vgap">Execute View</button>
  <hr/>
  <button onclick="reduce('product')">Reduce: Product count</button>
  <button onclick="reduce(null)">Reduce: Null sku count</button>
  <button onclick="reduce()">Reduce: Non-null sku count</button>
  <hr/>
  <pre>
Results
</pre>
  <hr/>
  <pre id="results"></pre>

免责声明

我会通过将每个产品移动到自己的文档中来规范产品。
于 2022-01-28T15:46:33.057 回答