gpt4 book ai didi

java - 如何使用 Java 查询和过滤多个嵌套数组

转载 作者:可可西里 更新时间:2023-11-01 10:28:02 35 4
gpt4 key购买 nike

我的目标是返回多个 questionElements,其中 questionElements 元标记条目等于我的搜索。例如。如果 metaTag 元素等于我的字符串,则返回它的父 questionEntry 元素并搜索嵌套在 show 中的所有元素。

所以我想要的是匹配包含所需“metaTags”值的文档,并“过滤”任何不包含此内部匹配项的子文档数组

这是我尝试使用 $redact 进行的聚合查询,但它没有给出我想要的结果:

 db.mongoColl.aggregate([{"$redact":{"$cond": { if: {$gt:[ {"$size": {
$setIntersection : [ { "$ifNull": [ "$metaTags", []]},
["MySearchString"]]} } , 0 ]} , then:"$$PRUNE",
else:"$$DESCEND" }}}]).pretty();

我的实例是:

private DB mongoDatabase;
private DBCollection mongoColl;
private DBObject dbObject;

// Singleton class
// Create client (server address(host,port), credential, options)
mongoClient = new MongoClient(new ServerAddress(host, port),
Collections.singletonList(credential),
options);

mongoDatabase = ClientSingleton.getInstance().getClient().getDB("MyDB");

我在数据库中要匹配的文档是:

{
"show":[
{
"season":[
{
"episodes":[
{
"questionEntry":{
"id":1,
"info":{
"seasonNumber":1,
"episodeNumber":5,
"episodeName":"A Hero Sits Next Door"
},
"questionItem":{
"theQuestion":"What is the name of the ringer hired by Mr. Weed?",
"attachedElement":{
"type":1,
"value":""
}
},
"options":[
{
"type":1,
"value":"Johnson"
},
{
"type":1,
"value":"Hideo"
},
{
"type":1,
"value":"Guillermo"
}
],
"answer":{
"questionId":1,
"answer":3
},
"metaTags":[
"Season 1",
"Episode 5",
"Trivia",
"Arya Stark",
"House Stark"
]
}
}
]
}
]
}
]
}

但是,如果文档中的任何数组不包含要匹配的“metaTags”值,即“Arya Stark”,那么我根本不希望结果中匹配该数组的任何元素。 “元标签”可以保持原样。

我正在运行最新的 java 驱动程序并在 Eclipse 中使用 java SE1.7 编译器,如果这对响应有任何影响的话。

最佳答案

$redact运算符真的不是这里的最佳选择,或者逻辑很简单,是导致尝试的查询不起作用的主要原因。 “编辑”选项几乎是针对单个特定条件的“全有或全无”过程,该条件可用于 $$DESCEND,从而遍历文档的级别。

充其量,通过在编码条件中的字段不存在的地方转置一个值,您会得到很多“误报”。在最坏的情况下,您最终会删除整个文档,相反它可能是一个匹配项。它有它的用途,但这并不是其中之一。

首先是一个基于您的结构的简化示例。这主要是为了能够从内容中可视化我们要过滤的东西:

{
"show": [
{
"name": "Game of Thrones",
"season": [
{
"_id": 1,
"episodes": [
{
"_id": 1,
"metaTags": [
"Arya Stark"
]
},
{
"_id": 2,
"metaTags": [
"John Snow"
]
}
]
},
{
"_id": 2,
"episodes": [
{
"_id": 1,
"metaTags": [
"Arya Stark"
]
}
]
}
]
},
{
"name": "Seinfeld",
"season": [
{
"_id": 1,
"episodes": [
{
"_id": 1,
"metaTags": [
"Jerry Seinfeld"
]
}
]
}
]
}
]
}

这里有两种获取结果的方法。首先是使用 $unwind 的传统方法为了使用数组,然后使用 $match 对其进行过滤和条件表达式,当然有几个阶段 $group重建数组的操作:

db.sample.aggregate([
{ "$match": {
"show.season.episodes.metaTags": "Arya Stark"
}},
{ "$unwind": "$show" },
{ "$unwind": "$show.season" },
{ "$unwind": "$show.season.episodes" },
{ "$unwind": "$show.season.episodes.metaTags" },
{ "$group": {
"_id": {
"_id": "$_id",
"show": {
"name": "$show.name",
"season": {
"_id": "$show.season._id",
"episodes": {
"_id": "$show.season.episodes._id",
}
}
}
},
"metaTags": { "$push": "$show.season.episodes.metaTags" },
"matched": {
"$sum": {
"$cond": [
{ "$eq": [ "$show.season.episodes.metaTags", "Arya Stark" ] },
1,
0
]
}
}
}},
{ "$sort": { "_id._id": 1, "_id.show.season.episodes._id": 1 } },
{ "$group": {
"_id": {
"_id": "$_id._id",
"show": {
"name": "$_id.show.name",
"season": {
"_id": "$_id.show.season._id",
},
}
},
"episodes": {
"$push": {
"$cond": [
{ "$gt": [ "$matched", 0 ] },
{
"_id": "$_id.show.season.episodes._id",
"metaTags": "$metaTags"
},
false
]
}
}
}},
{ "$unwind": "$episodes" },
{ "$match": { "episodes": { "$ne": false } } },
{ "$group": {
"_id": "$_id",
"episodes": { "$push": "$episodes" }
}},
{ "$sort": { "_id._id": 1, "_id.show.season._id": 1 } },
{ "$group": {
"_id": {
"_id": "$_id._id",
"show": {
"name": "$_id.show.name"
}
},
"season": {
"$push": {
"_id": "$_id.show.season._id",
"episodes": "$episodes"
}
}
}},
{ "$group": {
"_id": "$_id._id",
"show": {
"$push": {
"name": "$_id.show.name",
"season": "$season"
}
}
}}
])

这一切都很好,而且很容易理解。然而,在这里使用 $unwind 的过程会产生很多开销,尤其是当我们只讨论在文档本身内进行过滤而不是跨文档进行任何分组时。

对此有一种现代方法,但请注意,虽然高效,但它绝对是一个“怪物”,在处理嵌入式数组时很容易迷失在逻辑中:

db.sample.aggregate([
{ "$match": {
"show.season.episodes.metaTags": "Arya Stark"
}},
{ "$project": {
"show": {
"$setDifference": [
{ "$map": {
"input": "$show",
"as": "show",
"in": {
"$let": {
"vars": {
"season": {
"$setDifference": [
{ "$map": {
"input": "$$show.season",
"as": "season",
"in": {
"$let": {
"vars": {
"episodes": {
"$setDifference": [
{ "$map": {
"input": "$$season.episodes",
"as": "episode",
"in": {
"$cond": [
{ "$setIsSubset": [
"$$episode.metaTags",
["Arya Stark"]
]},
"$$episode",
false
]
}
}},
[false]
]
}
},
"in": {
"$cond": [
{ "$ne": [ "$$episodes", [] ] },
{
"_id": "$$season._id",
"episodes": "$$episodes"
},
false
]
}
}
}
}},
[false]
]
}
},
"in": {
"$cond": [
{ "$ne": ["$$season", [] ] },
{
"name": "$$show.name",
"season": "$$season"
},
false
]
}
}
}
}},
[false]
]
}
}}
])

里面有很多数组处理 $map每个级别以及带有 $let 的变量声明对于每个数组,因为我们都通过 $setDifference 来“过滤”内容并测试空数组。

使用单个管道 $project在初始查询匹配之后,这比之前的过程快得多。

两者产生相同的过滤结果:

{
"_id" : ObjectId("55b3455e64518e494632fa16"),
"show" : [
{
"name" : "Game of Thrones",
"season" : [
{
"_id" : 1,
"episodes" : [
{
"_id" : 1,
"metaTags" : [
"Arya Stark"
]
}
]
},
{
"_id" : 2,
"episodes" : [
{
"_id" : 1,
"metaTags" : [
"Arya Stark"
]
}
]
}
]
}
]
}

所有“show”、“season”和“episodes”数组完全过滤掉与内部“metaTags”条件不匹配的任何文档。 “metaTags”数组本身未被触及,仅通过 $setIsSubset 进行匹配测试。 ,实际上只有这样才能过滤不匹配的“剧集”数组内容。

将此转换为 Java 驱动程序的使用是一个相当简单的过程,因为这只是对象和列表的数据结构表示。同样,您只需使用标准列表和 BSON Document 在 Java 中构建相同的结构。对象。但它基本上都是列表和映射语法:

MongoDatabase db = mongoClient.getDatabase("test");

MongoCollection<Document> collection = db.getCollection("sample");

String searchString = new String("Arya Stark");

List<Document> pipeline = Arrays.<Document>asList(
new Document("$match",
new Document("show.season.episodes.metaTags",searchString)
),
new Document("$project",
new Document("show",
new Document("$setDifference",
Arrays.<Object>asList(
new Document("$map",
new Document("input","$show")
.append("as","show")
.append("in",
new Document("$let",
new Document("vars",
new Document("season",
new Document("$setDifference",
Arrays.<Object>asList(
new Document("$map",
new Document("input","$$show.season")
.append("as","season")
.append("in",
new Document("$let",
new Document("vars",
new Document("episodes",
new Document("$setDifference",
Arrays.<Object>asList(
new Document("$map",
new Document("input","$$season.episodes")
.append("as","episode")
.append("in",
new Document("$cond",
Arrays.<Object>asList(
new Document("$setIsSubset",
Arrays.<Object>asList(
"$$episode.metaTags",
Arrays.<Object>asList(searchString)
)
),
"$$episode",
false
)
)
)
),
Arrays.<Object>asList(false)
)
)
)
)
.append("in",
new Document("$cond",
Arrays.<Object>asList(
new Document("$ne",
Arrays.<Object>asList(
"$$episodes",
Arrays.<Object>asList()
)
),
new Document("_id","$$season._id")
.append("episodes","$$episodes"),
false
)
)
)
)
)
),
Arrays.<Object>asList(false)
)
)
)
)
.append("in",
new Document("$cond",
Arrays.<Object>asList(
new Document("$ne",
Arrays.<Object>asList(
"$$season",
Arrays.<Object>asList()
)
),
new Document("name","$$show.name")
.append("season","$$season"),
false
)
)
)
)
)
),
Arrays.<Object>asList(false)
)
)
)
)
);

System.out.println(JSON.serialize(pipeline));

AggregateIterable<Document> result = collection.aggregate(pipeline);

MongoCursor<Document> cursor = result.iterator();

while (cursor.hasNext()) {
Document doc = cursor.next();
System.out.println(doc.toJson());
}

如前所述,这是语法的“怪物”,它应该让您深入了解处理文档中的多层嵌套数组有多么困难。众所周知,超出单个数组的任何事物都难以处理,并且由于位置运算符的限制,基本上不可能对其执行原子更新。

所以这会起作用,您实际上只需要添加“metaTags”嵌入在“questionEntry”对象中。所以用“questionEntry.metaTags”代替那里的任何东西。但是,您可能会考虑从这种形式更改您的模式,以便在大量编码和维护中简化工作,并使内容可用于原子更新。

关于java - 如何使用 Java 查询和过滤多个嵌套数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31619838/

35 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com