#node.js #reactjs #express
#node.js #reactjs #экспресс
Вопрос:
Я работаю над приложением на основе стека Mern, в моем приложении у меня есть модель события, и событие содержит много комментариев. как мне отсортировать комментарии, чтобы показать самые новые комментарии сверху, в то время как старые будут внизу?
И где я должен использовать эту логику? в API (backend) или интерфейсе (React)? У комментария есть атрибут с именем createdAt
новая дата вставляется в атрибут createdAt при каждом создании комментария.
Вот как я извлекаю событие с комментариями в моем серверной части Express
router.get("/:id/eventcomments", async (req, res) => {
const event = await Event.findById({ _id: req.params.id }).populate(
"eventcomments"
);
res.json(event);
});
и вот модель схемы для комментариев.
const eventcommentSchema = new mongoose.Schema({
description: {
type: String,
required: true
},
name: {
type: String,
required: true
},
createdAt: {
type: Date,
required: true,
default: Date.now
},
event: { type: Schema.Types.ObjectId, ref: 'Event'}
})
Вот мой компонент
export default function EventAndComments(props) {
const EventComment = (props) => (
<CardContent>
<Typography variant="body2" color="textSecondary" component="p">
{props.comment.name}
</Typography>
<Typography variant="body2" color="textSecondary" component="p">
{props.comment.description}
</Typography>
</CardContent>
);
const theme = useTheme();
const [events, setEventData] = useState([]);
const [comments, setCommentData] = useState([]);
const useStyles = makeStyles((theme) => ({
root: {
maxWidth: 550,
},
media: {
height: 0,
paddingTop: "86%", // 16:9
display: "flex",
flexDirection: "column",
alignItems: "center",
},
expand: {
transform: "rotate(0deg)",
marginLeft: "auto",
transition: theme.transitions.create("transform", {
duration: theme.transitions.duration.shortest,
}),
},
expandOpen: {
transform: "rotate(180deg)",
},
avatar: {
backgroundColor: red[500],
},
}));
const classes = useStyles();
const [expanded, setExpanded] = React.useState(false);
const handleExpandClick = () => {
setExpanded(!expanded);
};
useEffect(() => {
axios
.get(
"http://localhost:9000/events/"
props.match.params.id
"/eventcomments"
)
.then((response) => {
setEventData(response.data);
})
.catch(function (error) {
console.log(error);
});
}, []);
const onPageLoad = () => {
axios
.get(
"http://localhost:9000/events/"
props.match.params.id
"/eventcomments"
)
.then((response) => {
setCommentData(response.data.eventcomments);
})
.catch(function (error) {
console.log(error);
});
};
useEffect(() => {
onPageLoad();
}, []);
const nowIso = new Date();
const getTitle = (startDateTs, endDateTs) => {
const now = Date.parse(nowIso);
if (endDateTs <= now) {
return "Started:" " " moment(startDateTs).format("LLLL");
}
if (startDateTs < now amp;amp; endDateTs > now) {
return "Live:" " " moment(startDateTs).format("LLLL");
}
return "Starting:" " " moment(startDateTs).format("LLLL");
};
const getEnded = (startDateTs, endDateTs) => {
const now = Date.parse(nowIso);
if (endDateTs <= now) {
return "Ended:" " " moment(startDateTs).format("LLLL");
}
if (startDateTs < now amp;amp; endDateTs > now) {
return "Will End:" " " moment(startDateTs).format("LLLL");
}
return "Ends:" " " moment(startDateTs).format("LLLL");
};
const [eventDescription, setEventComment] = React.useState("");
const [name, setName] = React.useState("");
const handleChange = (parameter) => (event) => {
if (parameter === "name") {
setName(event.target.value);
}
if (parameter === "description") {
setEventComment(event.target.value);
}
};
const onSubmit = useCallback(
(e) => {
e.preventDefault();
axios
.post(
"http://localhost:9000/events/"
props.match.params.id
"/eventcomment",
{ name: name, description: eventDescription }
)
.then(function (response) {
onPageLoad();
})
.catch(function (error) {
console.log(error);
});
},
[props.match.params.id, name, eventDescription]
);
let eventCommentList = comments.map((comment, k) => (
<EventComment comment={comment} key={k} />
));
return (
<Grid
container
spacing={0}
direction="column"
alignItems="center"
justify="center"
style={{ minHeight: "100vh" }}
>
<Card className={classes.root}>
<h3
style={{
background: " #800000",
color: "white",
textAlign: "center",
}}
className={classes.cardheader}
>
{events.title}
</h3>
<CardHeader
avatar={
<Avatar aria-label="recipe" className={classes.avatar}>
CB
</Avatar>
}
action={
<IconButton aria-label="settings">
<MoreVertIcon />
</IconButton>
}
title={getTitle(
Date.parse(events.startingDate),
Date.parse(events.closingDate)
)}
subheader={getEnded(
Date.parse(events.startingDate),
Date.parse(events.closingDate)
)}
style={{ background: "#DCDCDC" }}
/>
<CardMedia
className={classes.media}
image={events.eventImage}
title="Paella dish"
/>
<CardContent>
<Typography variant="body2" color="textSecondary" component="p">
{events.description}
</Typography>
</CardContent>
</Card>
<form
className={classes.root}
noValidate
autoComplete="off"
onSubmit={onSubmit}
>
<FormControl>
<InputLabel htmlFor="component-simple">Name</InputLabel>
<Input
id="component-simple"
value={name}
onChange={handleChange("name")}
label="Name"
/>
</FormControl>
<FormControl variant="outlined">
<InputLabel htmlFor="component-outlined">Description</InputLabel>
<OutlinedInput
id="component-outlined"
value={eventDescription}
onChange={handleChange("description")}
label="Description"
/>
</FormControl>
<Button type="submit" fullWidth variant="contained" color="primary">
Create Comment
</Button>
</form>
<CardContent>{eventCommentList}</CardContent>
</Grid>
);
}
}
Я добавил все свои коды.
Комментарии:
1. Вы можете сделать заказ на клиенте или сервере, сервер будет предпочтительнее, поскольку результаты могут быть кэшированы. поиск в mongodb
sort
2. Я использовал Mongo DB sort, но это не сработало. @Samuel
3. @Chuckwuma — что вы имеете в виду, сортировка MongoDB не работает? Пожалуйста, по крайней мере, изучите документы и попытайтесь их разрешить — SO не является обучающим сервисом.
Ответ №1:
Вы можете sort
использовать такую функциональность в mongoose
const event = await Event.findById({ _id: req.params.id }).populate("eventcomments").sort({createdAt:-1});
Подробнее об этом вы можете прочитать здесь
Обновить
Попробуйте передать это как опцию
const event = await Event.findById({ _id: req.params.id },null,{sort:{createdAt:-1}}).populate("eventcomments")
Комментарии:
1. Я пробовал этот пример раньше, но он не сработал, я также пробовал ваш код, но он тоже не сработал. @Shivam
2. это все еще не работает, я действительно не знаю почему. @Shivam
3. Команда сортировки применяется к запрашиваемой коллекции. Вы пытаетесь отсортировать вложенные документы в другой коллекции. Смотрите мой ответ для получения более подробной информации.
Ответ №2:
Строго говоря, вам на самом деле не нужно поле createdAt. Идентификаторы объектов MongoDB кодируют время создания объекта. Из документов MongoDB:
сортировка по полю _id, в котором хранятся значения ObjectId, примерно эквивалентна сортировке по времени создания.
Смотрите: https://docs.mongodb.com/v3.0/reference/bson-types/#objectid
Вы также можете извлечь временную метку из ObjectId с помощью ObjectId.getTimestamp().
Однако, если вы пытаетесь отсортировать вложенные документы, вы не сможете сделать это в запросе к родительскому. Вам придется использовать конвейер агрегации, поскольку команда sort будет сортировать ваши события, а не ваши комментарии.
Однако в вашем случае использования вам было бы лучше сохранить весь документ комментариев в документе события. Каждый раз, когда кто-то публикует комментарий, он должен попадать в массив комментариев к событию, где он будет находиться уже отсортированным. Это избавит ваш сервер от необходимости выполнять ТОННУ поисковых запросов и вносить изменения в документ каждый раз, когда кто-то запрашивает событие. То, как вы все настроили сейчас, ваша база данных будет получать запросы на чтение (1 * количество комментариев) каждый раз, когда кто-то просматривает событие. Затем эти комментарии нужно будет вставить в документ вашего события. Это нормально в системах реляционных баз данных; Это то, для чего они предназначены. Это не так хорошо работает в базах данных NoSQL.
Это называется шаблоном встроенного документа. И если вы хотите использовать MongoDB, то это очень хорошая идея ознакомиться с ним. Вот подробная ссылка, где вы можете найти больше информации об этом шаблоне и его полезности в системе NoSQL:
https://docs.mongodb.com/manual/tutorial/model-embedded-one-to-many-relationships-between-documents/
Комментарии:
1. Я обязательно изучу это, чтобы узнать больше о том, как сделать это лучше. @Charles
Ответ №3:
Мне удалось заставить это работать с этим кодом.
router.get("/:id/eventcomments", async (req, res) => {
Event.findById({ _id: req.params.id })
.populate("eventcomments", "_id name description createdAt",
null, {
sort: { createdAt: -1 },
})
.exec(function (error, results) {
res.json(results);
});
});
разница между тем, что у меня есть здесь, и примером, приведенным выше, заключается в том, что для этого требуется, чтобы я передавал имена своих полей.